commit 5fe212364 causes division by zero with large bauds

From: Felipe Balbi
Date: Tue Sep 10 2013 - 15:10:00 EST


Hi Alexey,

your commit 5fe212364 causes division by zero with any baud rate larger
than 3 Mbaud (IP supports up to 3686400).

Maybe this patch is all we need to get it fixed ? (untested)

diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 816d1a2..b50327f 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -240,8 +240,14 @@ 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 = 0;
+ int baudAbsDiff16 = 0;
+
+ if (n13)
+ baudAbsDiff13 = baud - (port->uartclk / (13 * n13));
+ if (n16)
+ baudAbsDiff16 = baud - (port->uartclk / (16 * n16));
+
if(baudAbsDiff13 < 0)
baudAbsDiff13 = -baudAbsDiff13;
if(baudAbsDiff16 < 0)

Another possibility would be to convert this into a lookup table (also
untested):

diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 816d1a2..942d68e 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -224,6 +224,48 @@ static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
pdata->enable_wakeup(up->dev, enable);
}

+struct uart_omap_config {
+ unsigned int baud;
+ unsigned int oversampling;
+ unsigned int divisor;
+};
+
+static struct uart_omap_config omap_port_configs[] = {
+ { .baud = 300, .oversampling = 16, .divisor = 10000, },
+ { .baud = 300, .oversampling = 16, .divisor = 10000, },
+ { .baud = 600, .oversampling = 16, .divisor = 5000, },
+ { .baud = 1200, .oversampling = 16, .divisor = 2500, },
+ { .baud = 2400, .oversampling = 16, .divisor = 1250, },
+ { .baud = 4800, .oversampling = 16, .divisor = 625, },
+ { .baud = 9600, .oversampling = 16, .divisor = 312, },
+ { .baud = 14400, .oversampling = 16, .divisor = 208, },
+ { .baud = 19200, .oversampling = 16, .divisor = 156, },
+ { .baud = 28800, .oversampling = 16, .divisor = 704, },
+ { .baud = 38400, .oversampling = 16, .divisor = 78, },
+ { .baud = 57600, .oversampling = 16, .divisor = 52, },
+ { .baud = 115200, .oversampling = 16, .divisor = 26, },
+ { .baud = 230400, .oversampling = 16, .divisor = 13, },
+ { .baud = 460800, .oversampling = 13, .divisor = 8, },
+ { .baud = 921600, .oversampling = 13, .divisor = 4, },
+ { .baud = 1843200, .oversampling = 13, .divisor = 2, },
+ { .baud = 3000000, .oversampling = 16, .divisor = 1, },
+ { .baud = 3686400, .oversampling = 13, .divisor = 1, },
+ { } /* Terminating Entry */
+};
+
+static struct uart_omap_config *
+__serial_omap_get_config(struct uart_port *port, unsigned int baud)
+{
+ struct uart_omap_config *cfg = omap_port_configs;
+
+ while (cfg++) {
+ if (cfg->baud == baud)
+ return cfg;
+ }
+
+ return NULL;
+}
+
/*
* serial_omap_baud_is_mode16 - check if baud rate is MODE16X
* @port: uart port info
@@ -238,16 +280,12 @@ static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
static bool
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));
- if(baudAbsDiff13 < 0)
- baudAbsDiff13 = -baudAbsDiff13;
- if(baudAbsDiff16 < 0)
- baudAbsDiff16 = -baudAbsDiff16;
-
- return (baudAbsDiff13 > baudAbsDiff16);
+ struct uart_omap_config *cfg = __serial_omap_get_config(port, baud);
+
+ if (!cfg)
+ return -EINVAL;
+
+ return (cfg->oversampling == 16);
}

/*
@@ -258,13 +296,12 @@ serial_omap_baud_is_mode16(struct uart_port *port, unsigned int baud)
static unsigned int
serial_omap_get_divisor(struct uart_port *port, unsigned int baud)
{
- unsigned int divisor;
+ struct uart_omap_config *cfg = __serial_omap_get_config(port, baud);

- if (!serial_omap_baud_is_mode16(port, baud))
- divisor = 13;
- else
- divisor = 16;
- return port->uartclk/(baud * divisor);
+ if (!cfg)
+ return -EINVAL;
+
+ return cfg->divisor;
}

static void serial_omap_enable_ms(struct uart_port *port)

--
balbi

Attachment: signature.asc
Description: Digital signature