Received: by 2002:a25:ad19:0:0:0:0:0 with SMTP id y25csp3134581ybi; Thu, 18 Jul 2019 21:42:27 -0700 (PDT) X-Google-Smtp-Source: APXvYqw6Z3Cj/PjfqQGJsEasWJ0JZ2u2Y7vw78uRJ2MGTpN3zTvMbeotLMGfCgnfFNEKJMA/rcxA X-Received: by 2002:a63:c70d:: with SMTP id n13mr51153541pgg.171.1563511347798; Thu, 18 Jul 2019 21:42:27 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1563511347; cv=none; d=google.com; s=arc-20160816; b=dmTeMY9CIIwCdw9v/kt183Ot0UqzEKEQxana+K/XlC6ilNkFcZ+TCenVl6Ej6yCtwF bGnINopFpH8bod3bl2jXT5GZ3AsJqBCkhT5MMhyTqRaY2Y39txdIAmDRx3jbiD7foDKk d/BN3NFuvwH7nPawGoIgHc+q/zFKeBhsFrOIGckdsJzoHerjDWXktr7WYEBZMnLbUftJ rRmjCz0rjt8z1iOPNQ3evJewB5e2xXVNBBOxEn8HKsjyLckz8QpKe+e589RuyCaO4dKR jdrgTL49e40YaXayWPL2plSoHf7Zs5AVNPutPy45jgKTMK33nJWHHtms9s3LO9qkh0eW 4RaA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=wjRi01IEUGQTzYxMJiocNYTu2L7srzPKmmgB/XImRW8=; b=VzPcX1FyQPtpJFvTOkPNE+rO4nsw2Rl+tPlL60GKrD45V8YYlPG0kYwvFIoRQU7wm3 9y6g4imvATDb7NVL1bOZeSjaVzywXs6x4tVC2J1ZmHGNf7/gS2j7TwJ7Pl8graaOc/C/ ykqoWD3hSqZdiY3w9pXyLDSP4mv+a1PART5zs0uaGNOpUuHwYhl50zaReVWwsCH3O9pa Fi8PDSQfgIv/mbLgWvpVOPHGvHHNbX10cTFxQKYrn/H3isFzC2Ldx0EylZ9JjuyR2rHa DMlMlCliqwgDg1saLYT52+zmZI1S2VnuJ78sxdZRPwR+HbIIXvq3klWeKNVkBTC+AWwg tBnA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=VqtBlrec; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id ba9si1041179plb.308.2019.07.18.21.42.12; Thu, 18 Jul 2019 21:42:27 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=VqtBlrec; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728567AbfGSElB (ORCPT + 99 others); Fri, 19 Jul 2019 00:41:01 -0400 Received: from mail.kernel.org ([198.145.29.99]:56464 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726941AbfGSD5K (ORCPT ); Thu, 18 Jul 2019 23:57:10 -0400 Received: from sasha-vm.mshome.net (c-73-47-72-35.hsd1.nh.comcast.net [73.47.72.35]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id F148021855; Fri, 19 Jul 2019 03:57:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1563508629; bh=ddgz5FpkFtKJjV40+Op1NTp30XAoAWKB4qb3QXdny4Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VqtBlrecgi3VgGRRXlmEwUkYnx21/ccVTfQ1sMmuzcGy9u63DjhIhc1HqxnKRsQNU 9AFUTIeY1hWWYnGU7Wr0EQ+Y9a1bBERRyV7uJdZZrN8EjICDeuL14qnh4hX1txVu/A jFD8FgW+VHOYZQ/G0Td83J1hJVK6UnGQ/SWdiAJc= From: Sasha Levin To: linux-kernel@vger.kernel.org, stable@vger.kernel.org Cc: Serge Semin , Greg Kroah-Hartman , Sasha Levin , linux-serial@vger.kernel.org Subject: [PATCH AUTOSEL 5.2 011/171] tty: max310x: Fix invalid baudrate divisors calculator Date: Thu, 18 Jul 2019 23:54:02 -0400 Message-Id: <20190719035643.14300-11-sashal@kernel.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190719035643.14300-1-sashal@kernel.org> References: <20190719035643.14300-1-sashal@kernel.org> MIME-Version: 1.0 X-stable: review X-Patchwork-Hint: Ignore Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Serge Semin [ Upstream commit 35240ba26a932b279a513f66fa4cabfd7af55221 ] Current calculator doesn't do it' job quite correct. First of all the max310x baud-rates generator supports the divisor being less than 16. In this case the x2/x4 modes can be used to double or quadruple the reference frequency. But the current baud-rate setter function just filters all these modes out by the first condition and setups these modes only if there is a clocks-baud division remainder. The former doesn't seem right at all, since enabling the x2/x4 modes causes the line noise tolerance reduction and should be only used as a last resort to enable a requested too high baud-rate. Finally the fraction is supposed to be calculated from D = Fref/(c*baud) formulae, but not from D % 16, which causes the precision loss. So to speak the current baud-rate calculator code works well only if the baud perfectly fits to the uart reference input frequency. Lets fix the calculator by implementing the algo fully compliant with the fractional baud-rate generator described in the datasheet: D = Fref / (c*baud), where c={16,8,4} is the x1/x2/x4 rate mode respectively, Fref - reference input frequency. The divisor fraction is calculated from the same formulae, but making sure it is found with a resolution of 0.0625 (four bits). Signed-off-by: Serge Semin Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/tty/serial/max310x.c | 51 ++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index e5aebbf5f302..c3afd128b8fc 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -496,37 +496,48 @@ static bool max310x_reg_precious(struct device *dev, unsigned int reg) static int max310x_set_baud(struct uart_port *port, int baud) { - unsigned int mode = 0, clk = port->uartclk, div = clk / baud; + unsigned int mode = 0, div = 0, frac = 0, c = 0, F = 0; - /* Check for minimal value for divider */ - if (div < 16) - div = 16; - - if (clk % baud && (div / 16) < 0x8000) { + /* + * Calculate the integer divisor first. Select a proper mode + * in case if the requested baud is too high for the pre-defined + * clocks frequency. + */ + div = port->uartclk / baud; + if (div < 8) { + /* Mode x4 */ + c = 4; + mode = MAX310X_BRGCFG_4XMODE_BIT; + } else if (div < 16) { /* Mode x2 */ + c = 8; mode = MAX310X_BRGCFG_2XMODE_BIT; - clk = port->uartclk * 2; - div = clk / baud; - - if (clk % baud && (div / 16) < 0x8000) { - /* Mode x4 */ - mode = MAX310X_BRGCFG_4XMODE_BIT; - clk = port->uartclk * 4; - div = clk / baud; - } + } else { + c = 16; } - max310x_port_write(port, MAX310X_BRGDIVMSB_REG, (div / 16) >> 8); - max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div / 16); - max310x_port_write(port, MAX310X_BRGCFG_REG, (div % 16) | mode); + /* Calculate the divisor in accordance with the fraction coefficient */ + div /= c; + F = c*baud; + + /* Calculate the baud rate fraction */ + if (div > 0) + frac = (16*(port->uartclk % F)) / F; + else + div = 1; + + max310x_port_write(port, MAX310X_BRGDIVMSB_REG, div >> 8); + max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div); + max310x_port_write(port, MAX310X_BRGCFG_REG, frac | mode); - return DIV_ROUND_CLOSEST(clk, div); + /* Return the actual baud rate we just programmed */ + return (16*port->uartclk) / (c*(16*div + frac)); } static int max310x_update_best_err(unsigned long f, long *besterr) { /* Use baudrate 115200 for calculate error */ - long err = f % (115200 * 16); + long err = f % (460800 * 16); if ((*besterr < 0) || (*besterr > err)) { *besterr = err; -- 2.20.1