Skip to contentSkip to conversation

tty: serial: OMAP: work around broken driver, IP block

PATCH New Paul Walmsley 5 participants 13 messages Jan 21, 2012 – Jan 23, 2012

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover tty: serial: OMAP: work around broken driver, IP block

[ This series is targeted for merging during v3.3-rc ]

On v3.3-rc1, the OMAP serial console doesn't behave properly when power management is enabled (the default with omap2plus_defconfig). This seems to be due to a combination of a silicon bug in the UART IP block and broken FIFO settings in the OMAP serial driver.

This patch series attempts to work around these problems. The workarounds are not perfect; see the patch descriptions for more details.

Tested on OMAP3530 BeagleBoard. This series will need to be tested on OMAP2 and OMAP4 before it is ready to merge.

- Paul

---

Paul Walmsley (2):
tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA mode
tty: serial: OMAP: transmit FIFO threshold interrupts don't wake the chip

 arch/arm/plat-omap/include/plat/omap-serial.h |    1 
 drivers/tty/serial/omap-serial.c              |   86 ++++++++++++++++++++++++-
 2 files changed, 82 insertions(+), 5 deletions(-)
#1 lore raw

PATCH 1/2 tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA mode

Ensure FIFO levels are set correctly in non-DMA mode (the default). This patch will cause a receive FIFO threshold interrupt to be raised when there is at least one byte in the RX FIFO. It will also cause a transmit FIFO threshold interrupt when there is only one byte remaining in the TX FIFO.

These changes fix the receive interrupt problem and part of the transmit interrupt problem. A separate set of issues must be worked around for the transmit path to have a basic level of functionality; a subsequent patch will address these.

DMA operation is unaffected by this patch.

Cc Tomi Valkeinen <[email protected]>
Cc Govindraj Raja <[email protected]>
Cc Kevin Hilman <[email protected]>
---
 drivers/tty/serial/omap-serial.c |   35 +++++++++++++++++++++++++++++++----
 1 files changed, 31 insertions(+), 4 deletions(-)
drivers/tty/serial/omap-serial.c +31 -4
index d192dcb..9de7d71 100644
@@ -46,6 +46,18 @@
 
 #define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/
 
+/* SCR register bitmasks */
+#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK		(1 << 7)
+#define OMAP_UART_SCR_TX_TRIG_GRANU1_MASK		(1 << 6)
+
+/* FCR register bitmasks */
+#define OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT		6
+#define OMAP_UART_FCR_RX_FIFO_TRIG_MASK			(0x3 << 6)
+#define OMAP_UART_FCR_TX_FIFO_TRIG_SHIFT		4
+
+/* TLR register bitmasks */
+#define OMAP_UART_TLR_TX_FIFO_TRIG_DMA_SHIFT		0
+
 static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];
 
 /* Forward declaration of functions */
@@ -694,6 +706,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
 	unsigned char efr = 0;
 	unsigned long flags = 0;
 	unsigned int baud, quot;
+	u32 tlr;
 
 	switch (termios->c_cflag & CSIZE) {
 	case CS5:
@@ -811,14 +824,28 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
 	up->mcr = serial_in(up, UART_MCR);
 	serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
 	/* FIFO ENABLE, DMA MODE */
-	serial_out(up, UART_FCR, up->fcr);
-	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+	up->scr |= OMAP_UART_SCR_TX_TRIG_GRANU1_MASK;
+	up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK;
 
 	if (up->use_dma) {
-		serial_out(up, UART_TI752_TLR, 0);
-		up->scr |= (UART_FCR_TRIGGER_4 | UART_FCR_TRIGGER_8);
+		tlr = 0;
+	} else {
+		up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
+
+		/* Set receive FIFO threshold to 1 */
+		up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK;
+		up->fcr |= (0x1 << OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT);
+
+		/* Set TX FIFO threshold to "63" (actually 1) */
+		up->fcr |= (0x3 << OMAP_UART_FCR_TX_FIFO_TRIG_SHIFT);
+		tlr = (0xf << OMAP_UART_TLR_TX_FIFO_TRIG_DMA_SHIFT);
 	}
 
+	serial_out(up, UART_TI752_TLR, tlr);
+	serial_out(up, UART_FCR, up->fcr);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
 	serial_out(up, UART_OMAP_SCR, up->scr);
 
 	serial_out(up, UART_EFR, up->efr);
#2 lore raw

PATCH 1/2 tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA mode

Quoted 62 lines · Paul Walmsley

Ensure FIFO levels are set correctly in non-DMA mode (the default). This patch will cause a receive FIFO threshold interrupt to be raised when there is at least one byte in the RX FIFO. ?It will also cause a transmit FIFO threshold interrupt when there is only one byte remaining in the TX FIFO.

These changes fix the receive interrupt problem and part of the transmit interrupt problem. ?A separate set of issues must be worked around for the transmit path to have a basic level of functionality; a subsequent patch will address these.

DMA operation is unaffected by this patch.

Cc Tomi Valkeinen <[email protected]>
Cc Govindraj Raja <[email protected]>
Cc Kevin Hilman <[email protected]>

---
?drivers/tty/serial/omap-serial.c | ? 35 +++++++++++++++++++++++++++++++---- ?1 files changed, 31 insertions(+), 4 deletions(-)

drivers/tty/serial/omap-serial.c +0 -0
index d192dcb..9de7d71 100644
@@ -46,6 +46,18 @@
 

?#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/

drivers/tty/serial/omap-serial.c +12 -0
+/* SCR register bitmasks */
+#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK ? ? ? ? ? ? ?(1 << 7)
+#define OMAP_UART_SCR_TX_TRIG_GRANU1_MASK ? ? ? ? ? ? ?(1 << 6)
+
+/* FCR register bitmasks */
+#define OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT ? ? ? ? ? ? ? 6
+#define OMAP_UART_FCR_RX_FIFO_TRIG_MASK ? ? ? ? ? ? ? ? ? ? ? ?(0x3 << 6)
+#define OMAP_UART_FCR_TX_FIFO_TRIG_SHIFT ? ? ? ? ? ? ? 4
+
+/* TLR register bitmasks */
+#define OMAP_UART_TLR_TX_FIFO_TRIG_DMA_SHIFT ? ? ? ? ? 0
+

?static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];

