[PATCH v5 09/15] OMAP2+: UART: Add runtime pm support for omap-serial driver
Ming Lei
tom.leiming at gmail.com
Thu Sep 22 03:48:51 EDT 2011
Hi,
On Wed, Sep 21, 2011 at 8:13 PM, Govindraj.R <govindraj.raja at ti.com> wrote:
> Adapts omap-serial driver to use pm_runtime API's.
> console_unlock();
>
> - if ((cpu_is_omap34xx() && bdata->pads) ||
> - (pdata->wk_en && pdata->wk_mask))
> + if ((cpu_is_omap34xx() && bdata->pads))
> device_init_wakeup(&pdev->dev, true);
Just a bit curious, why doesn't the code enable wakeup at default
on omap4, which will disable runtime pm of serial port on omap4.
I have tested your patches on omap4 panda(enable wakeup at
default manually), runtime pm of serial port 2 can work well and
remote wakeup too.
>
> kfree(pdata);
> diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h
> index 74822b3..8ef81ce 100644
> --- a/arch/arm/plat-omap/include/plat/omap-serial.h
> +++ b/arch/arm/plat-omap/include/plat/omap-serial.h
> @@ -62,6 +62,9 @@ struct omap_uart_port_info {
> upf_t flags; /* UPF_* flags */
>
> u32 errata;
> +
> + void (*enable_wakeup)(struct platform_device *, bool);
> + u32 (*get_context_loss_count)(struct device *);
> };
>
> struct uart_omap_dma {
> @@ -113,6 +116,8 @@ struct uart_omap_port {
> unsigned char msr_saved_flags;
> char name[20];
> unsigned long port_activity;
> + u32 context_loss_cnt;
> + u8 wakeups_enabled;
> };
>
> #endif /* __OMAP_SERIAL_H__ */
> diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
> index 9a0eac2..43c33da 100644
> --- a/drivers/tty/serial/omap-serial.c
> +++ b/drivers/tty/serial/omap-serial.c
> @@ -37,11 +37,14 @@
> #include <linux/clk.h>
> #include <linux/serial_core.h>
> #include <linux/irq.h>
> +#include <linux/pm_runtime.h>
>
> #include <plat/dma.h>
> #include <plat/dmtimer.h>
> #include <plat/omap-serial.h>
>
> +#define OMAP_UART_AUTOSUSPEND_DELAY -1
> +
> static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];
>
> /* Forward declaration of functions */
> @@ -102,6 +105,8 @@ static void serial_omap_stop_rxdma(struct uart_omap_port *up)
> omap_free_dma(up->uart_dma.rx_dma_channel);
> up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE;
> up->uart_dma.rx_dma_used = false;
> + pm_runtime_mark_last_busy(&up->pdev->dev);
> + pm_runtime_put_autosuspend(&up->pdev->dev);
> }
> }
>
> @@ -110,8 +115,11 @@ static void serial_omap_enable_ms(struct uart_port *port)
> struct uart_omap_port *up = (struct uart_omap_port *)port;
>
> dev_dbg(up->port.dev, "serial_omap_enable_ms+%d\n", up->pdev->id);
> +
> + pm_runtime_get_sync(&up->pdev->dev);
> up->ier |= UART_IER_MSI;
> serial_out(up, UART_IER, up->ier);
> + pm_runtime_put(&up->pdev->dev);
> }
>
> static void serial_omap_stop_tx(struct uart_port *port)
> @@ -129,23 +137,32 @@ static void serial_omap_stop_tx(struct uart_port *port)
> omap_stop_dma(up->uart_dma.tx_dma_channel);
> omap_free_dma(up->uart_dma.tx_dma_channel);
> up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE;
> + pm_runtime_mark_last_busy(&up->pdev->dev);
> + pm_runtime_put_autosuspend(&up->pdev->dev);
> }
>
> + pm_runtime_get_sync(&up->pdev->dev);
> if (up->ier & UART_IER_THRI) {
> up->ier &= ~UART_IER_THRI;
> serial_out(up, UART_IER, up->ier);
> }
> +
> + pm_runtime_mark_last_busy(&up->pdev->dev);
> + pm_runtime_put_autosuspend(&up->pdev->dev);
> }
>
> static void serial_omap_stop_rx(struct uart_port *port)
> {
> struct uart_omap_port *up = (struct uart_omap_port *)port;
>
> + pm_runtime_get_sync(&up->pdev->dev);
> if (up->use_dma)
> serial_omap_stop_rxdma(up);
> up->ier &= ~UART_IER_RLSI;
> up->port.read_status_mask &= ~UART_LSR_DR;
> serial_out(up, UART_IER, up->ier);
> + pm_runtime_mark_last_busy(&up->pdev->dev);
> + pm_runtime_put_autosuspend(&up->pdev->dev);
> }
>
> static inline void receive_chars(struct uart_omap_port *up, int *status)
> @@ -262,7 +279,10 @@ static void serial_omap_start_tx(struct uart_port *port)
> int ret = 0;
>
> if (!up->use_dma) {
> + pm_runtime_get_sync(&up->pdev->dev);
> serial_omap_enable_ier_thri(up);
> + pm_runtime_mark_last_busy(&up->pdev->dev);
> + pm_runtime_put_autosuspend(&up->pdev->dev);
> return;
> }
>
> @@ -272,6 +292,7 @@ static void serial_omap_start_tx(struct uart_port *port)
> xmit = &up->port.state->xmit;
>
> if (up->uart_dma.tx_dma_channel == OMAP_UART_DMA_CH_FREE) {
> + pm_runtime_get_sync(&up->pdev->dev);
> ret = omap_request_dma(up->uart_dma.uart_dma_tx,
> "UART Tx DMA",
> (void *)uart_tx_dma_callback, up,
> @@ -354,9 +375,13 @@ static inline irqreturn_t serial_omap_irq(int irq, void *dev_id)
> unsigned int iir, lsr;
> unsigned long flags;
>
> + pm_runtime_get_sync(&up->pdev->dev);
> iir = serial_in(up, UART_IIR);
> - if (iir & UART_IIR_NO_INT)
> + if (iir & UART_IIR_NO_INT) {
> + pm_runtime_mark_last_busy(&up->pdev->dev);
> + pm_runtime_put_autosuspend(&up->pdev->dev);
> return IRQ_NONE;
> + }
>
> spin_lock_irqsave(&up->port.lock, flags);
> lsr = serial_in(up, UART_LSR);
> @@ -378,6 +403,9 @@ static inline irqreturn_t serial_omap_irq(int irq, void *dev_id)
> transmit_chars(up);
>
> spin_unlock_irqrestore(&up->port.lock, flags);
> + pm_runtime_mark_last_busy(&up->pdev->dev);
> + pm_runtime_put_autosuspend(&up->pdev->dev);
> +
> up->port_activity = jiffies;
> return IRQ_HANDLED;
> }
> @@ -388,11 +416,12 @@ static unsigned int serial_omap_tx_empty(struct uart_port *port)
> unsigned long flags = 0;
> unsigned int ret = 0;
>
> + pm_runtime_get_sync(&up->pdev->dev);
> dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->pdev->id);
> spin_lock_irqsave(&up->port.lock, flags);
> ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
> spin_unlock_irqrestore(&up->port.lock, flags);
> -
> + pm_runtime_put(&up->pdev->dev);
> return ret;
> }
>
> @@ -402,7 +431,10 @@ static unsigned int serial_omap_get_mctrl(struct uart_port *port)
> unsigned char status;
> unsigned int ret = 0;
>
> + pm_runtime_get_sync(&up->pdev->dev);
> status = check_modem_status(up);
> + pm_runtime_put(&up->pdev->dev);
> +
> dev_dbg(up->port.dev, "serial_omap_get_mctrl+%d\n", up->pdev->id);
>
> if (status & UART_MSR_DCD)
> @@ -433,9 +465,11 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
> if (mctrl & TIOCM_LOOP)
> mcr |= UART_MCR_LOOP;
>
> + pm_runtime_get_sync(&up->pdev->dev);
> up->mcr = serial_in(up, UART_MCR);
> up->mcr |= mcr;
> serial_out(up, UART_MCR, up->mcr);
> + pm_runtime_put(&up->pdev->dev);
> }
>
> static void serial_omap_break_ctl(struct uart_port *port, int break_state)
> @@ -444,6 +478,7 @@ static void serial_omap_break_ctl(struct uart_port *port, int break_state)
> unsigned long flags = 0;
>
> dev_dbg(up->port.dev, "serial_omap_break_ctl+%d\n", up->pdev->id);
> + pm_runtime_get_sync(&up->pdev->dev);
> spin_lock_irqsave(&up->port.lock, flags);
> if (break_state == -1)
> up->lcr |= UART_LCR_SBC;
> @@ -451,6 +486,7 @@ static void serial_omap_break_ctl(struct uart_port *port, int break_state)
> up->lcr &= ~UART_LCR_SBC;
> serial_out(up, UART_LCR, up->lcr);
> spin_unlock_irqrestore(&up->port.lock, flags);
> + pm_runtime_put(&up->pdev->dev);
> }
>
> static int serial_omap_startup(struct uart_port *port)
> @@ -469,6 +505,7 @@ static int serial_omap_startup(struct uart_port *port)
>
> dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->pdev->id);
>
> + pm_runtime_get_sync(&up->pdev->dev);
> /*
> * Clear the FIFO buffers and disable them.
> * (they will be reenabled in set_termios())
> @@ -524,6 +561,8 @@ static int serial_omap_startup(struct uart_port *port)
> /* Enable module level wake up */
> serial_out(up, UART_OMAP_WER, OMAP_UART_WER_MOD_WKUP);
>
> + pm_runtime_mark_last_busy(&up->pdev->dev);
> + pm_runtime_put_autosuspend(&up->pdev->dev);
> up->port_activity = jiffies;
> return 0;
> }
> @@ -534,6 +573,8 @@ static void serial_omap_shutdown(struct uart_port *port)
> unsigned long flags = 0;
>
> dev_dbg(up->port.dev, "serial_omap_shutdown+%d\n", up->pdev->id);
> +
> + pm_runtime_get_sync(&up->pdev->dev);
> /*
> * Disable interrupts from this port
> */
> @@ -567,6 +608,7 @@ static void serial_omap_shutdown(struct uart_port *port)
> up->uart_dma.rx_buf_dma_phys);
> up->uart_dma.rx_buf = NULL;
> }
> + pm_runtime_put(&up->pdev->dev);
> free_irq(up->port.irq, up);
> }
>
> @@ -682,6 +724,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
> * Ok, we're now changing the port state. Do it with
> * interrupts disabled.
> */
> + pm_runtime_get_sync(&up->pdev->dev);
> spin_lock_irqsave(&up->port.lock, flags);
>
> /*
> @@ -814,6 +857,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
> serial_omap_configure_xonxoff(up, termios);
>
> spin_unlock_irqrestore(&up->port.lock, flags);
> + pm_runtime_put(&up->pdev->dev);
> dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->pdev->id);
> }
>
> @@ -825,6 +869,8 @@ serial_omap_pm(struct uart_port *port, unsigned int state,
> unsigned char efr;
>
> dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->pdev->id);
> +
> + pm_runtime_get_sync(&up->pdev->dev);
> serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
> efr = serial_in(up, UART_EFR);
> serial_out(up, UART_EFR, efr | UART_EFR_ECB);
> @@ -834,6 +880,7 @@ serial_omap_pm(struct uart_port *port, unsigned int state,
> serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
> serial_out(up, UART_EFR, efr);
> serial_out(up, UART_LCR, 0);
> + pm_runtime_put(&up->pdev->dev);
> }
>
> static void serial_omap_release_port(struct uart_port *port)
> @@ -911,19 +958,26 @@ static inline void wait_for_xmitr(struct uart_omap_port *up)
> static void serial_omap_poll_put_char(struct uart_port *port, unsigned char ch)
> {
> struct uart_omap_port *up = (struct uart_omap_port *)port;
> +
> + pm_runtime_get_sync(&up->pdev->dev);
> wait_for_xmitr(up);
> serial_out(up, UART_TX, ch);
> + pm_runtime_put(&up->pdev->dev);
> }
>
> static int serial_omap_poll_get_char(struct uart_port *port)
> {
> struct uart_omap_port *up = (struct uart_omap_port *)port;
> - unsigned int status = serial_in(up, UART_LSR);
> + unsigned int status;
>
> + pm_runtime_get_sync(&up->pdev->dev);
> + status = serial_in(up, UART_LSR);
> if (!(status & UART_LSR_DR))
> return NO_POLL_CHAR;
>
> - return serial_in(up, UART_RX);
> + status = serial_in(up, UART_RX);
> + pm_runtime_put(&up->pdev->dev);
> + return status;
> }
>
> #endif /* CONFIG_CONSOLE_POLL */
> @@ -951,6 +1005,8 @@ serial_omap_console_write(struct console *co, const char *s,
> unsigned int ier;
> int locked = 1;
>
> + pm_runtime_get_sync(&up->pdev->dev);
> +
> local_irq_save(flags);
> if (up->port.sysrq)
> locked = 0;
> @@ -983,6 +1039,8 @@ serial_omap_console_write(struct console *co, const char *s,
> if (up->msr_saved_flags)
> check_modem_status(up);
>
> + pm_runtime_mark_last_busy(&up->pdev->dev);
> + pm_runtime_put_autosuspend(&up->pdev->dev);
> if (locked)
> spin_unlock(&up->port.lock);
> local_irq_restore(flags);
> @@ -1065,19 +1123,18 @@ static struct uart_driver serial_omap_reg = {
> .cons = OMAP_CONSOLE,
> };
>
> -static int
> -serial_omap_suspend(struct platform_device *pdev, pm_message_t state)
> +static int serial_omap_suspend(struct device *dev)
> {
> - struct uart_omap_port *up = platform_get_drvdata(pdev);
> + struct uart_omap_port *up = dev_get_drvdata(dev);
>
> if (up)
> uart_suspend_port(&serial_omap_reg, &up->port);
> return 0;
> }
>
> -static int serial_omap_resume(struct platform_device *dev)
> +static int serial_omap_resume(struct device *dev)
> {
> - struct uart_omap_port *up = platform_get_drvdata(dev);
> + struct uart_omap_port *up = dev_get_drvdata(dev);
>
> if (up)
> uart_resume_port(&serial_omap_reg, &up->port);
> @@ -1140,6 +1197,7 @@ static int serial_omap_start_rxdma(struct uart_omap_port *up)
> int ret = 0;
>
> if (up->uart_dma.rx_dma_channel == -1) {
> + pm_runtime_get_sync(&up->pdev->dev);
> ret = omap_request_dma(up->uart_dma.uart_dma_rx,
> "UART Rx DMA",
> (void *)uart_rx_dma_callback, up,
> @@ -1305,6 +1363,16 @@ static int serial_omap_probe(struct platform_device *pdev)
> up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE;
> }
>
> + pm_runtime_use_autosuspend(&pdev->dev);
> + pm_runtime_set_autosuspend_delay(&pdev->dev,
> + OMAP_UART_AUTOSUSPEND_DELAY);
> +
> + pm_runtime_irq_safe(&pdev->dev);
> + if (device_may_wakeup(&pdev->dev)) {
> + pm_runtime_enable(&pdev->dev);
> + pm_runtime_get_sync(&pdev->dev);
> + }
> +
> ui[pdev->id] = up;
> serial_omap_add_console_port(up);
>
> @@ -1312,6 +1380,7 @@ static int serial_omap_probe(struct platform_device *pdev)
> if (ret != 0)
> goto do_release_region;
>
> + pm_runtime_put(&pdev->dev);
> platform_set_drvdata(pdev, up);
> return 0;
> err:
> @@ -1326,22 +1395,96 @@ static int serial_omap_remove(struct platform_device *dev)
> {
> struct uart_omap_port *up = platform_get_drvdata(dev);
>
> - platform_set_drvdata(dev, NULL);
> if (up) {
> + pm_runtime_disable(&up->pdev->dev);
> uart_remove_one_port(&serial_omap_reg, &up->port);
> kfree(up);
> }
> +
> + platform_set_drvdata(dev, NULL);
> + return 0;
> +}
> +
> +static void serial_omap_restore_context(struct uart_omap_port *up)
> +{
> + serial_out(up, UART_OMAP_MDR1, up->mdr1);
> + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); /* Config B mode */
> + serial_out(up, UART_EFR, UART_EFR_ECB);
> + serial_out(up, UART_LCR, 0x0); /* Operational mode */
> + serial_out(up, UART_IER, 0x0);
> + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); /* Config B mode */
> + serial_out(up, UART_DLL, up->dll);
> + serial_out(up, UART_DLM, up->dlh);
> + serial_out(up, UART_LCR, 0x0); /* Operational mode */
> + serial_out(up, UART_IER, up->ier);
> + serial_out(up, UART_FCR, up->fcr);
> + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
> + serial_out(up, UART_MCR, up->mcr);
> + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); /* Config B mode */
> + serial_out(up, UART_EFR, up->efr);
> + serial_out(up, UART_LCR, up->lcr);
> + /* UART 16x mode */
> + serial_out(up, UART_OMAP_MDR1, up->mdr1);
> +}
> +
> +static int serial_omap_runtime_suspend(struct device *dev)
> +{
> + struct uart_omap_port *up = dev_get_drvdata(dev);
> + struct omap_uart_port_info *pdata = dev->platform_data;
> +
> + if (!up)
> + return -EINVAL;
> +
> + if (!pdata->enable_wakeup || !pdata->get_context_loss_count)
> + return 0;
> +
> + if (pdata->get_context_loss_count)
> + up->context_loss_cnt = pdata->get_context_loss_count(dev);
> +
> + if (device_may_wakeup(dev)) {
> + if (!up->wakeups_enabled) {
> + pdata->enable_wakeup(up->pdev, true);
> + up->wakeups_enabled = true;
> + }
> + } else {
> + if (up->wakeups_enabled) {
> + pdata->enable_wakeup(up->pdev, false);
> + up->wakeups_enabled = false;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int serial_omap_runtime_resume(struct device *dev)
> +{
> + struct uart_omap_port *up = dev_get_drvdata(dev);
> + struct omap_uart_port_info *pdata = dev->platform_data;
> +
> + if (up) {
> + if (pdata->get_context_loss_count) {
> + u32 loss_cnt = pdata->get_context_loss_count(dev);
> +
> + if (up->context_loss_cnt != loss_cnt)
> + serial_omap_restore_context(up);
> + }
> + }
> +
> return 0;
> }
>
> +static const struct dev_pm_ops serial_omap_dev_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(serial_omap_suspend, serial_omap_resume)
> + SET_RUNTIME_PM_OPS(serial_omap_runtime_suspend,
> + serial_omap_runtime_resume, NULL)
> +};
> +
> static struct platform_driver serial_omap_driver = {
> .probe = serial_omap_probe,
> .remove = serial_omap_remove,
> -
> - .suspend = serial_omap_suspend,
> - .resume = serial_omap_resume,
> .driver = {
> .name = DRIVER_NAME,
> + .pm = &serial_omap_dev_pm_ops,
> },
> };
>
> --
> 1.7.4.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
thanks,
--
Ming Lei
More information about the linux-arm-kernel
mailing list