diff options
author | Fugang Duan <fugang.duan@nxp.com> | 2019-07-04 21:40:07 +0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2019-07-04 15:53:45 +0200 |
commit | 35a4ed0164e992c9c7b82eb1370081a292131904 (patch) | |
tree | 9b9194dced28e6836080305255daa7deeb93e789 | |
parent | 011bd05d1f5d99eeb91e8a6f81f00654a2b8d3d1 (diff) | |
download | linux-35a4ed0164e992c9c7b82eb1370081a292131904.tar.bz2 |
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: Fugang Duan <fugang.duan@nxp.com>
Link: https://lore.kernel.org/r/20190704134007.2316-1-fugang.duan@nxp.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/tty/serial/fsl_lpuart.c | 106 |
1 files changed, 86 insertions, 20 deletions
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 2f24d9796d20..92dad2b4ec36 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -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 { - char iotype; - u8 reg_off; + 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); - return 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); |