?/* Forward declaration of functions */

drivers/tty/serial/omap-serial.c +1 -0
@@ -694,6 +706,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
 ? ? ? ?unsigned char efr = 0;
 ? ? ? ?unsigned long flags = 0;
 ? ? ? ?unsigned int baud, quot;
+ ? ? ? u32 tlr;
 
 ? ? ? ?switch (termios->c_cflag & CSIZE) {

? ? ? ?case CS5:

drivers/tty/serial/omap-serial.c +0 -0
@@ -811,14 +824,28 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
 ? ? ? ?up->mcr = serial_in(up, UART_MCR);
 ? ? ? ?serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);

? ? ? ?/* FIFO ENABLE, DMA MODE */
- ? ? ? serial_out(up, UART_FCR, up->fcr);
- ? ? ? serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); +
+ ? ? ? up->scr |= OMAP_UART_SCR_TX_TRIG_GRANU1_MASK;
+ ? ? ? up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK;

Quoted 3 lines

? ? ? ?if (up->use_dma) {
- ? ? ? ? ? ? ? serial_out(up, UART_TI752_TLR, 0);
- ? ? ? ? ? ? ? up->scr |= (UART_FCR_TRIGGER_4 | UART_FCR_TRIGGER_8);

Any reasons for removing scr config for dma mode ?

Quoted 26 lines

+ ? ? ? ? ? ? ? tlr = 0;
+ ? ? ? } else {
+ ? ? ? ? ? ? ? up->scr &= ~OMAP_UART_SCR_TX_EMPTY;

drivers/tty/serial/omap-serial.c +8 -0
+
+ ? ? ? ? ? ? ? /* Set receive FIFO threshold to 1 */
+ ? ? ? ? ? ? ? up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK;
+ ? ? ? ? ? ? ? up->fcr |= (0x1 << OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT);
+
+ ? ? ? ? ? ? ? /* Set TX FIFO threshold to "63" (actually 1) */
+ ? ? ? ? ? ? ? up->fcr |= (0x3 << OMAP_UART_FCR_TX_FIFO_TRIG_SHIFT);
+ ? ? ? ? ? ? ? tlr = (0xf << OMAP_UART_TLR_TX_FIFO_TRIG_DMA_SHIFT);

? ? ? ?}

+ ? ? ? serial_out(up, UART_TI752_TLR, tlr);
+ ? ? ? serial_out(up, UART_FCR, up->fcr);
+ ? ? ? serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); +
? ? ? ?serial_out(up, UART_OMAP_SCR, up->scr);

? ? ? ?serial_out(up, UART_EFR, up->efr);

--
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

#3 lore raw

PATCH 1/2 tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA mode

Quoted 63 lines · Govindraj
Quoted 62 lines · Paul Walmsley

Ensure FIFO levels are set correctly in non-DMA mode (the default). This patch will cause a receive FIFO threshold interrupt to be raised when there is at least one byte in the RX FIFO. ?It will also cause a transmit FIFO threshold interrupt when there is only one byte remaining in the TX FIFO.

These changes fix the receive interrupt problem and part of the transmit interrupt problem. ?A separate set of issues must be worked around for the transmit path to have a basic level of functionality; a subsequent patch will address these.

DMA operation is unaffected by this patch.

Cc Tomi Valkeinen <[email protected]>
Cc Govindraj Raja <[email protected]>
Cc Kevin Hilman <[email protected]>

---
?drivers/tty/serial/omap-serial.c | ? 35 +++++++++++++++++++++++++++++++---- ?1 files changed, 31 insertions(+), 4 deletions(-)

drivers/tty/serial/omap-serial.c +0 -0
index d192dcb..9de7d71 100644
@@ -46,6 +46,18 @@
 

?#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/

