Commit 35a4ed01 authored by Fugang Duan's avatar Fugang Duan Committed by Greg Kroah-Hartman
Browse files

tty: serial: fsl_lpuart: add imx8qxp support



The lpuart of imx8ulp is basically the same as imx7ulp, but it
has new feature support based on imx7ulp, like it can assert a
DMA request on EOP(end-of-packet). imx8ulp lpuart use two clocks,
one is ipg bus clock that is used to access registers, the other
is baud clock that is used to transmit-receive data.

Signed-off-by: default avatarFugang Duan <fugang.duan@nxp.com>
Link: https://lore.kernel.org/r/20190704134007.2316-1-fugang.duan@nxp.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 011bd05d
Loading
Loading
Loading
Loading
+86 −20
Original line number Diff line number Diff line
@@ -234,9 +234,18 @@

static DEFINE_IDA(fsl_lpuart_ida);

enum lpuart_type {
	VF610_LPUART,
	LS1021A_LPUART,
	IMX7ULP_LPUART,
	IMX8QXP_LPUART,
};

struct lpuart_port {
	struct uart_port	port;
	struct clk		*clk;
	enum lpuart_type	devtype;
	struct clk		*ipg_clk;
	struct clk		*baud_clk;
	unsigned int		txfifo_size;
	unsigned int		rxfifo_size;

@@ -261,19 +270,29 @@ struct lpuart_port {
};

struct lpuart_soc_data {
	enum lpuart_type devtype;
	char iotype;
	u8 reg_off;
};

static const struct lpuart_soc_data vf_data = {
	.devtype = VF610_LPUART,
	.iotype = UPIO_MEM,
};

static const struct lpuart_soc_data ls_data = {
	.devtype = LS1021A_LPUART,
	.iotype = UPIO_MEM32BE,
};

static struct lpuart_soc_data imx_data = {
static struct lpuart_soc_data imx7ulp_data = {
	.devtype = IMX7ULP_LPUART,
	.iotype = UPIO_MEM32,
	.reg_off = IMX_REG_OFF,
};

static struct lpuart_soc_data imx8qxp_data = {
	.devtype = IMX8QXP_LPUART,
	.iotype = UPIO_MEM32,
	.reg_off = IMX_REG_OFF,
};
@@ -281,7 +300,8 @@ static struct lpuart_soc_data imx_data = {
static const struct of_device_id lpuart_dt_ids[] = {
	{ .compatible = "fsl,vf610-lpuart",	.data = &vf_data, },
	{ .compatible = "fsl,ls1021a-lpuart",	.data = &ls_data, },
	{ .compatible = "fsl,imx7ulp-lpuart",	.data = &imx_data, },
	{ .compatible = "fsl,imx7ulp-lpuart",	.data = &imx7ulp_data, },
	{ .compatible = "fsl,imx8qxp-lpuart",	.data = &imx8qxp_data, },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
@@ -289,6 +309,11 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
/* Forward declare this for the dma callbacks*/
static void lpuart_dma_tx_complete(void *arg);

static inline bool is_imx8qxp_lpuart(struct lpuart_port *sport)
{
	return sport->devtype == IMX8QXP_LPUART;
}

static inline u32 lpuart32_read(struct uart_port *port, u32 off)
{
	switch (port->iotype) {
@@ -314,6 +339,39 @@ static inline void lpuart32_write(struct uart_port *port, u32 val,
	}
}

static int __lpuart_enable_clks(struct lpuart_port *sport, bool is_en)
{
	int ret = 0;

	if (is_en) {
		ret = clk_prepare_enable(sport->ipg_clk);
		if (ret)
			return ret;

		ret = clk_prepare_enable(sport->baud_clk);
		if (ret) {
			clk_disable_unprepare(sport->ipg_clk);
			return ret;
		}
	} else {
		clk_disable_unprepare(sport->baud_clk);
		clk_disable_unprepare(sport->ipg_clk);
	}

	return 0;
}

static unsigned int lpuart_get_baud_clk_rate(struct lpuart_port *sport)
{
	if (is_imx8qxp_lpuart(sport))
		return clk_get_rate(sport->baud_clk);

	return clk_get_rate(sport->ipg_clk);
}

#define lpuart_enable_clks(x)	__lpuart_enable_clks(x, true)
#define lpuart_disable_clks(x)	__lpuart_enable_clks(x, false)

static void lpuart_stop_tx(struct uart_port *port)
{
	unsigned char temp;
@@ -2069,7 +2127,7 @@ lpuart_console_get_options(struct lpuart_port *sport, int *baud,
	brfa = readb(sport->port.membase + UARTCR4);
	brfa &= UARTCR4_BRFA_MASK;

	uartclk = clk_get_rate(sport->clk);
	uartclk = lpuart_get_baud_clk_rate(sport);
	/*
	 * baud = mod_clk/(16*(sbr[13]+(brfa)/32)
	 */
@@ -2112,7 +2170,7 @@ lpuart32_console_get_options(struct lpuart_port *sport, int *baud,
	bd = lpuart32_read(&sport->port, UARTBAUD);
	bd &= UARTBAUD_SBR_MASK;
	sbr = bd;
	uartclk = clk_get_rate(sport->clk);
	uartclk = lpuart_get_baud_clk_rate(sport);
	/*
	 * baud = mod_clk/(16*(sbr[13]+(brfa)/32)
	 */
@@ -2286,6 +2344,7 @@ static int lpuart_probe(struct platform_device *pdev)
	sport->port.mapbase = res->start;
	sport->port.dev = &pdev->dev;
	sport->port.type = PORT_LPUART;
	sport->devtype = sdata->devtype;
	ret = platform_get_irq(pdev, 0);
	if (ret < 0) {
		dev_err(&pdev->dev, "cannot obtain irq\n");
@@ -2301,20 +2360,27 @@ static int lpuart_probe(struct platform_device *pdev)

	sport->port.rs485_config = lpuart_config_rs485;

	sport->clk = devm_clk_get(&pdev->dev, "ipg");
	if (IS_ERR(sport->clk)) {
		ret = PTR_ERR(sport->clk);
		dev_err(&pdev->dev, "failed to get uart clk: %d\n", ret);
	sport->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
	if (IS_ERR(sport->ipg_clk)) {
		ret = PTR_ERR(sport->ipg_clk);
		dev_err(&pdev->dev, "failed to get uart ipg clk: %d\n", ret);
		return ret;
	}

	ret = clk_prepare_enable(sport->clk);
	if (ret) {
		dev_err(&pdev->dev, "failed to enable uart clk: %d\n", ret);
	sport->baud_clk = NULL;
	if (is_imx8qxp_lpuart(sport)) {
		sport->baud_clk = devm_clk_get(&pdev->dev, "baud");
		if (IS_ERR(sport->baud_clk)) {
			ret = PTR_ERR(sport->baud_clk);
			dev_err(&pdev->dev, "failed to get uart baud clk: %d\n", ret);
			return ret;
		}
	}

	sport->port.uartclk = clk_get_rate(sport->clk);
	ret = lpuart_enable_clks(sport);
	if (ret)
		return ret;
	sport->port.uartclk = lpuart_get_baud_clk_rate(sport);

	lpuart_ports[sport->port.line] = sport;

@@ -2362,7 +2428,7 @@ static int lpuart_probe(struct platform_device *pdev)

failed_attach_port:
failed_irq_request:
	clk_disable_unprepare(sport->clk);
	lpuart_disable_clks(sport);
	return ret;
}

@@ -2374,7 +2440,7 @@ static int lpuart_remove(struct platform_device *pdev)

	ida_simple_remove(&fsl_lpuart_ida, sport->port.line);

	clk_disable_unprepare(sport->clk);
	lpuart_disable_clks(sport);

	if (sport->dma_tx_chan)
		dma_release_channel(sport->dma_tx_chan);
@@ -2439,7 +2505,7 @@ static int lpuart_suspend(struct device *dev)
	}

	if (sport->port.suspended && !irq_wake)
		clk_disable_unprepare(sport->clk);
		lpuart_disable_clks(sport);

	return 0;
}
@@ -2451,7 +2517,7 @@ static int lpuart_resume(struct device *dev)
	unsigned long temp;

	if (sport->port.suspended && !irq_wake)
		clk_prepare_enable(sport->clk);
		lpuart_enable_clks(sport);

	if (lpuart_is_32(sport)) {
		lpuart32_setup_watermark(sport);