Skip to contentSkip to conversation

serial: omap: Fix missing pm_runtime_resume handling by simplifying code

PATCH In review Tony Lindgren 2 participants 4 messages Mar 25, 2014 – Apr 10, 2014 TTY layer and serial drivers

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 serial: omap: Fix missing pm_runtime_resume handling by simplifying code

The lack of pm_runtime_resume handling for the device state leads into device wake-up interrupts not working after a while for runtime PM.

Also, serial-omap is confused about the use of device_may_wakeup. The checks for device_may_wakeup should only be done for suspend and resume, not for pm_runtime_suspend and pm_runtime_resume. The wake-up events for PM runtime should always be enabled.

The lack of pm_runtime_resume handling leads into device wake-up interrupts not working after a while for runtime PM.

Rather than try to patch over the issue of adding complex tests to the pm_runtime_resume, let's fix the issues properly:

1. Make serial_omap_enable_wakeup deal with all internal PM state handling so we don't need to test for up->wakeups_enabled elsewhere.

Later on once omap3 boots in device tree only mode we can also remove the up->wakeups_enabled flag and rely on the wake-up interrupt enable/disable state alone.

2. Do the device_may_wakeup checks in suspend and resume only, for runtime PM the wake-up events need to be always enabled.

3. Finally just call serial_omap_enable_wakeup and make sure we call it also in pm_runtime_resume.

4. Note that we also have to use disable_irq_nosync as serial_omap_irq calls pm_runtime_get_sync.

Fixes 2a0b965cfb6e (serial: omap: Add support for optional wake-up)
Cc [email protected] # v3.13+
drivers/tty/serial/omap-serial.c +17 -12
@@ -225,14 +225,19 @@ static inline void serial_omap_enable_wakeirq(struct uart_omap_port *up,
 	if (enable)
 		enable_irq(up->wakeirq);
 	else
-		disable_irq(up->wakeirq);
+		disable_irq_nosync(up->wakeirq);
 }
 
 static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
 {
 	struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
 
+	if (enable == up->wakeups_enabled)
+		return;
+
 	serial_omap_enable_wakeirq(up, enable);
+	up->wakeups_enabled = enable;
+
 	if (!pdata || !pdata->enable_wakeup)
 		return;
 
@@ -1495,6 +1500,11 @@ static int serial_omap_suspend(struct device *dev)
 	uart_suspend_port(&serial_omap_reg, &up->port);
 	flush_work(&up->qos_work);
 
+	if (device_may_wakeup(dev))
+		serial_omap_enable_wakeup(up, true);
+	else
+		serial_omap_enable_wakeup(up, false);
+
 	return 0;
 }
 
