From patchwork Fri Dec 13 08:21:37 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Sangorrin X-Patchwork-Id: 3339041 Return-Path: X-Original-To: patchwork-ltsi-dev@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 6303C9F44E for ; Fri, 13 Dec 2013 08:47:02 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 2818220762 for ; Fri, 13 Dec 2013 08:47:01 +0000 (UTC) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) by mail.kernel.org (Postfix) with ESMTP id 46CB720766 for ; Fri, 13 Dec 2013 08:46:59 +0000 (UTC) Received: from mail.linux-foundation.org (localhost [IPv6:::1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 8545D71B; Fri, 13 Dec 2013 08:46:58 +0000 (UTC) X-Original-To: ltsi-dev@lists.linuxfoundation.org Delivered-To: ltsi-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTP id 2EF1A273 for ; Fri, 13 Dec 2013 08:46:57 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from imx9.toshiba.co.jp (imx9.toshiba.co.jp [202.33.96.51]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id 611F21F889 for ; Fri, 13 Dec 2013 08:46:56 +0000 (UTC) Received: from imx12.toshiba.co.jp (imx12 [61.202.160.132]) by imx9.toshiba.co.jp with ESMTP id rBD8NWHH025258 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Fri, 13 Dec 2013 17:23:33 +0900 (JST) Received: from arc11.toshiba.co.jp ([133.199.90.127]) by imx12.toshiba.co.jp with ESMTP id rBD8NJ2f017843; Fri, 13 Dec 2013 17:23:19 +0900 (JST) Received: (from root@localhost) by arc11.toshiba.co.jp id rBD8NJV5023522; Fri, 13 Dec 2013 17:23:19 +0900 (JST) Received: from ovp11.toshiba.co.jp [133.199.90.148] by arc11.toshiba.co.jp with ESMTP id TAA23509; Fri, 13 Dec 2013 17:23:19 +0900 Received: from mx.toshiba.co.jp (localhost [127.0.0.1]) by ovp11.toshiba.co.jp with ESMTP id rBD8NIUp010444; Fri, 13 Dec 2013 17:23:18 +0900 (JST) Received: from BK2211.rdc.toshiba.co.jp by toshiba.co.jp id rBD8NIrO007530; Fri, 13 Dec 2013 17:23:18 +0900 (JST) Received: from island.swc.toshiba.co.jp (localhost [127.0.0.1]) by BK2211.rdc.toshiba.co.jp (8.13.8+Sun/8.13.8) with ESMTP id rBD8NHFb027195; Fri, 13 Dec 2013 17:23:18 +0900 (JST) Received: from ubuntu.swc.toshiba.co.jp (unknown [133.196.174.184]) by island.swc.toshiba.co.jp (Postfix) with ESMTP id BAEA540010; Fri, 13 Dec 2013 17:22:31 +0900 (JST) From: Daniel Sangorrin To: ltsi-dev@lists.linuxfoundation.org Date: Fri, 13 Dec 2013 17:21:37 +0900 Message-Id: <1386922983-22135-23-git-send-email-daniel.sangorrin@toshiba.co.jp> X-Mailer: git-send-email 1.8.5 In-Reply-To: <1386922983-22135-1-git-send-email-daniel.sangorrin@toshiba.co.jp> References: <1386922983-22135-1-git-send-email-daniel.sangorrin@toshiba.co.jp> X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org Cc: michal.simek@xilinx.com Subject: [LTSI-dev] [PATCH 022/108] tty: xuartps: Updating set_baud_rate() X-BeenThere: ltsi-dev@lists.linuxfoundation.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: "A list to discuss patches, development, and other things related to the LTSI project" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ltsi-dev-bounces@lists.linuxfoundation.org Errors-To: ltsi-dev-bounces@lists.linuxfoundation.org X-Virus-Scanned: ClamAV using ClamSMTP From: Soren Brinkmann 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 Signed-off-by: Greg Kroah-Hartman (cherry picked from commit e6b39bfd0db207d2e9f3f78468d18f529f3b7901) Signed-off-by: Daniel Sangorrin Signed-off-by: Yoshitake Kobayashi --- 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 cf54878..3f15e80 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); }