Hi,
Here's a couple of patches that should improve the behavior of the omap serial
driver at high data rates and baud rates.
I was actually surprised to see that proper dma support isn't there yet, but
there you go. Anyone got news on that, by the way?
Thanks for reviewing this,
Frans
Frans Klaver (3):
tty: omap-serial: prevent division by zero
tty: omap-serial: use threaded interrupt handler
tty: omap-serial: support setting of hardware flow control in dts
.../devicetree/bindings/serial/omap_serial.txt | 1 +
drivers/tty/serial/omap-serial.c | 52 ++++++++++++++++------
2 files changed, 40 insertions(+), 13 deletions(-)
--
1.9.3
Hi,
Apologies, something went wrong. Here's a resend.
Here's a couple of patches that should improve the behavior of the omap serial
driver at high data rates and baud rates.
I was actually surprised to see that proper dma support isn't there yet, but
there you go. Anyone got news on that, by the way?
Thanks for reviewing this,
Frans
Frans Klaver (3):
tty: omap-serial: prevent division by zero
tty: omap-serial: use threaded interrupt handler
tty: omap-serial: support setting of hardware flow control in dts
.../devicetree/bindings/serial/omap_serial.txt | 1 +
drivers/tty/serial/omap-serial.c | 52 ++++++++++++++++------
2 files changed, 40 insertions(+), 13 deletions(-)
--
1.9.3
If the chosen baud rate is large enough (e.g. 3.5 megabaud), the
calculated n13 and n16 values in serial_omap_baud_is_mode16 may become
0. This causes a division by zero when calculating the difference
between calculated and desired baud rates. To prevent this, cap n13 and
n16 on 1.
Signed-off-by: Frans Klaver <[email protected]>
---
drivers/tty/serial/omap-serial.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index d017cec..e454b7c 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -254,8 +254,16 @@ serial_omap_baud_is_mode16(struct uart_port *port, unsigned int baud)
{
unsigned int n13 = port->uartclk / (13 * baud);
unsigned int n16 = port->uartclk / (16 * baud);
- int baudAbsDiff13 = baud - (port->uartclk / (13 * n13));
- int baudAbsDiff16 = baud - (port->uartclk / (16 * n16));
+ int baudAbsDiff13;
+ int baudAbsDiff16;
+
+ if (n13 == 0)
+ n13 = 1;
+ if (n16 == 0)
+ n16 = 1;
+
+ baudAbsDiff13 = baud - (port->uartclk / (13 * n13));
+ baudAbsDiff16 = baud - (port->uartclk / (16 * n16));
if (baudAbsDiff13 < 0)
baudAbsDiff13 = -baudAbsDiff13;
if (baudAbsDiff16 < 0)
--
1.9.3
This makes hardware flow control availability configurable from the
device tree.
Signed-off-by: Frans Klaver <[email protected]>
---
Documentation/devicetree/bindings/serial/omap_serial.txt | 1 +
drivers/tty/serial/omap-serial.c | 3 +++
2 files changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/serial/omap_serial.txt b/Documentation/devicetree/bindings/serial/omap_serial.txt
index 342eedd..1b629e9 100644
--- a/Documentation/devicetree/bindings/serial/omap_serial.txt
+++ b/Documentation/devicetree/bindings/serial/omap_serial.txt
@@ -8,3 +8,4 @@ Required properties:
Optional properties:
- clock-frequency : frequency of the clock input to the UART
+- has-hw-flow-control : the hardware has flow control capability
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index ff2d931..87df0ee 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -1660,6 +1660,9 @@ static int serial_omap_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
wakeirq = irq_of_parse_and_map(pdev->dev.of_node, 1);
omap_up_info = of_get_uart_port_info(&pdev->dev);
+ if (of_property_read_bool(pdev->dev.of_node,
+ "has-hw-flow-control"))
+ omap_up_info->flags |= UPF_HARD_FLOW;
pdev->dev.platform_data = omap_up_info;
} else {
uartirq = platform_get_irq(pdev, 0);
--
1.9.3
At 3.6Mbaud, with slightly over 2Mbit/s data coming in, we see 1600 uart
rx buffer overflows within 30 seconds. Threading the interrupt handling reduces
this to about 170 overflows in 10 minutes.
In practice this therefore reduces the need for hardware flow control,
meaning the sending side doesn't have to buffer as much either.
Signed-off-by: Frans Klaver <[email protected]>
---
drivers/tty/serial/omap-serial.c | 37 ++++++++++++++++++++++++++-----------
1 file changed, 26 insertions(+), 11 deletions(-)
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index e454b7c..ff2d931 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -569,6 +569,21 @@ static void serial_omap_rdi(struct uart_omap_port *up, unsigned int lsr)
}
/**
+ * serial_omap_fast_irq() - schedule interrupt handling
+ */
+static irqreturn_t serial_omap_fast_irq(int irq, void *dev_id)
+{
+ struct uart_omap_port *up = dev_id;
+ unsigned int iir = serial_in(up, UART_IIR);
+ if (iir & UART_IIR_NO_INT) {
+ return IRQ_NONE;
+ }
+
+ disable_irq_nosync(up->port.irq);
+ return IRQ_WAKE_THREAD;
+}
+
+/**
* serial_omap_irq() - This handles the interrupt from one port
* @irq: uart port irq number
* @dev_id: uart port info
@@ -578,18 +593,12 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id)
struct uart_omap_port *up = dev_id;
unsigned int iir, lsr;
unsigned int type;
- irqreturn_t ret = IRQ_NONE;
int max_count = 256;
spin_lock(&up->port.lock);
pm_runtime_get_sync(up->dev);
do {
- iir = serial_in(up, UART_IIR);
- if (iir & UART_IIR_NO_INT)
- break;
-
- ret = IRQ_HANDLED;
lsr = serial_in(up, UART_LSR);
/* extract IRQ type from IIR register */
@@ -618,17 +627,21 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id)
default:
break;
}
+
+ iir = serial_in(up, UART_IIR);
} while (!(iir & UART_IIR_NO_INT) && max_count--);
spin_unlock(&up->port.lock);
tty_flip_buffer_push(&up->port.state->port);
+ enable_irq(up->port.irq);
+
pm_runtime_mark_last_busy(up->dev);
pm_runtime_put_autosuspend(up->dev);
up->port_activity = jiffies;
- return ret;
+ return IRQ_HANDLED;
}
static unsigned int serial_omap_tx_empty(struct uart_port *port)
@@ -725,15 +738,17 @@ static int serial_omap_startup(struct uart_port *port)
/*
* Allocate the IRQ
*/
- retval = request_irq(up->port.irq, serial_omap_irq, up->port.irqflags,
- up->name, up);
+ retval = request_threaded_irq(up->port.irq, serial_omap_fast_irq,
+ serial_omap_irq, up->port.irqflags,
+ up->name, up);
if (retval)
return retval;
/* Optional wake-up IRQ */
if (up->wakeirq) {
- retval = request_irq(up->wakeirq, serial_omap_irq,
- up->port.irqflags, up->name, up);
+ retval = request_threaded_irq(up->wakeirq, serial_omap_fast_irq,
+ serial_omap_irq, up->port.irqflags,
+ up->name, up);
if (retval) {
free_irq(up->port.irq, up);
return retval;
--
1.9.3