drivers/tty/serial/omap-serial.c +12 -0
+/* SCR register bitmasks */
+#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK ? ? ? ? ? ? ?(1 << 7)
+#define OMAP_UART_SCR_TX_TRIG_GRANU1_MASK ? ? ? ? ? ? ?(1 << 6)
+
+/* FCR register bitmasks */
+#define OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT ? ? ? ? ? ? ? 6
+#define OMAP_UART_FCR_RX_FIFO_TRIG_MASK ? ? ? ? ? ? ? ? ? ? ? ?(0x3 << 6)
+#define OMAP_UART_FCR_TX_FIFO_TRIG_SHIFT ? ? ? ? ? ? ? 4
+
+/* TLR register bitmasks */
+#define OMAP_UART_TLR_TX_FIFO_TRIG_DMA_SHIFT ? ? ? ? ? 0
+

?static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];

?/* Forward declaration of functions */

drivers/tty/serial/omap-serial.c +1 -0
@@ -694,6 +706,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
 ? ? ? ?unsigned char efr = 0;
 ? ? ? ?unsigned long flags = 0;
 ? ? ? ?unsigned int baud, quot;
+ ? ? ? u32 tlr;
 
 ? ? ? ?switch (termios->c_cflag & CSIZE) {

? ? ? ?case CS5:

drivers/tty/serial/omap-serial.c +0 -0
@@ -811,14 +824,28 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
 ? ? ? ?up->mcr = serial_in(up, UART_MCR);
 ? ? ? ?serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);

? ? ? ?/* FIFO ENABLE, DMA MODE */
- ? ? ? serial_out(up, UART_FCR, up->fcr);
- ? ? ? serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); +
+ ? ? ? up->scr |= OMAP_UART_SCR_TX_TRIG_GRANU1_MASK;
+ ? ? ? up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK;

Quoted 6 lines
Quoted 3 lines

? ? ? ?if (up->use_dma) {
- ? ? ? ? ? ? ? serial_out(up, UART_TI752_TLR, 0);
- ? ? ? ? ? ? ? up->scr |= (UART_FCR_TRIGGER_4 | UART_FCR_TRIGGER_8);

Any reasons for removing scr config for dma mode ?

Please ignore.. was in hurry.

Quoted 27 lines
Quoted 27 lines

+ ? ? ? ? ? ? ? tlr = 0;
+ ? ? ? } else {
+ ? ? ? ? ? ? ? up->scr &= ~OMAP_UART_SCR_TX_EMPTY;

drivers/tty/serial/omap-serial.c +8 -0
+
+ ? ? ? ? ? ? ? /* Set receive FIFO threshold to 1 */
+ ? ? ? ? ? ? ? up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK;
+ ? ? ? ? ? ? ? up->fcr |= (0x1 << OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT);
+
+ ? ? ? ? ? ? ? /* Set TX FIFO threshold to "63" (actually 1) */
+ ? ? ? ? ? ? ? up->fcr |= (0x3 << OMAP_UART_FCR_TX_FIFO_TRIG_SHIFT);
+ ? ? ? ? ? ? ? tlr = (0xf << OMAP_UART_TLR_TX_FIFO_TRIG_DMA_SHIFT);

? ? ? ?}

+ ? ? ? serial_out(up, UART_TI752_TLR, tlr);
+ ? ? ? serial_out(up, UART_FCR, up->fcr);
+ ? ? ? serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); +
? ? ? ?serial_out(up, UART_OMAP_SCR, up->scr);

? ? ? ?serial_out(up, UART_EFR, up->efr);

--
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

#4 lore raw

PATCH 2/2 tty: serial: OMAP: transmit FIFO threshold interrupts don't wake the chip

In reply to Paul Walmsley

It seems that when the transmit FIFO threshold is reached on OMAP UARTs, it does not result in a PRCM wakeup. This appears to be a silicon bug. This means that if the MPU powerdomain is in a low-power state, the MPU will not be awakened to refill the FIFO until the next interrupt from another device.

The best solution, at least for the short term, would be for the OMAP serial driver to call a OMAP subarchitecture function to prevent the MPU powerdomain from entering a low power state while the FIFO has data to transmit. However, we no longer have a clean way to do this, since patches that add platform_data function pointers have been deprecated by the OMAP maintainer. So we attempt to work around this as well. The workarounds depend on the setting of CONFIG_CPU_IDLE.

When CONFIG_CPU_IDLE=n, the driver will now only transmit one byte at a time. This causes the transmit FIFO threshold interrupt to stay active until there is no more data to be sent. Thus, the MPU powerdomain stays on during transmits. Aside from that energy consumption penalty, each transmitted byte results in a huge number of UART interrupts -- about five per byte. This wastes CPU time and is quite inefficient, but is probably the most expedient workaround in this case.

When CONFIG_CPU_IDLE=y, there is a slightly more direct workaround:
the PM QoS constraint can be abused to keep the MPU powerdomain on. This results in a normal number of interrupts, but, similar to the above workaround, wastes power by preventing the MPU from entering WFI.

Future patches are planned for the 3.4 merge window to implement more efficient, but also more disruptive, workarounds to these problems.

DMA operation is unaffected by this patch.