@@ -1502,6 +1512,9 @@ static int serial_omap_resume(struct device *dev)
 {
 	struct uart_omap_port *up = dev_get_drvdata(dev);
 
+	if (device_may_wakeup(dev))
+		serial_omap_enable_wakeup(up, false);
+
 	uart_resume_port(&serial_omap_reg, &up->port);
 
 	return 0;
@@ -1877,17 +1890,7 @@ static int serial_omap_runtime_suspend(struct device *dev)
 
 	up->context_loss_cnt = serial_omap_get_context_loss_count(up);
 
-	if (device_may_wakeup(dev)) {
-		if (!up->wakeups_enabled) {
-			serial_omap_enable_wakeup(up, true);
-			up->wakeups_enabled = true;
-		}
-	} else {
-		if (up->wakeups_enabled) {
-			serial_omap_enable_wakeup(up, false);
-			up->wakeups_enabled = false;
-		}
-	}
+	serial_omap_enable_wakeup(up, true);
 
 	up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
 	schedule_work(&up->qos_work);
@@ -1901,6 +1904,8 @@ static int serial_omap_runtime_resume(struct device *dev)
 
 	int loss_cnt = serial_omap_get_context_loss_count(up);
 
+	serial_omap_enable_wakeup(up, false);
+
 	if (loss_cnt < 0) {
 		dev_dbg(dev, "serial_omap_get_context_loss_count failed : %d\n",
 			loss_cnt);
#1 lore raw
Quoted 37 lines · Tony Lindgren

The lack of pm_runtime_resume handling for the device state leads into device wake-up interrupts not working after a while for runtime PM.

Also, serial-omap is confused about the use of device_may_wakeup. The checks for device_may_wakeup should only be done for suspend and resume, not for pm_runtime_suspend and pm_runtime_resume. The wake-up events for PM runtime should always be enabled.

The lack of pm_runtime_resume handling leads into device wake-up interrupts not working after a while for runtime PM.

Rather than try to patch over the issue of adding complex tests to the pm_runtime_resume, let's fix the issues properly:

1. Make serial_omap_enable_wakeup deal with all internal PM state handling so we don't need to test for up->wakeups_enabled elsewhere.

Later on once omap3 boots in device tree only mode we can also remove the up->wakeups_enabled flag and rely on the wake-up interrupt enable/disable state alone.

2. Do the device_may_wakeup checks in suspend and resume only, for runtime PM the wake-up events need to be always enabled.

3. Finally just call serial_omap_enable_wakeup and make sure we call it also in pm_runtime_resume.

4. Note that we also have to use disable_irq_nosync as serial_omap_irq calls pm_runtime_get_sync.

Fixes 2a0b965cfb6e (serial: omap: Add support for optional wake-up)
Cc [email protected] # v3.13+

drivers/tty/serial/omap-serial.c static inline void serial_omap_enable_wakeirq(struct uart_omap_port *up, · line 228

Quoted 5 lines
drivers/tty/serial/omap-serial.c +1 -1
@@ -225,14 +225,19 @@ static inline void serial_omap_enable_wakeirq(struct uart_omap_port *up,
 	if (enable)
 		enable_irq(up->wakeirq);
 	else
-		disable_irq(up->wakeirq);
+		disable_irq_nosync(up->wakeirq);

looks to me liket his should be a separate fix of its own...

drivers/tty/serial/omap-serial.c static inline void serial_omap_enable_wakeirq(struct uart_omap_port *up, · line 236

Quoted 6 lines
drivers/tty/serial/omap-serial.c +2 -0
 static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
 {
 	struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
 
+	if (enable == up->wakeups_enabled)
+		return;

is there any case where you would call this function twice with the same argument ?

Quoted 57 lines
drivers/tty/serial/omap-serial.c +14 -11
+
 	serial_omap_enable_wakeirq(up, enable);
+	up->wakeups_enabled = enable;
+
 	if (!pdata || !pdata->enable_wakeup)
 		return;
 
@@ -1495,6 +1500,11 @@ static int serial_omap_suspend(struct device *dev)
 	uart_suspend_port(&serial_omap_reg, &up->port);
 	flush_work(&up->qos_work);
 
+	if (device_may_wakeup(dev))
+		serial_omap_enable_wakeup(up, true);
+	else
+		serial_omap_enable_wakeup(up, false);
+
 	return 0;
 }
 
@@ -1502,6 +1512,9 @@ static int serial_omap_resume(struct device *dev)
 {
 	struct uart_omap_port *up = dev_get_drvdata(dev);
 
+	if (device_may_wakeup(dev))
+		serial_omap_enable_wakeup(up, false);
+
 	uart_resume_port(&serial_omap_reg, &up->port);
 
 	return 0;
@@ -1877,17 +1890,7 @@ static int serial_omap_runtime_suspend(struct device *dev)
 
 	up->context_loss_cnt = serial_omap_get_context_loss_count(up);
 
-	if (device_may_wakeup(dev)) {
-		if (!up->wakeups_enabled) {
-			serial_omap_enable_wakeup(up, true);
-			up->wakeups_enabled = true;
-		}
-	} else {
-		if (up->wakeups_enabled) {
-			serial_omap_enable_wakeup(up, false);
-			up->wakeups_enabled = false;
-		}
-	}
+	serial_omap_enable_wakeup(up, true);
 
 	up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
 	schedule_work(&up->qos_work);
@@ -1901,6 +1904,8 @@ static int serial_omap_runtime_resume(struct device *dev)
 
 	int loss_cnt = serial_omap_get_context_loss_count(up);
 
+	serial_omap_enable_wakeup(up, false);
+
 	if (loss_cnt < 0) {
 		dev_dbg(dev, "serial_omap_get_context_loss_count failed : %d\n",
 			loss_cnt);
Signature · 1 line
balbi
#2 lore raw
Quoted 35 lines · Felipe Balbi
Quoted 34 lines · Tony Lindgren

The lack of pm_runtime_resume handling for the device state leads into device wake-up interrupts not working after a while for runtime PM.

Also, serial-omap is confused about the use of device_may_wakeup. The checks for device_may_wakeup should only be done for suspend and resume, not for pm_runtime_suspend and pm_runtime_resume. The wake-up events for PM runtime should always be enabled.

The lack of pm_runtime_resume handling leads into device wake-up interrupts not working after a while for runtime PM.

Rather than try to patch over the issue of adding complex tests to the pm_runtime_resume, let's fix the issues properly:

1. Make serial_omap_enable_wakeup deal with all internal PM state handling so we don't need to test for up->wakeups_enabled elsewhere.

Later on once omap3 boots in device tree only mode we can also remove the up->wakeups_enabled flag and rely on the wake-up interrupt enable/disable state alone.

2. Do the device_may_wakeup checks in suspend and resume only, for runtime PM the wake-up events need to be always enabled.

3. Finally just call serial_omap_enable_wakeup and make sure we call it also in pm_runtime_resume.

4. Note that we also have to use disable_irq_nosync as serial_omap_irq calls pm_runtime_get_sync.

Fixes 2a0b965cfb6e (serial: omap: Add support for optional wake-up)
Cc [email protected] # v3.13+
Quoted 10 lines

drivers/tty/serial/omap-serial.c static inline void serial_omap_enable_wakeirq(struct uart_omap_port *up, · line 228

Quoted 8 lines
drivers/tty/serial/omap-serial.c +1 -1
@@ -225,14 +225,19 @@ static inline void serial_omap_enable_wakeirq(struct uart_omap_port *up,
 	if (enable)
 		enable_irq(up->wakeirq);
 	else
-		disable_irq(up->wakeirq);
+		disable_irq_nosync(up->wakeirq);

looks to me liket his should be a separate fix of its own...

I don't think fixing disable_irq_nosync here is not needed without fixing the rest. We're currently never calling serial_omap_enable_wakeup from the runtime PM resume path.

So serial_omap_enable_wakeirq(up, 0) would only get called in the case of device_may_wakeup disabled from serial_omap_runtime_suspend, which is not called from the interrupt context.

Quoted 9 lines

drivers/tty/serial/omap-serial.c static inline void serial_omap_enable_wakeirq(struct uart_omap_port *up, · line 236

Quoted 6 lines
drivers/tty/serial/omap-serial.c +2 -0
 static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
 {
 	struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
 
+	if (enable == up->wakeups_enabled)
+		return;

is there any case where you would call this function twice with the same argument ?

Yes at least when serial-omap is runtime PM enabled and doing a suspend without device_may_wakeup being set.

I'd like to get rid of the wakeups_enabled, but it's probably safest to do that when ripping out the remaining context_loos_cnt stuff after we've made omap3 to boot also in device tree only mode.

Regards,

Tony

Acked-by on v1 1/1
#3 lore raw
In reply to Tony Lindgren
Quoted 27 lines · Tony Lindgren

The lack of pm_runtime_resume handling for the device state leads into device wake-up interrupts not working after a while for runtime PM.

Also, serial-omap is confused about the use of device_may_wakeup. The checks for device_may_wakeup should only be done for suspend and resume, not for pm_runtime_suspend and pm_runtime_resume. The wake-up events for PM runtime should always be enabled.

The lack of pm_runtime_resume handling leads into device wake-up interrupts not working after a while for runtime PM.

Rather than try to patch over the issue of adding complex tests to the pm_runtime_resume, let's fix the issues properly:

1. Make serial_omap_enable_wakeup deal with all internal PM state handling so we don't need to test for up->wakeups_enabled elsewhere.

Later on once omap3 boots in device tree only mode we can also remove the up->wakeups_enabled flag and rely on the wake-up interrupt enable/disable state alone.

2. Do the device_may_wakeup checks in suspend and resume only, for runtime PM the wake-up events need to be always enabled.

3. Finally just call serial_omap_enable_wakeup and make sure we call it also in pm_runtime_resume.

Quoted 6 lines

4. Note that we also have to use disable_irq_nosync as serial_omap_irq calls pm_runtime_get_sync.

Fixes 2a0b965cfb6e (serial: omap: Add support for optional wake-up)
Cc [email protected] # v3.13+

FWIW:

Signature · 1 line
balbi