diff options
-rw-r--r-- | Documentation/devicetree/bindings/serial/fsl-lpuart.txt | 12 | ||||
-rw-r--r-- | drivers/tty/serial/fsl_lpuart.c | 102 |
2 files changed, 91 insertions, 23 deletions
diff --git a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt index c95005efbcb8..9ad9ddc7c666 100644 --- a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt +++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt @@ -6,14 +6,24 @@ Required properties: on Vybrid vf610 SoC with 8-bit register organization - "fsl,ls1021a-lpuart" for lpuart compatible with the one integrated on LS1021A SoC with 32-bit big-endian register organization + - "fsl,imx7ulp-lpuart" for lpuart compatible with the one integrated + on i.MX7ULP SoC with 32-bit little-endian register organization + - "fsl,imx8qm-lpuart"for lpuart compatible with the one integrated + on i.MX8QM SoC with 32-bit little-endian register organization, which + is based on i.MX7ULP lpuart IP but add EEOP new feature. - reg : Address and length of the register set for the device - interrupts : Should contain uart interrupt - clocks : phandle + clock specifier pairs, one for each entry in clock-names -- clock-names : should contain: "ipg" - the uart clock +- clock-names : should contain: "ipg" - the uart peripheral register accessing + clock source, if "per" clock missing, the "ipg" clock also is the uart module + clock. Optional properties: - dmas: A list of two dma specifiers, one for each entry in dma-names. - dma-names: should contain "tx" and "rx". +- clocks : phandle + clock specifier pairs, one for each entry in clock-names +- clock-names : "per" - the uart module clock. + clock. Note: Optional properties for DMA support. Write them both or both not. diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 531bbb773879..41cf98fdaba4 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -237,7 +237,8 @@ struct lpuart_port { struct uart_port port; - struct clk *clk; + struct clk *ipg_clk; + struct clk *per_clk; unsigned int txfifo_size; unsigned int rxfifo_size; bool lpuart32; @@ -270,12 +271,11 @@ static const struct of_device_id lpuart_dt_ids[] = { .compatible = "fsl,ls1021a-lpuart", }, { - .compatible = "fsl,imx8qm-lpuart", + .compatible = "fsl,imx7ulp-lpuart", }, { - .compatible = "fsl,imx7ulp-lpuart", + .compatible = "fsl,imx8qm-lpuart", }, - { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, lpuart_dt_ids); @@ -1140,6 +1140,15 @@ static int lpuart_startup(struct uart_port *port) unsigned long flags; unsigned char temp; + ret = clk_prepare_enable(sport->ipg_clk); + if (ret) + return ret; + ret = clk_prepare_enable(sport->per_clk); + if (ret) { + clk_disable_unprepare(sport->ipg_clk); + return ret; + } + /* determine FIFO size and enable FIFO mode */ temp = readb(sport->port.membase + UARTPFIFO); @@ -1197,6 +1206,15 @@ static int lpuart32_startup(struct uart_port *port) unsigned long flags; unsigned long temp; + ret = clk_prepare_enable(sport->ipg_clk); + if (ret) + return ret; + ret = clk_prepare_enable(sport->per_clk); + if (ret) { + clk_disable_unprepare(sport->ipg_clk); + return ret; + } + /* determine FIFO size */ temp = lpuart32_read(sport->port.membase + UARTFIFO); @@ -1253,6 +1271,8 @@ static void lpuart_shutdown(struct uart_port *port) lpuart_stop_tx(port); } + clk_disable_unprepare(sport->per_clk); + clk_disable_unprepare(sport->ipg_clk); } static void lpuart32_shutdown(struct uart_port *port) @@ -1272,6 +1292,9 @@ static void lpuart32_shutdown(struct uart_port *port) spin_unlock_irqrestore(&port->lock, flags); devm_free_irq(port->dev, port->irq, sport); + + clk_disable_unprepare(sport->per_clk); + clk_disable_unprepare(sport->ipg_clk); } static void @@ -1755,7 +1778,10 @@ 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); + if (sport->per_clk) + uartclk = clk_get_rate(sport->per_clk); + else + uartclk = clk_get_rate(sport->ipg_clk); /* * baud = mod_clk/(16*(sbr[13]+(brfa)/32) */ @@ -1798,7 +1824,11 @@ lpuart32_console_get_options(struct lpuart_port *sport, int *baud, bd = lpuart32_read(sport->port.membase + UARTBAUD); bd &= UARTBAUD_SBR_MASK; sbr = bd; - uartclk = clk_get_rate(sport->clk); + if (sport->per_clk) + uartclk = clk_get_rate(sport->per_clk); + else + uartclk = clk_get_rate(sport->ipg_clk); + /* * baud = mod_clk/(16*(sbr[13]+(brfa)/32) */ @@ -1816,6 +1846,7 @@ static int __init lpuart_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; + int ret; /* * check whether an invalid uart number has been specified, and @@ -1829,6 +1860,15 @@ static int __init lpuart_console_setup(struct console *co, char *options) if (sport == NULL) return -ENODEV; + ret = clk_prepare_enable(sport->ipg_clk); + if (ret) + return ret; + ret = clk_prepare_enable(sport->per_clk); + if (ret) { + clk_disable_unprepare(sport->ipg_clk); + return ret; + } + if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else @@ -1905,7 +1945,9 @@ static int lpuart_probe(struct platform_device *pdev) return -EINVAL; } sport->port.line = ret; - sport->lpuart32 = 1; /* of_device_is_compatible(np, "fsl,imx8dv-lpuart"); */ + sport->lpuart32 = of_device_is_compatible(np, "fsl,ls1021a-lpuart") | + of_device_is_compatible(np, "fsl,imx7ulp-lpuart") | + of_device_is_compatible(np, "fsl,imx8qm-lpuart"); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); sport->port.membase = devm_ioremap_resource(&pdev->dev, res); @@ -1931,21 +1973,31 @@ 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->per_clk); + dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret); return ret; } + sport->per_clk = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(sport->per_clk)) + sport->per_clk = NULL; - ret = clk_prepare_enable(sport->clk); + ret = clk_prepare_enable(sport->ipg_clk); if (ret) { + dev_err(&pdev->dev, "failed to enable uart ipg clk: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(sport->per_clk); + if (ret) { + clk_disable_unprepare(sport->ipg_clk); dev_err(&pdev->dev, "failed to enable uart clk: %d\n", ret); return ret; } - - sport->port.uartclk = clk_get_rate(sport->clk); - pr_info("uartclk = %ld\n", clk_get_rate(sport->clk)); + if (sport->per_clk) + sport->port.uartclk = clk_get_rate(sport->per_clk); + else + sport->port.uartclk = clk_get_rate(sport->ipg_clk); lpuart_ports[sport->port.line] = sport; @@ -1958,7 +2010,8 @@ static int lpuart_probe(struct platform_device *pdev) ret = uart_add_one_port(&lpuart_reg, &sport->port); if (ret) { - clk_disable_unprepare(sport->clk); + clk_disable_unprepare(sport->per_clk); + clk_disable_unprepare(sport->ipg_clk); return ret; } @@ -1968,6 +2021,8 @@ static int lpuart_probe(struct platform_device *pdev) writeb(UARTMODEM_TXRTSE, sport->port.membase + UARTMODEM); } + clk_disable_unprepare(sport->per_clk); + clk_disable_unprepare(sport->ipg_clk); return 0; } @@ -1977,8 +2032,6 @@ static int lpuart_remove(struct platform_device *pdev) uart_remove_one_port(&lpuart_reg, &sport->port); - clk_disable_unprepare(sport->clk); - if (sport->dma_tx_chan) dma_release_channel(sport->dma_tx_chan); @@ -1993,6 +2046,11 @@ static int lpuart_suspend(struct device *dev) { struct lpuart_port *sport = dev_get_drvdata(dev); unsigned long temp; + int ret; + + ret = clk_prepare_enable(sport->ipg_clk); + if (ret) + return ret; if (sport->lpuart32) { /* disable Rx/Tx and interrupts */ @@ -2031,8 +2089,7 @@ static int lpuart_suspend(struct device *dev) dmaengine_terminate_all(sport->dma_tx_chan); } - if (sport->port.suspended && !sport->port.irq_wake) - clk_disable_unprepare(sport->clk); + clk_disable_unprepare(sport->ipg_clk); return 0; } @@ -2042,8 +2099,7 @@ static int lpuart_resume(struct device *dev) struct lpuart_port *sport = dev_get_drvdata(dev); unsigned long temp; - if (sport->port.suspended && !sport->port.irq_wake) - clk_prepare_enable(sport->clk); + clk_prepare_enable(sport->ipg_clk); if (sport->lpuart32) { lpuart32_setup_watermark(sport); @@ -2080,6 +2136,8 @@ static int lpuart_resume(struct device *dev) uart_resume_port(&lpuart_reg, &sport->port); + clk_disable_unprepare(sport->ipg_clk); + return 0; } #endif |