diff options
Diffstat (limited to 'drivers/tty/serial/8250/8250_uniphier.c')
| -rw-r--r-- | drivers/tty/serial/8250/8250_uniphier.c | 148 |
1 files changed, 76 insertions, 72 deletions
diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 746680ebf90c..e3db60bf50c9 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -1,15 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/clk.h> @@ -21,20 +12,18 @@ #include "8250.h" -/* Most (but not all) of UniPhier UART devices have 64-depth FIFO. */ -#define UNIPHIER_UART_DEFAULT_FIFO_SIZE 64 - /* * This hardware is similar to 8250, but its register map is a bit different: * - 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,16 +61,21 @@ 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) +static u32 uniphier_serial_in(struct uart_port *p, unsigned 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 */ + fallthrough; case UART_MCR: offset = UNIPHIER_UART_LCR_MCR; break; @@ -91,19 +85,23 @@ 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; } -static void uniphier_serial_out(struct uart_port *p, int offset, int value) +static void uniphier_serial_out(struct uart_port *p, unsigned int offset, u32 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; + fallthrough; case UART_FCR: offset = UNIPHIER_UART_CHAR_FCR; break; @@ -111,13 +109,13 @@ static void uniphier_serial_out(struct uart_port *p, int offset, int value) valshift = 8; /* Divisor latch access bit does not exist. */ value &= ~UART_LCR_DLAB; - /* fall through */ + fallthrough; case UART_MCR: offset = UNIPHIER_UART_LCR_MCR; - normal = false; break; default: offset <<= UNIPHIER_UART_REGSHIFT; + normal = true; break; } @@ -147,52 +145,16 @@ static void uniphier_serial_out(struct uart_port *p, int offset, int value) * The divisor latch register exists at different address. * Override dl_read/write callbacks. */ -static int uniphier_serial_dl_read(struct uart_8250_port *up) +static u32 uniphier_serial_dl_read(struct uart_8250_port *up) { return readl(up->port.membase + UNIPHIER_UART_DLR); } -static void uniphier_serial_dl_write(struct uart_8250_port *up, int value) +static void uniphier_serial_dl_write(struct uart_8250_port *up, u32 value) { writel(value, up->port.membase + UNIPHIER_UART_DLR); } -static int uniphier_of_serial_setup(struct device *dev, struct uart_port *port, - struct uniphier8250_priv *priv) -{ - int ret; - u32 prop; - struct device_node *np = dev->of_node; - - ret = of_alias_get_id(np, "serial"); - if (ret < 0) { - dev_err(dev, "failed to get alias id\n"); - return ret; - } - port->line = priv->line = ret; - - /* Get clk rate through clk driver */ - priv->clk = devm_clk_get(dev, NULL); - if (IS_ERR(priv->clk)) { - dev_err(dev, "failed to get clock\n"); - return PTR_ERR(priv->clk); - } - - ret = clk_prepare_enable(priv->clk); - if (ret < 0) - return ret; - - port->uartclk = clk_get_rate(priv->clk); - - /* Check for fifo size */ - if (of_property_read_u32(np, "fifo-size", &prop) == 0) - port->fifosize = prop; - else - port->fifosize = UNIPHIER_UART_DEFAULT_FIFO_SIZE; - - return 0; -} - static int uniphier_uart_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -200,7 +162,6 @@ static int uniphier_uart_probe(struct platform_device *pdev) struct uniphier8250_priv *priv; struct resource *regs; void __iomem *membase; - int irq; int ret; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -213,22 +174,24 @@ static int uniphier_uart_probe(struct platform_device *pdev) if (!membase) return -ENOMEM; - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "failed to get IRQ number\n"); - return irq; - } - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; memset(&up, 0, sizeof(up)); - ret = uniphier_of_serial_setup(dev, &up.port, priv); - if (ret < 0) + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(dev, "failed to get clock\n"); + return PTR_ERR(priv->clk); + } + + ret = clk_prepare_enable(priv->clk); + if (ret) return ret; + up.port.uartclk = clk_get_rate(priv->clk); + spin_lock_init(&priv->atomic_write_lock); up.port.dev = dev; @@ -236,14 +199,21 @@ static int uniphier_uart_probe(struct platform_device *pdev) up.port.mapbase = regs->start; up.port.mapsize = resource_size(regs); up.port.membase = membase; - up.port.irq = irq; + + ret = uart_read_port_properties(&up.port); + if (ret) + return ret; up.port.type = PORT_16550A; up.port.iotype = UPIO_MEM32; + up.port.fifosize = 64; up.port.regshift = UNIPHIER_UART_REGSHIFT; up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE; up.capabilities = UART_CAP_FIFO; + if (of_property_read_bool(dev->of_node, "auto-flow-control")) + up.capabilities |= UART_CAP_AFE; + up.port.serial_in = uniphier_serial_in; up.port.serial_out = uniphier_serial_out; up.dl_read = uniphier_serial_dl_read; @@ -255,22 +225,55 @@ static int uniphier_uart_probe(struct platform_device *pdev) clk_disable_unprepare(priv->clk); return ret; } + priv->line = ret; platform_set_drvdata(pdev, priv); return 0; } -static int uniphier_uart_remove(struct platform_device *pdev) +static void uniphier_uart_remove(struct platform_device *pdev) { struct uniphier8250_priv *priv = platform_get_drvdata(pdev); serial8250_unregister_port(priv->line); clk_disable_unprepare(priv->clk); +} + +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 +286,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); |
