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>
This commit is contained in:
Miquel Raynal 2018-04-21 16:07:33 +02:00 committed by Greg Kroah-Hartman
parent 7a91e38297
commit 394e835145
1 changed files with 64 additions and 0 deletions

View File

@ -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 */
},
};