diff options
author | Miquel Raynal <miquel.raynal@bootlin.com> | 2018-04-21 16:07:33 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-04-22 17:38:01 +0200 |
commit | 394e835145414f8633303b46fe938e10fda697c4 (patch) | |
tree | f4181eeecf9623e62375ef134862f9167dd4c9ec /drivers/tty/serial/mvebu-uart.c | |
parent | 7a91e382974c2d75c8a49022c361113c7d8f4935 (diff) | |
download | linux-394e835145414f8633303b46fe938e10fda697c4.tar.bz2 |
serial: mvebu-uart: add suspend/resume support
Add suspend and resume hooks to save/restore the registers content
during S2RAM operation.
Also save/restore the oversampling rate register (OSAMP) as earlier
stages already tuned that register to get a precise UART clock.
Suggested-by: Allen Yan <yanwei@marvell.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/serial/mvebu-uart.c')
-rw-r--r-- | drivers/tty/serial/mvebu-uart.c | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index 750e5645dc85..013b86a1044f 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -71,6 +71,8 @@ #define UART_BRDV 0x10 #define BRDV_BAUD_MASK 0x3FF +#define UART_OSAMP 0x14 + #define MVEBU_NR_UARTS 2 #define MVEBU_UART_TYPE "mvebu-uart" @@ -108,6 +110,17 @@ struct mvebu_uart_driver_data { struct uart_flags flags; }; +/* Saved registers during suspend */ +struct mvebu_uart_pm_regs { + unsigned int rbr; + unsigned int tsh; + unsigned int ctrl; + unsigned int intr; + unsigned int stat; + unsigned int brdv; + unsigned int osamp; +}; + /* MVEBU UART driver structure */ struct mvebu_uart { struct uart_port *port; @@ -115,6 +128,9 @@ struct mvebu_uart { int irq[UART_IRQ_COUNT]; unsigned char __iomem *nb; struct mvebu_uart_driver_data *data; +#if defined(CONFIG_PM) + struct mvebu_uart_pm_regs pm_regs; +#endif /* CONFIG_PM */ }; static struct mvebu_uart *to_mvuart(struct uart_port *port) @@ -719,6 +735,51 @@ static struct uart_driver mvebu_uart_driver = { #endif }; +#if defined(CONFIG_PM) +static int mvebu_uart_suspend(struct device *dev) +{ + struct mvebu_uart *mvuart = dev_get_drvdata(dev); + struct uart_port *port = mvuart->port; + + uart_suspend_port(&mvebu_uart_driver, port); + + mvuart->pm_regs.rbr = readl(port->membase + UART_RBR(port)); + mvuart->pm_regs.tsh = readl(port->membase + UART_TSH(port)); + mvuart->pm_regs.ctrl = readl(port->membase + UART_CTRL(port)); + mvuart->pm_regs.intr = readl(port->membase + UART_INTR(port)); + mvuart->pm_regs.stat = readl(port->membase + UART_STAT); + mvuart->pm_regs.brdv = readl(port->membase + UART_BRDV); + mvuart->pm_regs.osamp = readl(port->membase + UART_OSAMP); + + device_set_wakeup_enable(dev, true); + + return 0; +} + +static int mvebu_uart_resume(struct device *dev) +{ + struct mvebu_uart *mvuart = dev_get_drvdata(dev); + struct uart_port *port = mvuart->port; + + writel(mvuart->pm_regs.rbr, port->membase + UART_RBR(port)); + writel(mvuart->pm_regs.tsh, port->membase + UART_TSH(port)); + writel(mvuart->pm_regs.ctrl, port->membase + UART_CTRL(port)); + writel(mvuart->pm_regs.intr, port->membase + UART_INTR(port)); + writel(mvuart->pm_regs.stat, port->membase + UART_STAT); + writel(mvuart->pm_regs.brdv, port->membase + UART_BRDV); + writel(mvuart->pm_regs.osamp, port->membase + UART_OSAMP); + + uart_resume_port(&mvebu_uart_driver, port); + + return 0; +} + +static const struct dev_pm_ops mvebu_uart_pm_ops = { + .suspend = mvebu_uart_suspend, + .resume = mvebu_uart_resume, +}; +#endif /* CONFIG_PM */ + static const struct of_device_id mvebu_uart_of_match[]; /* Counter to keep track of each UART port id when not using CONFIG_OF */ @@ -892,6 +953,9 @@ static struct platform_driver mvebu_uart_platform_driver = { .name = "mvebu-uart", .of_match_table = of_match_ptr(mvebu_uart_of_match), .suppress_bind_attrs = true, +#if defined(CONFIG_PM) + .pm = &mvebu_uart_pm_ops, +#endif /* CONFIG_PM */ }, }; |