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>
This commit is contained in:
Fugang Duan 2019-07-04 21:40:07 +08:00 committed by Greg Kroah-Hartman
parent 011bd05d1f
commit 35a4ed0164
1 changed files with 86 additions and 20 deletions

View File

@ -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);