tty: xuartps: Implement suspend/resume callbacks
authorSoren Brinkmann <soren.brinkmann@xilinx.com>
Thu, 17 Oct 2013 21:08:12 +0000 (14:08 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 20 Oct 2013 02:47:38 +0000 (19:47 -0700)
Implement suspend and resume callbacks in order to support system
suspend/hibernation.

Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/xilinx_uartps.c

index 82195040e90656c488e90be9df3981fe5aa2d01f..9ecd8ea4f3ae8552fe14e684c7d7b5bee0841928 100644 (file)
@@ -1198,6 +1198,119 @@ console_initcall(xuartps_console_init);
 
 #endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */
 
+#ifdef CONFIG_PM_SLEEP
+/**
+ * xuartps_suspend - suspend event
+ * @device: Pointer to the device structure
+ *
+ * Returns 0
+ */
+static int xuartps_suspend(struct device *device)
+{
+       struct uart_port *port = dev_get_drvdata(device);
+       struct tty_struct *tty;
+       struct device *tty_dev;
+       int may_wake = 0;
+
+       /* Get the tty which could be NULL so don't assume it's valid */
+       tty = tty_port_tty_get(&port->state->port);
+       if (tty) {
+               tty_dev = tty->dev;
+               may_wake = device_may_wakeup(tty_dev);
+               tty_kref_put(tty);
+       }
+
+       /*
+        * Call the API provided in serial_core.c file which handles
+        * the suspend.
+        */
+       uart_suspend_port(&xuartps_uart_driver, port);
+       if (console_suspend_enabled && !may_wake) {
+               struct xuartps *xuartps = port->private_data;
+
+               clk_disable(xuartps->refclk);
+               clk_disable(xuartps->aperclk);
+       } else {
+               unsigned long flags = 0;
+
+               spin_lock_irqsave(&port->lock, flags);
+               /* Empty the receive FIFO 1st before making changes */
+               while (!(xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_RXEMPTY))
+                       xuartps_readl(XUARTPS_FIFO_OFFSET);
+               /* set RX trigger level to 1 */
+               xuartps_writel(1, XUARTPS_RXWM_OFFSET);
+               /* disable RX timeout interrups */
+               xuartps_writel(XUARTPS_IXR_TOUT, XUARTPS_IDR_OFFSET);
+               spin_unlock_irqrestore(&port->lock, flags);
+       }
+
+       return 0;
+}
+
+/**
+ * xuartps_resume - Resume after a previous suspend
+ * @device: Pointer to the device structure
+ *
+ * Returns 0
+ */
+static int xuartps_resume(struct device *device)
+{
+       struct uart_port *port = dev_get_drvdata(device);
+       unsigned long flags = 0;
+       u32 ctrl_reg;
+       struct tty_struct *tty;
+       struct device *tty_dev;
+       int may_wake = 0;
+
+       /* Get the tty which could be NULL so don't assume it's valid */
+       tty = tty_port_tty_get(&port->state->port);
+       if (tty) {
+               tty_dev = tty->dev;
+               may_wake = device_may_wakeup(tty_dev);
+               tty_kref_put(tty);
+       }
+
+       if (console_suspend_enabled && !may_wake) {
+               struct xuartps *xuartps = port->private_data;
+
+               clk_enable(xuartps->aperclk);
+               clk_enable(xuartps->refclk);
+
+               spin_lock_irqsave(&port->lock, flags);
+
+               /* Set TX/RX Reset */
+               xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) |
+                               (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST),
+                               XUARTPS_CR_OFFSET);
+               while (xuartps_readl(XUARTPS_CR_OFFSET) &
+                               (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST))
+                       cpu_relax();
+
+               /* restore rx timeout value */
+               xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET);
+               /* Enable Tx/Rx */
+               ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET);
+               xuartps_writel(
+                       (ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) |
+                       (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN),
+                       XUARTPS_CR_OFFSET);
+
+               spin_unlock_irqrestore(&port->lock, flags);
+       } else {
+               spin_lock_irqsave(&port->lock, flags);
+               /* restore original rx trigger level */
+               xuartps_writel(rx_trigger_level, XUARTPS_RXWM_OFFSET);
+               /* enable RX timeout interrupt */
+               xuartps_writel(XUARTPS_IXR_TOUT, XUARTPS_IER_OFFSET);
+               spin_unlock_irqrestore(&port->lock, flags);
+       }
+
+       return uart_resume_port(&xuartps_uart_driver, port);
+}
+#endif /* ! CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(xuartps_dev_pm_ops, xuartps_suspend, xuartps_resume);
+
 /** Structure Definitions
  */
 static struct uart_driver xuartps_uart_driver = {
@@ -1348,6 +1461,7 @@ static struct platform_driver xuartps_platform_driver = {
                .owner = THIS_MODULE,
                .name = XUARTPS_NAME,           /* Driver name */
                .of_match_table = xuartps_of_match,
+               .pm = &xuartps_dev_pm_ops,
                },
 };