| From 9ad9305ff9d38b67ab53642596bd59555f92658e Mon Sep 17 00:00:00 2001 |
| From: Soren Brinkmann <soren.brinkmann@xilinx.com> |
| Date: Thu, 17 Oct 2013 14:08:10 -0700 |
| Subject: tty: xuartps: Updating set_baud_rate() |
| |
| The original algorithm to find the best baud rate dividers does not necessarily |
| find the best set of dividers. And in the worst case may even write illegal |
| values to the hardware. |
| The new function should make better use of the hardware capabilities and be able |
| to provide valid settings for a wider range of baud rates and also input clocks. |
| |
| Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| (cherry picked from commit e6b39bfd0db207d2e9f3f78468d18f529f3b7901) |
| Signed-off-by: Daniel Sangorrin <daniel.sangorrin@toshiba.co.jp> |
| Signed-off-by: Yoshitake Kobayashi <yoshitake.kobayashi@toshiba.co.jp> |
| --- |
| drivers/tty/serial/xilinx_uartps.c | 136 +++++++++++++++++++++++++------------ |
| 1 file changed, 93 insertions(+), 43 deletions(-) |
| |
| diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c |
| index cf5487841362..3f15e8048448 100644 |
| --- a/drivers/tty/serial/xilinx_uartps.c |
| +++ b/drivers/tty/serial/xilinx_uartps.c |
| @@ -156,6 +156,11 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); |
| #define XUARTPS_SR_TXFULL 0x00000010 /* TX FIFO full */ |
| #define XUARTPS_SR_RXTRIG 0x00000001 /* Rx Trigger */ |
| |
| +/* baud dividers min/max values */ |
| +#define XUARTPS_BDIV_MIN 4 |
| +#define XUARTPS_BDIV_MAX 255 |
| +#define XUARTPS_CD_MAX 65535 |
| + |
| /** |
| * struct xuartps - device data |
| * @refclk Reference clock |
| @@ -305,59 +310,94 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) |
| } |
| |
| /** |
| - * xuartps_set_baud_rate - Calculate and set the baud rate |
| - * @port: Handle to the uart port structure |
| - * @baud: Baud rate to set |
| - * |
| + * xuartps_calc_baud_divs - Calculate baud rate divisors |
| + * @clk: UART module input clock |
| + * @baud: Desired baud rate |
| + * @rbdiv: BDIV value (return value) |
| + * @rcd: CD value (return value) |
| + * @div8: Value for clk_sel bit in mod (return value) |
| * Returns baud rate, requested baud when possible, or actual baud when there |
| - * was too much error |
| - **/ |
| -static unsigned int xuartps_set_baud_rate(struct uart_port *port, |
| - unsigned int baud) |
| + * was too much error, zero if no valid divisors are found. |
| + * |
| + * Formula to obtain baud rate is |
| + * baud_tx/rx rate = clk/CD * (BDIV + 1) |
| + * input_clk = (Uart User Defined Clock or Apb Clock) |
| + * depends on UCLKEN in MR Reg |
| + * clk = input_clk or input_clk/8; |
| + * depends on CLKS in MR reg |
| + * CD and BDIV depends on values in |
| + * baud rate generate register |
| + * baud rate clock divisor register |
| + */ |
| +static unsigned int xuartps_calc_baud_divs(unsigned int clk, unsigned int baud, |
| + u32 *rbdiv, u32 *rcd, int *div8) |
| { |
| - unsigned int sel_clk; |
| - unsigned int calc_baud = 0; |
| - unsigned int brgr_val, brdiv_val; |
| + u32 cd, bdiv; |
| + unsigned int calc_baud; |
| + unsigned int bestbaud = 0; |
| unsigned int bauderror; |
| + unsigned int besterror = ~0; |
| |
| - /* Formula to obtain baud rate is |
| - * baud_tx/rx rate = sel_clk/CD * (BDIV + 1) |
| - * input_clk = (Uart User Defined Clock or Apb Clock) |
| - * depends on UCLKEN in MR Reg |
| - * sel_clk = input_clk or input_clk/8; |
| - * depends on CLKS in MR reg |
| - * CD and BDIV depends on values in |
| - * baud rate generate register |
| - * baud rate clock divisor register |
| - */ |
| - sel_clk = port->uartclk; |
| - if (xuartps_readl(XUARTPS_MR_OFFSET) & XUARTPS_MR_CLKSEL) |
| - sel_clk = sel_clk / 8; |
| - |
| - /* Find the best values for baud generation */ |
| - for (brdiv_val = 4; brdiv_val < 255; brdiv_val++) { |
| + if (baud < clk / ((XUARTPS_BDIV_MAX + 1) * XUARTPS_CD_MAX)) { |
| + *div8 = 1; |
| + clk /= 8; |
| + } else { |
| + *div8 = 0; |
| + } |
| |
| - brgr_val = sel_clk / (baud * (brdiv_val + 1)); |
| - if (brgr_val < 2 || brgr_val > 65535) |
| + for (bdiv = XUARTPS_BDIV_MIN; bdiv <= XUARTPS_BDIV_MAX; bdiv++) { |
| + cd = DIV_ROUND_CLOSEST(clk, baud * (bdiv + 1)); |
| + if (cd < 1 || cd > XUARTPS_CD_MAX) |
| continue; |
| |
| - calc_baud = sel_clk / (brgr_val * (brdiv_val + 1)); |
| + calc_baud = clk / (cd * (bdiv + 1)); |
| |
| if (baud > calc_baud) |
| bauderror = baud - calc_baud; |
| else |
| bauderror = calc_baud - baud; |
| |
| - /* use the values when percent error is acceptable */ |
| - if (((bauderror * 100) / baud) < 3) { |
| - calc_baud = baud; |
| - break; |
| + if (besterror > bauderror) { |
| + *rbdiv = bdiv; |
| + *rcd = cd; |
| + bestbaud = calc_baud; |
| + besterror = bauderror; |
| } |
| } |
| + /* use the values when percent error is acceptable */ |
| + if (((besterror * 100) / baud) < 3) |
| + bestbaud = baud; |
| + |
| + return bestbaud; |
| +} |
| |
| - /* Set the values for the new baud rate */ |
| - xuartps_writel(brgr_val, XUARTPS_BAUDGEN_OFFSET); |
| - xuartps_writel(brdiv_val, XUARTPS_BAUDDIV_OFFSET); |
| +/** |
| + * xuartps_set_baud_rate - Calculate and set the baud rate |
| + * @port: Handle to the uart port structure |
| + * @baud: Baud rate to set |
| + * Returns baud rate, requested baud when possible, or actual baud when there |
| + * was too much error, zero if no valid divisors are found. |
| + */ |
| +static unsigned int xuartps_set_baud_rate(struct uart_port *port, |
| + unsigned int baud) |
| +{ |
| + unsigned int calc_baud; |
| + u32 cd, bdiv; |
| + u32 mreg; |
| + int div8; |
| + |
| + calc_baud = xuartps_calc_baud_divs(port->uartclk, baud, &bdiv, &cd, |
| + &div8); |
| + |
| + /* Write new divisors to hardware */ |
| + mreg = xuartps_readl(XUARTPS_MR_OFFSET); |
| + if (div8) |
| + mreg |= XUARTPS_MR_CLKSEL; |
| + else |
| + mreg &= ~XUARTPS_MR_CLKSEL; |
| + xuartps_writel(mreg, XUARTPS_MR_OFFSET); |
| + xuartps_writel(cd, XUARTPS_BAUDGEN_OFFSET); |
| + xuartps_writel(bdiv, XUARTPS_BAUDDIV_OFFSET); |
| |
| return calc_baud; |
| } |
| @@ -495,7 +535,7 @@ static void xuartps_set_termios(struct uart_port *port, |
| struct ktermios *termios, struct ktermios *old) |
| { |
| unsigned int cval = 0; |
| - unsigned int baud; |
| + unsigned int baud, minbaud, maxbaud; |
| unsigned long flags; |
| unsigned int ctrl_reg, mode_reg; |
| |
| @@ -512,8 +552,14 @@ static void xuartps_set_termios(struct uart_port *port, |
| (XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS), |
| XUARTPS_CR_OFFSET); |
| |
| - /* Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk */ |
| - baud = uart_get_baud_rate(port, termios, old, 0, 10000000); |
| + /* |
| + * Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk |
| + * min and max baud should be calculated here based on port->uartclk. |
| + * this way we get a valid baud and can safely call set_baud() |
| + */ |
| + minbaud = port->uartclk / ((XUARTPS_BDIV_MAX + 1) * XUARTPS_CD_MAX * 8); |
| + maxbaud = port->uartclk / (XUARTPS_BDIV_MIN + 1); |
| + baud = uart_get_baud_rate(port, termios, old, minbaud, maxbaud); |
| baud = xuartps_set_baud_rate(port, baud); |
| if (tty_termios_baud_rate(termios)) |
| tty_termios_encode_baud_rate(termios, baud, baud); |
| @@ -589,13 +635,17 @@ static void xuartps_set_termios(struct uart_port *port, |
| cval |= XUARTPS_MR_PARITY_MARK; |
| else |
| cval |= XUARTPS_MR_PARITY_SPACE; |
| - } else if (termios->c_cflag & PARODD) |
| + } else { |
| + if (termios->c_cflag & PARODD) |
| cval |= XUARTPS_MR_PARITY_ODD; |
| else |
| cval |= XUARTPS_MR_PARITY_EVEN; |
| - } else |
| + } |
| + } else { |
| cval |= XUARTPS_MR_PARITY_NONE; |
| - xuartps_writel(cval , XUARTPS_MR_OFFSET); |
| + } |
| + cval |= mode_reg & 1; |
| + xuartps_writel(cval, XUARTPS_MR_OFFSET); |
| |
| spin_unlock_irqrestore(&port->lock, flags); |
| } |
| -- |
| 1.8.5.rc3 |
| |