Cc Tomi Valkeinen <[email protected]>
Cc Govindraj Raja <[email protected]>
Cc Kevin Hilman <[email protected]>
---
 arch/arm/plat-omap/include/plat/omap-serial.h |    1 
 drivers/tty/serial/omap-serial.c              |   51 +++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 1 deletions(-)
arch/arm/plat-omap/include/plat/omap-serial.h +1 -0
index 9ff4444..12a64eb 100644
@@ -131,6 +131,7 @@ struct uart_omap_port {
 	u32			context_loss_cnt;
 	u32			errata;
 	u8			wakeups_enabled;
+	u8			max_tx_count;
 
 	struct pm_qos_request	pm_qos_request;
 	u32			latency;
drivers/tty/serial/omap-serial.c +50 -1

81 lines not on this page · raw message

#5 lore raw

PATCH 2/2 tty: serial: OMAP: transmit FIFO threshold interrupts don't wake the chip

Quoted 23 lines · Paul Walmsley

It seems that when the transmit FIFO threshold is reached on OMAP UARTs, it does not result in a PRCM wakeup. ?This appears to be a silicon bug. ?This means that if the MPU powerdomain is in a low-power state, the MPU will not be awakened to refill the FIFO until the next interrupt from another device.

The best solution, at least for the short term, would be for the OMAP serial driver to call a OMAP subarchitecture function to prevent the MPU powerdomain from entering a low power state while the FIFO has data to transmit. ?However, we no longer have a clean way to do this, since patches that add platform_data function pointers have been deprecated by the OMAP maintainer. ?So we attempt to work around this as well. ?The workarounds depend on the setting of CONFIG_CPU_IDLE.

When CONFIG_CPU_IDLE=n, the driver will now only transmit one byte at a time. ?This causes the transmit FIFO threshold interrupt to stay active until there is no more data to be sent. ?Thus, the MPU powerdomain stays on during transmits. ?Aside from that energy consumption penalty, each transmitted byte results in a huge number of UART interrupts -- about five per byte. ?This wastes CPU time and is quite inefficient, but is probably the most expedient workaround in this case.

Quoted 9 lines

When CONFIG_CPU_IDLE=y, there is a slightly more direct workaround:
the PM QoS constraint can be abused to keep the MPU powerdomain on. This results in a normal number of interrupts, but, similar to the above workaround, wastes power by preventing the MPU from entering WFI.

Future patches are planned for the 3.4 merge window to implement more efficient, but also more disruptive, workarounds to these problems.

With these two patches number of interrupts seems to increase by 24x after boot up.

[...]

/ # cat /proc/interrupts | grep "UART2"
74: 3902 INTC OMAP UART2
/ #
[...]

without these two patches + cpu_idle enabled

[...]

/ #
/ # cat /proc/interrupts | grep "UART2"
74: 158 INTC OMAP UART2
/ #

[...]

I am using beagle xm and 3.3rc1

Looks like there are far two many uart irqs which keeps the mpu busy and thus preventing uart sluggishness.

--
Thanks,
Govindraj.R

Quoted 119 lines

DMA operation is unaffected by this patch.

Cc Tomi Valkeinen <[email protected]>
Cc Govindraj Raja <[email protected]>
Cc Kevin Hilman <[email protected]>

---
?arch/arm/plat-omap/include/plat/omap-serial.h | ? ?1 ?drivers/tty/serial/omap-serial.c ? ? ? ? ? ? ?| ? 51 +++++++++++++++++++++++++ ?2 files changed, 51 insertions(+), 1 deletions(-)

arch/arm/plat-omap/include/plat/omap-serial.h +1 -0
index 9ff4444..12a64eb 100644
@@ -131,6 +131,7 @@ struct uart_omap_port {
 ? ? ? ?u32 ? ? ? ? ? ? ? ? ? ? context_loss_cnt;
 ? ? ? ?u32 ? ? ? ? ? ? ? ? ? ? errata;
 ? ? ? ?u8 ? ? ? ? ? ? ? ? ? ? ?wakeups_enabled;
+ ? ? ? u8 ? ? ? ? ? ? ? ? ? ? ?max_tx_count;
 
 ? ? ? ?struct pm_qos_request ? pm_qos_request;
 ? ? ? ?u32 ? ? ? ? ? ? ? ? ? ? latency;
drivers/tty/serial/omap-serial.c +43 -0
index 9de7d71..621fde5 100644
@@ -88,6 +88,49 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up)
 ? ? ? ?serial_out(up, UART_FCR, 0);
 ?}
 
