aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/mvebu-uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/mvebu-uart.c')
-rw-r--r--drivers/tty/serial/mvebu-uart.c32
1 files changed, 23 insertions, 9 deletions
diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c
index 170e446a2f62..7e7b1559fa36 100644
--- a/drivers/tty/serial/mvebu-uart.c
+++ b/drivers/tty/serial/mvebu-uart.c
@@ -72,6 +72,8 @@
#define BRDV_BAUD_MASK 0x3FF
#define UART_OSAMP 0x14
+#define OSAMP_DEFAULT_DIVISOR 16
+#define OSAMP_DIVISORS_MASK 0x3F3F3F3F
#define MVEBU_NR_UARTS 2
@@ -444,25 +446,34 @@ static void mvebu_uart_shutdown(struct uart_port *port)
static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud)
{
struct mvebu_uart *mvuart = to_mvuart(port);
- unsigned int baud_rate_div;
- u32 brdv;
+ unsigned int d_divisor, m_divisor;
+ u32 brdv, osamp;
if (IS_ERR(mvuart->clk))
return -PTR_ERR(mvuart->clk);
/*
- * The UART clock is divided by the value of the divisor to generate
- * UCLK_OUT clock, which is 16 times faster than the baudrate.
- * This prescaler can achieve all standard baudrates until 230400.
- * Higher baudrates could be achieved for the extended UART by using the
- * programmable oversampling stack (also called fractional divisor).
+ * The baudrate is derived from the UART clock thanks to two divisors:
+ * > D ("baud generator"): can divide the clock from 2 to 2^10 - 1.
+ * > M ("fractional divisor"): allows a better accuracy for
+ * baudrates higher than 230400.
+ *
+ * As the derivation of M is rather complicated, the code sticks to its
+ * default value (x16) when all the prescalers are zeroed, and only
+ * makes use of D to configure the desired baudrate.
*/
- baud_rate_div = DIV_ROUND_UP(port->uartclk, baud * 16);
+ m_divisor = OSAMP_DEFAULT_DIVISOR;
+ d_divisor = DIV_ROUND_UP(port->uartclk, baud * m_divisor);
+
brdv = readl(port->membase + UART_BRDV);
brdv &= ~BRDV_BAUD_MASK;
- brdv |= baud_rate_div;
+ brdv |= d_divisor;
writel(brdv, port->membase + UART_BRDV);
+ osamp = readl(port->membase + UART_OSAMP);
+ osamp &= ~OSAMP_DIVISORS_MASK;
+ writel(osamp, port->membase + UART_OSAMP);
+
return 0;
}
@@ -799,6 +810,9 @@ static int mvebu_uart_probe(struct platform_device *pdev)
return -EINVAL;
}
+ if (!match)
+ return -ENODEV;
+
/* Assume that all UART ports have a DT alias or none has */
id = of_alias_get_id(pdev->dev.of_node, "serial");
if (!pdev->dev.of_node || id < 0)