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@xxxxxxxxxxx> Signed-off-by: Miquel Raynal <miquel.raynal@xxxxxxxxxxx> --- drivers/tty/serial/mvebu-uart.c | 64 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) 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 */ }, }; -- 2.14.1 -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html