+/**
+ * serial_omap_block_cpu_low_power_state - prevent MPU pwrdm from leaving ON
+ * @up: struct uart_omap_port *
+ *
+ * Prevent the MPU powerdomain from entering a power state lower than
+ * ON. ?(It should be sufficient to prevent it from entering INACTIVE,
+ * but there is presently no easy way to do this.) ?This works around
+ * a suspected silicon bug in the OMAP UART IP blocks. ?The UARTs should
+ * wake the PRCM when the transmit FIFO threshold interrupt is raised, but
+ * they do not. ? See also serial_omap_allow_cpu_low_power_state(). ?No
+ * return value.
+ */
+static void serial_omap_block_cpu_low_power_state(struct uart_omap_port *up)
+{
+#ifdef CONFIG_CPU_IDLE
+ ? ? ? up->latency = 1;
+ ? ? ? schedule_work(&up->qos_work);
+#else
+ ? ? ? up->max_tx_count = 1;
+#endif
+}
+
+/**
+ * serial_omap_allow_cpu_low_power_state - remove power state restriction on MPU
+ * @up: struct uart_omap_port *
+ *
+ * Cancel the effects of serial_omap_block_cpu_low_power_state().
+ * This should allow the MPU powerdomain to enter a power state lower
+ * than ON, assuming the rest of the kernel is not restricting it.
+ * This works around a suspected silicon bug in the OMAP UART IP
+ * blocks. ?The UARTs should wake the PRCM when the transmit FIFO
+ * threshold interrupt is raised, but they do not. ?No return value.
+ */
+static void serial_omap_allow_cpu_low_power_state(struct uart_omap_port *up)
+{
+#ifdef CONFIG_CPU_IDLE
+ ? ? ? up->latency = up->calc_latency;
+ ? ? ? schedule_work(&up->qos_work);
+#else
+ ? ? ? up->max_tx_count = up->port.fifosize / 4;
+#endif
+}
+

