Skip to content

Commit

Permalink
serial: mvebu-uart: add suspend/resume support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Miquel Raynal authored and Greg Kroah-Hartman committed Apr 22, 2018
1 parent 7a91e38 commit 394e835
Showing 1 changed file with 64 additions and 0 deletions.
64 changes: 64 additions & 0 deletions drivers/tty/serial/mvebu-uart.c
Original file line number Diff line number Diff line change
@@ -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,13 +110,27 @@ 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;
struct clk *clk;
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 */
},
};

0 comments on commit 394e835

Please sign in to comment.