Skip to content

Commit 68a0db1

Browse files
Allen Yangregkh
authored andcommitted
serial: mvebu-uart: add function to change baudrate
Until now, the first UART port baudrate was set by the bootloader. Add a function allowing to change the baudrate. Changes may be done from userspace but also at probe time by the kernel. Use the simplest method: baudrate divisor. Works for all UART ports until 230400 baud. To achieve higher baudrates, software should implement the fractional divisor feature that allows more accuracy for higher rates. Signed-off-by: Allen Yan <[email protected]> [<[email protected]>: changed termios handling] Signed-off-by: Miquel Raynal <[email protected]> Reviewed-by: Gregory CLEMENT <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 9c3d3ee commit 68a0db1

File tree

1 file changed

+65
-4
lines changed

1 file changed

+65
-4
lines changed

drivers/tty/serial/mvebu-uart.c

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
| STAT_PAR_ERR | STAT_OVR_ERR)
7373

7474
#define UART_BRDV 0x10
75+
#define BRDV_BAUD_MASK 0x3FF
7576

7677
#define MVEBU_NR_UARTS 1
7778

@@ -344,6 +345,31 @@ static void mvebu_uart_shutdown(struct uart_port *port)
344345
free_irq(port->irq, port);
345346
}
346347

348+
static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud)
349+
{
350+
struct mvebu_uart *mvuart = to_mvuart(port);
351+
unsigned int baud_rate_div;
352+
u32 brdv;
353+
354+
if (IS_ERR(mvuart->clk))
355+
return -PTR_ERR(mvuart->clk);
356+
357+
/*
358+
* The UART clock is divided by the value of the divisor to generate
359+
* UCLK_OUT clock, which is 16 times faster than the baudrate.
360+
* This prescaler can achieve all standard baudrates until 230400.
361+
* Higher baudrates could be achieved for the extended UART by using the
362+
* programmable oversampling stack (also called fractional divisor).
363+
*/
364+
baud_rate_div = DIV_ROUND_UP(port->uartclk, baud * 16);
365+
brdv = readl(port->membase + UART_BRDV);
366+
brdv &= ~BRDV_BAUD_MASK;
367+
brdv |= baud_rate_div;
368+
writel(brdv, port->membase + UART_BRDV);
369+
370+
return 0;
371+
}
372+
347373
static void mvebu_uart_set_termios(struct uart_port *port,
348374
struct ktermios *termios,
349375
struct ktermios *old)
@@ -367,11 +393,30 @@ static void mvebu_uart_set_termios(struct uart_port *port,
367393
if ((termios->c_cflag & CREAD) == 0)
368394
port->ignore_status_mask |= STAT_RX_RDY(port) | STAT_BRK_ERR;
369395

370-
if (old)
371-
tty_termios_copy_hw(termios, old);
396+
/*
397+
* Maximum achievable frequency with simple baudrate divisor is 230400.
398+
* Since the error per bit frame would be of more than 15%, achieving
399+
* higher frequencies would require to implement the fractional divisor
400+
* feature.
401+
*/
402+
baud = uart_get_baud_rate(port, termios, old, 0, 230400);
403+
if (mvebu_uart_baud_rate_set(port, baud)) {
404+
/* No clock available, baudrate cannot be changed */
405+
if (old)
406+
baud = uart_get_baud_rate(port, old, NULL, 0, 230400);
407+
} else {
408+
tty_termios_encode_baud_rate(termios, baud, baud);
409+
uart_update_timeout(port, termios->c_cflag, baud);
410+
}
372411

373-
baud = uart_get_baud_rate(port, termios, old, 0, 460800);
374-
uart_update_timeout(port, termios->c_cflag, baud);
412+
/* Only the following flag changes are supported */
413+
if (old) {
414+
termios->c_iflag &= INPCK | IGNPAR;
415+
termios->c_iflag |= old->c_iflag & ~(INPCK | IGNPAR);
416+
termios->c_cflag &= CREAD | CBAUD;
417+
termios->c_cflag |= old->c_cflag & ~(CREAD | CBAUD);
418+
termios->c_lflag = old->c_lflag;
419+
}
375420

376421
spin_unlock_irqrestore(&port->lock, flags);
377422
}
@@ -654,12 +699,28 @@ static int mvebu_uart_probe(struct platform_device *pdev)
654699
if (!mvuart)
655700
return -ENOMEM;
656701

702+
/* Get controller data depending on the compatible string */
657703
mvuart->data = (struct mvebu_uart_driver_data *)match->data;
658704
mvuart->port = port;
659705

660706
port->private_data = mvuart;
661707
platform_set_drvdata(pdev, mvuart);
662708

709+
/* Get fixed clock frequency */
710+
mvuart->clk = devm_clk_get(&pdev->dev, NULL);
711+
if (IS_ERR(mvuart->clk)) {
712+
if (PTR_ERR(mvuart->clk) == -EPROBE_DEFER)
713+
return PTR_ERR(mvuart->clk);
714+
715+
if (IS_EXTENDED(port)) {
716+
dev_err(&pdev->dev, "unable to get UART clock\n");
717+
return PTR_ERR(mvuart->clk);
718+
}
719+
} else {
720+
if (!clk_prepare_enable(mvuart->clk))
721+
port->uartclk = clk_get_rate(mvuart->clk);
722+
}
723+
663724
/* UART Soft Reset*/
664725
writel(CTRL_SOFT_RST, port->membase + UART_CTRL(port));
665726
udelay(1);

0 commit comments

Comments
 (0)