diff options
Diffstat (limited to 'drivers/tty/serial/8250/8250_uniphier.c')
-rw-r--r-- | drivers/tty/serial/8250/8250_uniphier.c | 63 |
1 files changed, 54 insertions, 9 deletions
diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 746680ebf90c..8a10b10e27aa 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -29,12 +29,13 @@ * - MMIO32 (regshift = 2) * - FCR is not at 2, but 3 * - LCR and MCR are not at 3 and 4, they share 4 + * - No SCR (Instead, CHAR can be used as a scratch register) * - Divisor latch at 9, no divisor latch access bit */ #define UNIPHIER_UART_REGSHIFT 2 -/* bit[15:8] = CHAR (not used), bit[7:0] = FCR */ +/* bit[15:8] = CHAR, bit[7:0] = FCR */ #define UNIPHIER_UART_CHAR_FCR (3 << (UNIPHIER_UART_REGSHIFT)) /* bit[15:8] = LCR, bit[7:0] = MCR */ #define UNIPHIER_UART_LCR_MCR (4 << (UNIPHIER_UART_REGSHIFT)) @@ -72,13 +73,18 @@ OF_EARLYCON_DECLARE(uniphier, "socionext,uniphier-uart", /* * The register map is slightly different from that of 8250. - * IO callbacks must be overridden for correct access to FCR, LCR, and MCR. + * IO callbacks must be overridden for correct access to FCR, LCR, MCR and SCR. */ static unsigned int uniphier_serial_in(struct uart_port *p, int offset) { unsigned int valshift = 0; switch (offset) { + case UART_SCR: + /* No SCR for this hardware. Use CHAR as a scratch register */ + valshift = 8; + offset = UNIPHIER_UART_CHAR_FCR; + break; case UART_LCR: valshift = 8; /* fall through */ @@ -91,8 +97,8 @@ static unsigned int uniphier_serial_in(struct uart_port *p, int offset) } /* - * The return value must be masked with 0xff because LCR and MCR reside - * in the same register that must be accessed by 32-bit write/read. + * The return value must be masked with 0xff because some registers + * share the same offset that must be accessed by 32-bit write/read. * 8 or 16 bit access to this hardware result in unexpected behavior. */ return (readl(p->membase + offset) >> valshift) & 0xff; @@ -101,9 +107,13 @@ static unsigned int uniphier_serial_in(struct uart_port *p, int offset) static void uniphier_serial_out(struct uart_port *p, int offset, int value) { unsigned int valshift = 0; - bool normal = true; + bool normal = false; switch (offset) { + case UART_SCR: + /* No SCR for this hardware. Use CHAR as a scratch register */ + valshift = 8; + /* fall through */ case UART_FCR: offset = UNIPHIER_UART_CHAR_FCR; break; @@ -114,10 +124,10 @@ static void uniphier_serial_out(struct uart_port *p, int offset, int value) /* fall through */ case UART_MCR: offset = UNIPHIER_UART_LCR_MCR; - normal = false; break; default: offset <<= UNIPHIER_UART_REGSHIFT; + normal = true; break; } @@ -169,7 +179,7 @@ static int uniphier_of_serial_setup(struct device *dev, struct uart_port *port, dev_err(dev, "failed to get alias id\n"); return ret; } - port->line = priv->line = ret; + port->line = ret; /* Get clk rate through clk driver */ priv->clk = devm_clk_get(dev, NULL); @@ -249,8 +259,8 @@ static int uniphier_uart_probe(struct platform_device *pdev) up.dl_read = uniphier_serial_dl_read; up.dl_write = uniphier_serial_dl_write; - ret = serial8250_register_8250_port(&up); - if (ret < 0) { + priv->line = serial8250_register_8250_port(&up); + if (priv->line < 0) { dev_err(dev, "failed to register 8250 port\n"); clk_disable_unprepare(priv->clk); return ret; @@ -271,6 +281,40 @@ static int uniphier_uart_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused uniphier_uart_suspend(struct device *dev) +{ + struct uniphier8250_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(priv->line); + + serial8250_suspend_port(priv->line); + + if (!uart_console(&up->port) || console_suspend_enabled) + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int __maybe_unused uniphier_uart_resume(struct device *dev) +{ + struct uniphier8250_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(priv->line); + int ret; + + if (!uart_console(&up->port) || console_suspend_enabled) { + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + } + + serial8250_resume_port(priv->line); + + return 0; +} + +static const struct dev_pm_ops uniphier_uart_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(uniphier_uart_suspend, uniphier_uart_resume) +}; + static const struct of_device_id uniphier_uart_match[] = { { .compatible = "socionext,uniphier-uart" }, { /* sentinel */ } @@ -283,6 +327,7 @@ static struct platform_driver uniphier_uart_platform_driver = { .driver = { .name = "uniphier-uart", .of_match_table = uniphier_uart_match, + .pm = &uniphier_uart_pm_ops, }, }; module_platform_driver(uniphier_uart_platform_driver); |