?/*
?* serial_omap_get_divisor - calculate divisor value ?* @port: uart port info

drivers/tty/serial/omap-serial.c +7 -1
@@ -163,6 +206,9 @@ static void serial_omap_stop_tx(struct uart_port *port)
 ? ? ? ? ? ? ? ?serial_out(up, UART_IER, up->ier);
 ? ? ? ?}
 
+ ? ? ? if (!up->use_dma)
+ ? ? ? ? ? ? ? serial_omap_allow_cpu_low_power_state(up);
+
 ? ? ? ?pm_runtime_mark_last_busy(&up->pdev->dev);
 ? ? ? ?pm_runtime_put_autosuspend(&up->pdev->dev);
 ?}
@@ -264,7 +310,7 @@ static void transmit_chars(struct uart_omap_port *up)
 ? ? ? ? ? ? ? ?serial_omap_stop_tx(&up->port);
 ? ? ? ? ? ? ? ?return;
 ? ? ? ?}
- ? ? ? count = up->port.fifosize / 4;
+ ? ? ? count = up->max_tx_count;
 ? ? ? ?do {
 ? ? ? ? ? ? ? ?serial_out(up, UART_TX, xmit->buf[xmit->tail]);
 ? ? ? ? ? ? ? ?xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
@@ -297,6 +343,7 @@ static void serial_omap_start_tx(struct uart_port *port)
 
 ? ? ? ?if (!up->use_dma) {
 ? ? ? ? ? ? ? ?pm_runtime_get_sync(&up->pdev->dev);
+ ? ? ? ? ? ? ? serial_omap_block_cpu_low_power_state(up);
 ? ? ? ? ? ? ? ?serial_omap_enable_ier_thri(up);
 ? ? ? ? ? ? ? ?pm_runtime_mark_last_busy(&up->pdev->dev);
 ? ? ? ? ? ? ? ?pm_runtime_put_autosuspend(&up->pdev->dev);
@@ -1421,6 +1468,8 @@ static int serial_omap_probe(struct platform_device *pdev)
 ? ? ? ?up->port.fifosize = 64;
 ? ? ? ?up->port.ops = &serial_omap_pops;
 
+ ? ? ? up->max_tx_count = up->port.fifosize / 4;
+
 ? ? ? ?if (pdev->dev.of_node)
 ? ? ? ? ? ? ? ?up->port.line = of_alias_get_id(pdev->dev.of_node, "serial");

? ? ? ?else

--
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

#6 lore raw

PATCH 2/2 tty: serial: OMAP: transmit FIFO threshold interrupts don't wake the chip

Quoted 30 lines · Govindraj
Quoted 29 lines · Paul Walmsley

It seems that when the transmit FIFO threshold is reached on OMAP UARTs, it does not result in a PRCM wakeup. ?This appears to be a silicon bug. ?This means that if the MPU powerdomain is in a low-power state, the MPU will not be awakened to refill the FIFO until the next interrupt from another device.

The best solution, at least for the short term, would be for the OMAP serial driver to call a OMAP subarchitecture function to prevent the MPU powerdomain from entering a low power state while the FIFO has data to transmit. ?However, we no longer have a clean way to do this, since patches that add platform_data function pointers have been deprecated by the OMAP maintainer. ?So we attempt to work around this as well. ?The workarounds depend on the setting of CONFIG_CPU_IDLE.

When CONFIG_CPU_IDLE=n, the driver will now only transmit one byte at a time. ?This causes the transmit FIFO threshold interrupt to stay active until there is no more data to be sent. ?Thus, the MPU powerdomain stays on during transmits. ?Aside from that energy consumption penalty, each transmitted byte results in a huge number of UART interrupts -- about five per byte. ?This wastes CPU time and is quite inefficient, but is probably the most expedient workaround in this case.

When CONFIG_CPU_IDLE=y, there is a slightly more direct workaround:
the PM QoS constraint can be abused to keep the MPU powerdomain on. This results in a normal number of interrupts, but, similar to the above workaround, wastes power by preventing the MPU from entering WFI.

Quoted 6 lines
Quoted 3 lines

Future patches are planned for the 3.4 merge window to implement more efficient, but also more disruptive, workarounds to these problems.

With these two patches number of interrupts seems to increase by 24x after boot up.

If you re-read the patch description that you quoted above, you'll see that this behavior was clearly mentioned:

Quoted 9 lines
Quoted 8 lines

When CONFIG_CPU_IDLE=n, the driver will now only transmit one byte at a time. ?This causes the transmit FIFO threshold interrupt to stay active until there is no more data to be sent. ?Thus, the MPU powerdomain stays on during transmits. ?Aside from that energy consumption penalty, each transmitted byte results in a huge number of UART interrupts -- about five per byte. ?This wastes CPU time and is quite inefficient, but is probably the most expedient workaround in this case.

Quoted 6 lines

/ # cat /proc/interrupts | grep "UART2"
74: 3902 INTC OMAP UART2
/ #
[...]

without these two patches + cpu_idle enabled

You should compare apples to apples. The omap-serial.c without these two patches is unusable as a console when CONFIG_CPU_IDLE=n; there are long lags between transmit FIFO drains. CONFIG_CPU_IDLE is the setting in the standard omap2plus_defconfig.

I guess it was never tested with that standard config? Or were the v3.3 omap-serial patch series submitted with this bug known?

Quoted 6 lines

[...]

/ #
/ # cat /proc/interrupts | grep "UART2"
74: 158 INTC OMAP UART2
/ #

The only reason why the driver 'works' when CONFIG_CPU_IDLE=y is because the omap-serial.c RX path wakeup latency constraint calculation is broken. It adds a 1 microsecond latency constraint to the CPU, when it should be adding a ~ 1100 microsecond latency constraint to the CPU. (Well, ~ 5500 microsecond, but that's another issue.)

And this keeps the CPU from entering a low-power state not just during transmits, but during the entire time the driver is active for the console UART.

Quoted 4 lines

I am using beagle xm and 3.3rc1

Looks like there are far two many uart irqs which keeps the mpu busy and thus preventing uart sluggishness.

Yes, that's known behavior, as described in the quoted patch description. It's not clear why the number of interrupts is ~5x the number of transmitted bytes, rather than say 2x the number of transmitted bytes. But the IRQ handler in omap-serial.c is also a mess, so that may have something to do with it.

Do you have a better workaround for the CONFIG_CPU_IDLE=n case that is acceptable for the -rc series? If so, perhaps you can post it?

- Paul

#7 lore raw

PATCH 2/2 tty: serial: OMAP: transmit FIFO threshold interrupts don't wake the chip

In reply to Govindraj
Quoted 53 lines · Govindraj
Quoted 23 lines · Paul Walmsley

It seems that when the transmit FIFO threshold is reached on OMAP UARTs, it does not result in a PRCM wakeup. ?This appears to be a silicon bug. ?This means that if the MPU powerdomain is in a low-power state, the MPU will not be awakened to refill the FIFO until the next interrupt from another device.

The best solution, at least for the short term, would be for the OMAP serial driver to call a OMAP subarchitecture function to prevent the MPU powerdomain from entering a low power state while the FIFO has data to transmit. ?However, we no longer have a clean way to do this, since patches that add platform_data function pointers have been deprecated by the OMAP maintainer. ?So we attempt to work around this as well. ?The workarounds depend on the setting of CONFIG_CPU_IDLE.

When CONFIG_CPU_IDLE=n, the driver will now only transmit one byte at a time. ?This causes the transmit FIFO threshold interrupt to stay active until there is no more data to be sent. ?Thus, the MPU powerdomain stays on during transmits. ?Aside from that energy consumption penalty, each transmitted byte results in a huge number of UART interrupts -- about five per byte. ?This wastes CPU time and is quite inefficient, but is probably the most expedient workaround in this case.

Quoted 9 lines

When CONFIG_CPU_IDLE=y, there is a slightly more direct workaround:
the PM QoS constraint can be abused to keep the MPU powerdomain on. This results in a normal number of interrupts, but, similar to the above workaround, wastes power by preventing the MPU from entering WFI.

Future patches are planned for the 3.4 merge window to implement more efficient, but also more disruptive, workarounds to these problems.

With these two patches number of interrupts seems to increase by 24x after boot up.

[...]

/ # cat /proc/interrupts | grep "UART2"
?74: ? ? ? 3902 ? ? ?INTC ?OMAP UART2
/ #
[...]

without these two patches + cpu_idle enabled

[...]

/ #
/ # cat /proc/interrupts | grep "UART2"
?74: ? ? ? ?158 ? ? ?INTC ?OMAP UART2
/ #

Quoted 8 lines

[...]

I am using beagle xm and 3.3rc1

Looks like there are far two many uart irqs which keeps the mpu busy and thus preventing uart sluggishness.

after looking in closely,
looks like every byte is getting transferred in case of non cpu_idle cases causing 1 irq for each tx.

Quoted 124 lines

--
Thanks,
Govindraj.R

Quoted 119 lines

DMA operation is unaffected by this patch.

Cc Tomi Valkeinen <[email protected]>
Cc Govindraj Raja <[email protected]>
Cc Kevin Hilman <[email protected]>

---
?arch/arm/plat-omap/include/plat/omap-serial.h | ? ?1 ?drivers/tty/serial/omap-serial.c ? ? ? ? ? ? ?| ? 51 +++++++++++++++++++++++++ ?2 files changed, 51 insertions(+), 1 deletions(-)

arch/arm/plat-omap/include/plat/omap-serial.h +1 -0
index 9ff4444..12a64eb 100644
@@ -131,6 +131,7 @@ struct uart_omap_port {
 ? ? ? ?u32 ? ? ? ? ? ? ? ? ? ? context_loss_cnt;
 ? ? ? ?u32 ? ? ? ? ? ? ? ? ? ? errata;
 ? ? ? ?u8 ? ? ? ? ? ? ? ? ? ? ?wakeups_enabled;
+ ? ? ? u8 ? ? ? ? ? ? ? ? ? ? ?max_tx_count;
 
 ? ? ? ?struct pm_qos_request ? pm_qos_request;
 ? ? ? ?u32 ? ? ? ? ? ? ? ? ? ? latency;
drivers/tty/serial/omap-serial.c +43 -0
index 9de7d71..621fde5 100644
@@ -88,6 +88,49 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up)
 ? ? ? ?serial_out(up, UART_FCR, 0);
 ?}
 
+/**
+ * serial_omap_block_cpu_low_power_state - prevent MPU pwrdm from leaving ON
+ * @up: struct uart_omap_port *
+ *
+ * Prevent the MPU powerdomain from entering a power state lower than
+ * ON. ?(It should be sufficient to prevent it from entering INACTIVE,
+ * but there is presently no easy way to do this.) ?This works around
+ * a suspected silicon bug in the OMAP UART IP blocks. ?The UARTs should
+ * wake the PRCM when the transmit FIFO threshold interrupt is raised, but
+ * they do not. ? See also serial_omap_allow_cpu_low_power_state(). ?No
+ * return value.
+ */
+static void serial_omap_block_cpu_low_power_state(struct uart_omap_port *up)
+{
+#ifdef CONFIG_CPU_IDLE
+ ? ? ? up->latency = 1;
+ ? ? ? schedule_work(&up->qos_work);
+#else
+ ? ? ? up->max_tx_count = 1;
+#endif
+}
+
+/**
+ * serial_omap_allow_cpu_low_power_state - remove power state restriction on MPU
+ * @up: struct uart_omap_port *
+ *
+ * Cancel the effects of serial_omap_block_cpu_low_power_state().
+ * This should allow the MPU powerdomain to enter a power state lower
+ * than ON, assuming the rest of the kernel is not restricting it.
+ * This works around a suspected silicon bug in the OMAP UART IP
+ * blocks. ?The UARTs should wake the PRCM when the transmit FIFO
+ * threshold interrupt is raised, but they do not. ?No return value.
+ */
+static void serial_omap_allow_cpu_low_power_state(struct uart_omap_port *up)
+{
+#ifdef CONFIG_CPU_IDLE
+ ? ? ? up->latency = up->calc_latency;
+ ? ? ? schedule_work(&up->qos_work);
+#else
+ ? ? ? up->max_tx_count = up->port.fifosize / 4;
+#endif
+}
+

