[PATCH v3 04/12] Serial: OMAP: Add runtime pm support for omap-serial driver
Govindraj
govindraj.ti at gmail.com
Mon Jun 27 10:31:57 EDT 2011
On Sat, Jun 25, 2011 at 5:00 AM, Kevin Hilman <khilman at ti.com> wrote:
> "Govindraj.R" <govindraj.raja at ti.com> writes:
>
>> Adapts omap-serial driver to use pm_runtime api's.
>>
>> 1.) Populate reg values to uart port which can be used for context restore.
>
> Please make this part a separate patch.
>
>> 2.) Moving context_restore func to driver from serial.c
>> 3.) Adding port_enable/disable func to enable/disable given uart port.
>> enable port using get_sync and disable using autosuspend.
>> 4.) using runtime irq safe api to make get_sync be called from irq context.
>
>>
>> Acked-by: Alan Cox <alan at linux.intel.com>
>> Signed-off-by: Govindraj.R <govindraj.raja at ti.com>
>
> This is great! we're almost there. Some minor comments below...
>
>> ---
>> arch/arm/mach-omap2/serial.c | 22 +++
>> arch/arm/plat-omap/include/plat/omap-serial.h | 2 +
>> drivers/tty/serial/omap-serial.c | 212 ++++++++++++++++++++++---
>> 3 files changed, 210 insertions(+), 26 deletions(-)
>>
>> diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
>> index 8c1a4c7..1651c2c 100644
>> --- a/arch/arm/mach-omap2/serial.c
>> +++ b/arch/arm/mach-omap2/serial.c
>> @@ -189,6 +189,27 @@ static void omap_serial_fill_default_pads(struct omap_board_data *bdata)
>> }
>> }
>>
>> +static void omap_uart_wakeup_enable(struct platform_device *pdev, bool enable)
>> +{
>> + struct omap_uart_port_info *up = pdev->dev.platform_data;
>> +
>> + /* Set or clear wake-enable bit */
>> + if (up->wk_en && up->wk_mask) {
>> + u32 v = __raw_readl(up->wk_en);
>> + if (enable)
>> + v |= up->wk_mask;
>> + else
>> + v &= ~up->wk_mask;
>> + __raw_writel(v, up->wk_en);
>> + }
>
> I think I asked this in previous series, but can't remember the answer
> now... can't we enable bits in the WKEN registers once at init time,
> and then just use omap_hwmod_[enable|disable]_wakeup() here?
>
by default all bits are enabled in WKEN,
I will use omap_hwmod_[enable|disable]_wakeup() api's
if these API's take care of WKEN regs, then no issue
using the same.
>> + /* Enable or clear io-pad wakeup */
>> + if (enable)
>> + omap_device_enable_ioring_wakeup(pdev);
>> + else
>> + omap_device_disable_ioring_wakeup(pdev);
>> +}
>> +
>> static void omap_uart_idle_init(struct omap_uart_port_info *uart,
>> unsigned short num)
>> {
>> @@ -332,6 +353,7 @@ void __init omap_serial_init_port(struct omap_board_data *bdata)
>>
>> pdata->uartclk = OMAP24XX_BASE_BAUD * 16;
>> pdata->flags = UPF_BOOT_AUTOCONF;
>> + pdata->enable_wakeup = omap_uart_wakeup_enable;
>> if (bdata->id == omap_uart_con_id)
>> pdata->console_uart = true;
>>
>> diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h
>> index 2ca885b..ac30de8 100644
>> --- a/arch/arm/plat-omap/include/plat/omap-serial.h
>> +++ b/arch/arm/plat-omap/include/plat/omap-serial.h
>> @@ -65,6 +65,7 @@ struct omap_uart_port_info {
>> unsigned int errata;
>> unsigned int console_uart;
>>
>> + void (*enable_wakeup)(struct platform_device *, bool);
>> void __iomem *wk_st;
>> void __iomem *wk_en;
>> u32 wk_mask;
>> @@ -120,6 +121,7 @@ struct uart_omap_port {
>> char name[20];
>> unsigned long port_activity;
>> unsigned int errata;
>> + void (*enable_wakeup)(struct platform_device *, bool);
>> };
>>
>> #endif /* __OMAP_SERIAL_H__ */
>> diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
>> index 47cadf4..897416f 100644
>> --- a/drivers/tty/serial/omap-serial.c
>> +++ b/drivers/tty/serial/omap-serial.c
>> @@ -37,10 +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>
>> +#include <plat/omap_device.h>
>> +
>> +#define OMAP_UART_AUTOSUSPEND_DELAY (30 * HZ) /* Value is msecs */
>
> As Jon already pointed out, the HZ here is wrong. Please define this
> value in msecs.
>
corrected.
>> static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];
>>
>> @@ -94,6 +98,17 @@ serial_omap_get_divisor(struct uart_port *port, unsigned int baud)
>> return port->uartclk/(baud * divisor);
>> }
>>
>> +static inline void serial_omap_port_disable(struct uart_omap_port *up)
>> +{
>> + pm_runtime_mark_last_busy(&up->pdev->dev);
>> + pm_runtime_put_autosuspend(&up->pdev->dev);
>> +}
>> +
>> +static inline void serial_omap_port_enable(struct uart_omap_port *up)
>> +{
>> + pm_runtime_get_sync(&up->pdev->dev);
>> +}
>
> These inlines are not needed. Please use runtime PM calls directly in
> the code. For _enable(), this is straight forward. For _disable()
> though, I don't think you want (or need) to _mark_last_busy() for every
> instance of omap_port_disable(). You only need to do this for ones
> TX/RX transactions...
>
Fine. will modify.
>> static void serial_omap_stop_rxdma(struct uart_omap_port *up)
>> {
>> if (up->uart_dma.rx_dma_used) {
>> @@ -110,8 +125,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);
>> +
>> + serial_omap_port_enable(up);
>> up->ier |= UART_IER_MSI;
>> serial_out(up, UART_IER, up->ier);
>> + serial_omap_port_disable(up);
>
> ...for example, this one is not really activity based, so should
> probably just be pm_runtime_get_sync(), write the register, then
> pm_runtime_put() (async version.)
>
> I didn't look at all the others below, but they should be looked at
> individually.
>
ok. I will check them.
>> }
>>
>> static void serial_omap_stop_tx(struct uart_port *port)
>> @@ -131,21 +149,26 @@ static void serial_omap_stop_tx(struct uart_port *port)
>> up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE;
>> }
>>
>> + serial_omap_port_enable(up);
>> if (up->ier & UART_IER_THRI) {
>> up->ier &= ~UART_IER_THRI;
>> serial_out(up, UART_IER, up->ier);
>> }
>> +
>> + serial_omap_port_disable(up);
>> }
>>
>> static void serial_omap_stop_rx(struct uart_port *port)
>> {
>> struct uart_omap_port *up = (struct uart_omap_port *)port;
>>
>> + serial_omap_port_enable(up);
>> 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);
>> + serial_omap_port_disable(up);
>> }
>>
>> static inline void receive_chars(struct uart_omap_port *up, int *status)
>> @@ -261,8 +284,10 @@ static void serial_omap_start_tx(struct uart_port *port)
>> unsigned int start;
>> int ret = 0;
>>
>> + serial_omap_port_enable(up);
>> if (!up->use_dma) {
>> serial_omap_enable_ier_thri(up);
>> + serial_omap_port_disable(up);
>> return;
>> }
>>
>> @@ -354,9 +379,12 @@ static inline irqreturn_t serial_omap_irq(int irq, void *dev_id)
>> unsigned int iir, lsr;
>> unsigned long flags;
>>
>> + serial_omap_port_enable(up);
>> iir = serial_in(up, UART_IIR);
>> - if (iir & UART_IIR_NO_INT)
>> + if (iir & UART_IIR_NO_INT) {
>> + serial_omap_port_disable(up);
>> return IRQ_NONE;
>> + }
>>
>> spin_lock_irqsave(&up->port.lock, flags);
>> lsr = serial_in(up, UART_LSR);
>> @@ -378,6 +406,8 @@ static inline irqreturn_t serial_omap_irq(int irq, void *dev_id)
>> transmit_chars(up);
>>
>> spin_unlock_irqrestore(&up->port.lock, flags);
>> + serial_omap_port_disable(up);
>> +
>> up->port_activity = jiffies;
>> return IRQ_HANDLED;
>> }
>> @@ -388,11 +418,12 @@ static unsigned int serial_omap_tx_empty(struct uart_port *port)
>> unsigned long flags = 0;
>> unsigned int ret = 0;
>>
>> + serial_omap_port_enable(up);
>> 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);
>> -
>> + serial_omap_port_disable(up);
>> return ret;
>> }
>>
>> @@ -402,7 +433,10 @@ static unsigned int serial_omap_get_mctrl(struct uart_port *port)
>> unsigned char status;
>> unsigned int ret = 0;
>>
>> + serial_omap_port_enable(up);
>> status = check_modem_status(up);
>> + serial_omap_port_disable(up);
>> +
>> dev_dbg(up->port.dev, "serial_omap_get_mctrl+%d\n", up->pdev->id);
>>
>> if (status & UART_MSR_DCD)
>> @@ -434,7 +468,9 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
>> mcr |= UART_MCR_LOOP;
>>
>> mcr |= up->mcr;
>> + serial_omap_port_enable(up);
>> serial_out(up, UART_MCR, mcr);
>> + serial_omap_port_disable(up);
>> }
>>
>> static void serial_omap_break_ctl(struct uart_port *port, int break_state)
>> @@ -443,6 +479,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);
>> + serial_omap_port_enable(up);
>> spin_lock_irqsave(&up->port.lock, flags);
>> if (break_state == -1)
>> up->lcr |= UART_LCR_SBC;
>> @@ -450,6 +487,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);
>> + serial_omap_port_disable(up);
>> }
>>
>> static int serial_omap_startup(struct uart_port *port)
>> @@ -468,6 +506,7 @@ static int serial_omap_startup(struct uart_port *port)
>>
>> dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->pdev->id);
>>
>> + serial_omap_port_enable(up);
>> /*
>> * Clear the FIFO buffers and disable them.
>> * (they will be reenabled in set_termios())
>> @@ -523,6 +562,7 @@ 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);
>>
>> + serial_omap_port_disable(up);
>> up->port_activity = jiffies;
>> return 0;
>> }
>> @@ -533,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);
>> +
>> + serial_omap_port_enable(up);
>> /*
>> * Disable interrupts from this port
>> */
>> @@ -566,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;
>> }
>> + serial_omap_port_disable(up);
>> free_irq(up->port.irq, up);
>> }
>>
>> @@ -671,6 +714,10 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
>> baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/13);
>> quot = serial_omap_get_divisor(port, baud);
>>
>> + up->dll = quot & 0xff;
>> + up->dlh = quot >> 8;
>> + up->mdr1 = UART_OMAP_MDR1_DISABLE;
>> +
>> up->fcr = UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_01 |
>> UART_FCR_ENABLE_FIFO;
>> if (up->use_dma)
>> @@ -680,6 +727,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.
>> */
>> + serial_omap_port_enable(up);
>> spin_lock_irqsave(&up->port.lock, flags);
>>
>> /*
>> @@ -723,6 +771,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
>> up->ier |= UART_IER_MSI;
>> serial_out(up, UART_IER, up->ier);
>> serial_out(up, UART_LCR, cval); /* reset DLAB */
>> + up->lcr = cval;
>>
>> /* FIFOs and DMA Settings */
>>
>> @@ -758,8 +807,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
>> serial_out(up, UART_MCR, up->mcr);
>>
>> /* Protocol, Baud Rate, and Interrupt Settings */
>> -
>> - serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_DISABLE);
>> + serial_out(up, UART_OMAP_MDR1, up->mdr1);
>> serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
>>
>> up->efr = serial_in(up, UART_EFR);
>> @@ -769,8 +817,8 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
>> serial_out(up, UART_IER, 0);
>> serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
>>
>> - serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */
>> - serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */
>> + serial_out(up, UART_DLL, up->dll); /* LS of divisor */
>> + serial_out(up, UART_DLM, up->dlh); /* MS of divisor */
>>
>> serial_out(up, UART_LCR, 0);
>> serial_out(up, UART_IER, up->ier);
>> @@ -780,9 +828,11 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
>> serial_out(up, UART_LCR, cval);
>>
>> if (baud > 230400 && baud != 3000000)
>> - serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_13X_MODE);
>> + up->mdr1 = UART_OMAP_MDR1_13X_MODE;
>> else
>> - serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_16X_MODE);
>> + up->mdr1 = UART_OMAP_MDR1_16X_MODE;
>> +
>> + serial_out(up, UART_OMAP_MDR1, up->mdr1)
>>
>> /* Hardware Flow Control Configuration */
>>
>> @@ -810,6 +860,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);
>> + serial_omap_port_disable(up);
>> dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->pdev->id);
>> }
>>
>> @@ -821,6 +872,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);
>> +
>> + serial_omap_port_enable(up);
>> 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);
>> @@ -830,6 +883,10 @@ 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);
>> + if (state)
>> + pm_runtime_put_sync(&up->pdev->dev);
>> + else
>> + serial_omap_port_disable(up);
>
> OK, so this boils down to either a _put_sync() or a _mark_last_busy +
> _put_autosuspend(), depending on whether this is suspending or resuming,
> right?
>
yes also during bootup.
disable the ports immediately that are not used during bootup.
> Why the difference?
Need to put the ports down immediately during system wide suspend
other wise autosuspend delay will prevent system to enter
suspend state immediately.
>
>> }
>>
>> static void serial_omap_release_port(struct uart_port *port)
>> @@ -907,25 +964,31 @@ 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;
>> +
>> + serial_omap_port_enable(up);
>> wait_for_xmitr(up);
>> serial_out(up, UART_TX, ch);
>> + serial_omap_port_disable(up);
>> }
>>
>> 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;
>>
>> + serial_omap_port_enable(up);
>> + 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);
>> + serial_omap_port_disable(up);
>> + return status;
>> }
>>
>> #endif /* CONFIG_CONSOLE_POLL */
>>
>> #ifdef CONFIG_SERIAL_OMAP_CONSOLE
>> -
>> static struct uart_omap_port *serial_omap_console_ports[4];
>>
>> static struct uart_driver serial_omap_reg;
>> @@ -955,6 +1018,8 @@ serial_omap_console_write(struct console *co, const char *s,
>> else
>> spin_lock(&up->port.lock);
>>
>> + serial_omap_port_enable(up);
>> +
>> /*
>> * First save the IER then disable the interrupts
>> */
>> @@ -979,6 +1044,7 @@ serial_omap_console_write(struct console *co, const char *s,
>> if (up->msr_saved_flags)
>> check_modem_status(up);
>>
>> + serial_omap_port_disable(up);
>> if (locked)
>> spin_unlock(&up->port.lock);
>> local_irq_restore(flags);
>> @@ -1061,22 +1127,27 @@ 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)
>> + if (up) {
>> uart_suspend_port(&serial_omap_reg, &up->port);
>> + console_trylock();
>
> This locking needs to be explained. Why it is needed, but very
> importantly, why you are not checking the return value of the _trylock()
>
any print after this point can result in failure of immediate system suspending
due to delayed autosuspend from omap_console_write.
log prints after uart suspend and print them during resume.
>> + serial_omap_pm(&up->port, 3, 0);
>
> Why is this call needed?
>
Actually this is needed if no_console_suspend is used, for that
case the console will remain active since serial_omap_pm is not
getting called from serial_core and port is not suspended
immediately with put_sync.
prints during system wide suspend and delayed autosuspend
from console_write keeps system active in no_console_suspend case
so put in forced suspend state and log all prints to be printed later.
probably needs to a condition check if (no_console_suspend)
> uart_suspend_port() calls uart_change_pm() which should call the ->pm
> method of struct uart_ops (which is serial_omap_pm). What am I missing?
>
> Also notice you're not calling serial_omap_pm() in the resume method
> below to change it back.
>
>> + }
>> return 0;
>> }
>
> Also, device wakeup capability should be checked and enabled/disabled in
> the suspend path (not only the runtime suspend path.)
>
ok.
>> -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)
>> + if (up) {
>> uart_resume_port(&serial_omap_reg, &up->port);
>> + console_unlock();
>
> Again, please describe locking in comments.
>
> Also, what happens here if the console_trylock() in your suspend method
> failed?
>
need to add a flag to check and unlock
from return status of trylock.
>> + }
>> +
>> return 0;
>> }
>>
>> @@ -1097,6 +1168,7 @@ static void serial_omap_rx_timeout(unsigned long uart_no)
>> serial_omap_stop_rxdma(up);
>> up->ier |= (UART_IER_RDI | UART_IER_RLSI);
>> serial_out(up, UART_IER, up->ier);
>> + serial_omap_port_disable(up);
>> }
>> return;
>> }
>> @@ -1128,6 +1200,9 @@ static void serial_omap_rx_timeout(unsigned long uart_no)
>>
>> static void uart_rx_dma_callback(int lch, u16 ch_status, void *data)
>> {
>> + struct uart_omap_port *up = (struct uart_omap_port *)data;
>> +
>> + serial_omap_port_disable(up);
>> return;
>> }
>>
>> @@ -1135,6 +1210,7 @@ static int serial_omap_start_rxdma(struct uart_omap_port *up)
>> {
>> int ret = 0;
>>
>> + serial_omap_port_enable(up);
>> if (up->uart_dma.rx_dma_channel == -1) {
>> ret = omap_request_dma(up->uart_dma.uart_dma_rx,
>> "UART Rx DMA",
>> @@ -1214,6 +1290,7 @@ static void uart_tx_dma_callback(int lch, u16 ch_status, void *data)
>> serial_omap_stop_tx(&up->port);
>> up->uart_dma.tx_dma_used = false;
>> spin_unlock(&(up->uart_dma.tx_lock));
>> + serial_omap_port_disable(up);
>> } else {
>> omap_stop_dma(up->uart_dma.tx_dma_channel);
>> serial_omap_continue_tx(up);
>> @@ -1224,9 +1301,10 @@ static void uart_tx_dma_callback(int lch, u16 ch_status, void *data)
>>
>> static int serial_omap_probe(struct platform_device *pdev)
>> {
>> - struct uart_omap_port *up;
>> + struct uart_omap_port *up = NULL;
>> struct resource *mem, *irq, *dma_tx, *dma_rx;
>> struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data;
>> + struct omap_device *od;
>> int ret = -ENOSPC;
>>
>> mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> @@ -1276,12 +1354,20 @@ static int serial_omap_probe(struct platform_device *pdev)
>> up->port.ops = &serial_omap_pops;
>> up->port.line = pdev->id;
>>
>> - up->port.membase = omap_up_info->membase;
>> - up->port.mapbase = omap_up_info->mapbase;
>> + up->port.mapbase = mem->start;
>> + up->port.membase = ioremap(mem->start, mem->end - mem->start);
>> +
>> + if (!up->port.membase) {
>> + dev_err(&pdev->dev, "can't ioremap UART\n");
>> + ret = -ENOMEM;
>> + goto err1;
>> + }
>> +
>> up->port.flags = omap_up_info->flags;
>> - up->port.irqflags = omap_up_info->irqflags;
>> up->port.uartclk = omap_up_info->uartclk;
>> up->uart_dma.uart_base = mem->start;
>> + up->errata = omap_up_info->errata;
>> + up->enable_wakeup = omap_up_info->enable_wakeup;
>>
>> if (omap_up_info->dma_enabled) {
>> up->uart_dma.uart_dma_tx = dma_tx->start;
>> @@ -1295,18 +1381,36 @@ 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_enable(&pdev->dev);
>> + pm_runtime_irq_safe(&pdev->dev);
>> +
>> + if (omap_up_info->console_uart) {
>> + od = to_omap_device(up->pdev);
>> + omap_hwmod_idle(od->hwmods[0]);
>> + serial_omap_port_enable(up);
>> + serial_omap_port_disable(up);
>> + }
>> +
>> ui[pdev->id] = up;
>> serial_omap_add_console_port(up);
>>
>> ret = uart_add_one_port(&serial_omap_reg, &up->port);
>> if (ret != 0)
>> - goto do_release_region;
>> + goto err1;
>>
>> + dev_set_drvdata(&pdev->dev, up);
>> platform_set_drvdata(pdev, up);
>> +
>> return 0;
>> err:
>> dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n",
>> pdev->id, __func__, ret);
>> +err1:
>> + kfree(up);
>> do_release_region:
>> release_mem_region(mem->start, (mem->end - mem->start) + 1);
>> return ret;
>> @@ -1318,20 +1422,76 @@ static int serial_omap_remove(struct platform_device *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);
>> }
>> return 0;
>> }
>>
>> +static void omap_uart_restore_context(struct uart_omap_port *up)
>> +{
>> + u16 efr = 0;
>> +
>> + serial_out(up, UART_OMAP_MDR1, up->mdr1);
>> + serial_out(up, UART_LCR, 0xBF); /* Config B mode */
>> + efr = serial_in(up, UART_EFR);
>> + 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, 0xBF); /* 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, 0x80);
>> + serial_out(up, UART_MCR, up->mcr);
>> + serial_out(up, UART_LCR, 0xBF); /* Config B mode */
>> + serial_out(up, UART_EFR, efr);
>> + serial_out(up, UART_LCR, up->lcr);
>> + /* UART 16x mode */
>> + serial_out(up, UART_OMAP_MDR1, up->mdr1);
>> +}
>> +
>> +static int omap_serial_runtime_suspend(struct device *dev)
>> +{
>> + struct uart_omap_port *up = dev_get_drvdata(dev);
>> +
>> + if (!up)
>> + goto done;
>> +
>> + if (device_may_wakeup(dev))
>> + up->enable_wakeup(up->pdev, true);
>> + else
>> + up->enable_wakeup(up->pdev, false);
>
> I know the earlier code was doing it this way too, but an optimization
> would be to have the driver keep state whether the wakeups are enabled
> or disabled, so you don't have to keep calling this function (which can
> be expensive with the PRCM reads/writes.
>
if dynamically disabled from user space from sys-fs interface.
it may not reflect disable_wakup immediately if internal state machine of
wakeup is maintained within uart driver.
also need to modify the internals of this func pointer to use
hmwod API's as commented above.
> That state can also be used in the suspend path, which should also be
> managing wakeups.
>
ok.
>> +done:
>> + return 0;
>> +}
>> +
>> +static int omap_serial_runtime_resume(struct device *dev)
>> +{
>> + struct uart_omap_port *up = dev_get_drvdata(dev);
>> +
>> + if (up)
>> + omap_uart_restore_context(up);
>
> You only need to restore context when returning from off-mode. You
> should be using omap_device_get_context_loss_count (called via pdata
> function pointer) for this. See the MMC driver or DSS driver for how
> this is done.
>
agree, will use that API.
--
Thanks,
Govindraj.R
>> + return 0;
>> +}
>> +
>> +static const struct dev_pm_ops omap_serial_dev_pm_ops = {
>> + .suspend = serial_omap_suspend,
>> + .resume = serial_omap_resume,
>> + .runtime_suspend = omap_serial_runtime_suspend,
>> + .runtime_resume = omap_serial_runtime_resume,
>> +};
>> +
>> 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 = &omap_serial_dev_pm_ops,
>> },
>> };
>
> Kevin
> --
> To unsubscribe from this list: send the line "unsubscribe linux-serial" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
More information about the linux-arm-kernel
mailing list