?/*
?* serial_omap_get_divisor - calculate divisor value ?* @port: uart port info

drivers/tty/serial/omap-serial.c +7 -1
@@ -163,6 +206,9 @@ static void serial_omap_stop_tx(struct uart_port *port)
 ? ? ? ? ? ? ? ?serial_out(up, UART_IER, up->ier);
 ? ? ? ?}
 
+ ? ? ? if (!up->use_dma)
+ ? ? ? ? ? ? ? serial_omap_allow_cpu_low_power_state(up);
+
 ? ? ? ?pm_runtime_mark_last_busy(&up->pdev->dev);
 ? ? ? ?pm_runtime_put_autosuspend(&up->pdev->dev);
 ?}
@@ -264,7 +310,7 @@ static void transmit_chars(struct uart_omap_port *up)
 ? ? ? ? ? ? ? ?serial_omap_stop_tx(&up->port);
 ? ? ? ? ? ? ? ?return;
 ? ? ? ?}
- ? ? ? count = up->port.fifosize / 4;
+ ? ? ? count = up->max_tx_count;
 ? ? ? ?do {
 ? ? ? ? ? ? ? ?serial_out(up, UART_TX, xmit->buf[xmit->tail]);
 ? ? ? ? ? ? ? ?xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
@@ -297,6 +343,7 @@ static void serial_omap_start_tx(struct uart_port *port)
 
 ? ? ? ?if (!up->use_dma) {
 ? ? ? ? ? ? ? ?pm_runtime_get_sync(&up->pdev->dev);
+ ? ? ? ? ? ? ? serial_omap_block_cpu_low_power_state(up);
 ? ? ? ? ? ? ? ?serial_omap_enable_ier_thri(up);
 ? ? ? ? ? ? ? ?pm_runtime_mark_last_busy(&up->pdev->dev);
 ? ? ? ? ? ? ? ?pm_runtime_put_autosuspend(&up->pdev->dev);
@@ -1421,6 +1468,8 @@ static int serial_omap_probe(struct platform_device *pdev)
 ? ? ? ?up->port.fifosize = 64;
 ? ? ? ?up->port.ops = &serial_omap_pops;
 
+ ? ? ? up->max_tx_count = up->port.fifosize / 4;
+
 ? ? ? ?if (pdev->dev.of_node)
 ? ? ? ? ? ? ? ?up->port.line = of_alias_get_id(pdev->dev.of_node, "serial");

? ? ? ?else

--
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

#8 lore raw

cover tty: serial: OMAP: work around broken driver, IP block

In reply to Paul Walmsley
Quoted 7 lines · Paul Walmsley

[ This series is targeted for merging during v3.3-rc ]

On v3.3-rc1, the OMAP serial console doesn't behave properly when power management is enabled (the default with omap2plus_defconfig). This seems to be due to a combination of a silicon bug in the UART IP block and broken FIFO settings in the OMAP serial driver.

Quoted 5 lines

This patch series attempts to work around these problems. The workarounds are not perfect; see the patch descriptions for more details.

Tested on OMAP3530 BeagleBoard. This series will need to be tested on OMAP2 and OMAP4 before it is ready to merge.

Who is going to do that testing? When? Why isn't it done already?

I guess this means I can ignore them for now, right?

greg k-h

#9 lore raw

cover tty: serial: OMAP: work around broken driver, IP block

Quoted 8 lines · Greg KH
Quoted 7 lines · Paul Walmsley

[ This series is targeted for merging during v3.3-rc ]

On v3.3-rc1, the OMAP serial console doesn't behave properly when power management is enabled (the default with omap2plus_defconfig). This seems to be due to a combination of a silicon bug in the UART IP block and broken FIFO settings in the OMAP serial driver.

Quoted 7 lines
Quoted 5 lines

This patch series attempts to work around these problems. The workarounds are not perfect; see the patch descriptions for more details.

Tested on OMAP3530 BeagleBoard. This series will need to be tested on OMAP2 and OMAP4 before it is ready to merge.

Who is going to do that testing?

I will do it, although I do cling to the hope that others will help.

Quoted 1 line

When?

I'll do it within the next two or three days, unless others beat me to it.

Quoted 1 line

Why isn't it done already?

That's always the question about testing, isn't it?

In fact, now that you mention it, why haven't these patches been tested yet on every possible combination of OMAP SoC and console UART? Unbelievable, how negligent people are.

Quoted 1 line

I guess this means I can ignore them for now, right?

Yes. I'll follow up with a pull request (or revisions) after I've tested them on some OMAP2 and OMAP4 boards.

regards

- Paul

#10 lore raw

cover tty: serial: OMAP: work around broken driver, IP block

Quoted 1 line

I will do it, although I do cling to the hope that others will help.

I always work on the basis that if its had some coverage testing then the other folks who might have been affected should have reviewed the patch. They opted not to, within reason - their lookout.

They'll only test it later when it his the tree if they don't do it earlier 8)

Alan

#11 lore raw

cover tty: serial: OMAP: work around broken driver, IP block

In reply to Paul Walmsley
Quoted 7 lines · Paul Walmsley

[ This series is targeted for merging during v3.3-rc ]

On v3.3-rc1, the OMAP serial console doesn't behave properly when power management is enabled (the default with omap2plus_defconfig). This seems to be due to a combination of a silicon bug in the UART IP block and broken FIFO settings in the OMAP serial driver.

Quoted 5 lines

This patch series attempts to work around these problems. The workarounds are not perfect; see the patch descriptions for more details.

Tested on OMAP3530 BeagleBoard. This series will need to be tested on OMAP2 and OMAP4 before it is ready to merge.

Just did a quick test on an OMAP4430 ES2 Pandaboard and the (OMAP2420-based) N800. This series has no obvious effect on either N800 or OMAP4430 Pandaboard, aside from the expected increase in interrupts in the non-CPUIdle case. Probably this is due to the relative immaturity of the PM code on those two chip families.

- Paul

#12 lore raw

cover tty: serial: OMAP: work around broken driver, IP block

In reply to Paul Walmsley
Quoted 7 lines · Paul Walmsley

[ This series is targeted for merging during v3.3-rc ]

On v3.3-rc1, the OMAP serial console doesn't behave properly when power management is enabled (the default with omap2plus_defconfig). This seems to be due to a combination of a silicon bug in the UART IP block and broken FIFO settings in the OMAP serial driver.

Quoted 5 lines

This patch series attempts to work around these problems. The workarounds are not perfect; see the patch descriptions for more details.

Tested on OMAP3530 BeagleBoard. This series will need to be tested on OMAP2 and OMAP4 before it is ready to merge.

Thanks, this makes the console usable on my OMAP3 overo.

Tomi

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120123/8301390e/attachment.sig>