Received: by 2002:a25:ad19:0:0:0:0:0 with SMTP id y25csp3120072ybi; Thu, 18 Jul 2019 21:25:02 -0700 (PDT) X-Google-Smtp-Source: APXvYqxYFgdJ6gMYP5VzjUX4oFpuCEdreYcu3B1N6w8pZSVSmbWZn4yotwUeF0OrjGHIpw+7Jmvn X-Received: by 2002:a63:4a51:: with SMTP id j17mr51643526pgl.284.1563510302622; Thu, 18 Jul 2019 21:25:02 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1563510302; cv=none; d=google.com; s=arc-20160816; b=dVDqBGF1OMnGyMUrANza33uyd9d2bG1MaVJxMBVb1gvCxDbndG1kZJkOlbJ7JjLRkN LJEwVocxfVz/NxV6U9WA1PKm+mgCJN6w9FRcnYkhAl/BI3tVV4VvH8OL/SXsIGAQ9Ag0 c41TAX45tQEO37o6mo5xU6YgnBXpjjzJHYD95FADTFVoy+VY3CG9tur4DS7IdGOX4lHP FuUXqJLKc86HsUfK5PCYwljQRFHG9bZBMWSVZ0sGsCzpcmpQKQeWB92E6Ffi85/maur9 fbznF7PsOwaMTrf1dNLP324uuZC0EWQUFxrJGf6vA+EXEsKMOKGqjdGVGkfWy7brDdeQ tdrw== 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=Ac/78wjaByS/i+8RohMKtm+ciO5misOgA4IYbgKockw=; b=kerydGD/3zSQOhduPYtFZHoiOsfXnsOI5YwVkEzi2wKANcwmfIvvgHmO0gBWQ/V0ff nfzSKW6w0BHvE7singBHS3nRiGBMrNNIOyG6RoOGrLv7tD5Ch1O/1uVE2+0WSBC6d9Hy GUnO592nHWOV/yASqsGZQVFe19kelclSfiGHyNh/wBpuuidKytW25CCUAb4aM97Mb9qy ubFDZ4taD3DLMpQdComP8DhYH4eusVV7/auVIsTmpkg1UVqI0lGGCWRgreR6wh74/jZ0 gBHNFd1LvYHnONGkD7sxaDKLAJCmWbuSgRS00KQaaI3VGHyM0EScikF8JUB0s12jSPkY Z7Hw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=eI+E86OF; 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 f15si879524plr.260.2019.07.18.21.24.47; Thu, 18 Jul 2019 21:25:02 -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=eI+E86OF; 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 S2389946AbfGSEWh (ORCPT + 99 others); Fri, 19 Jul 2019 00:22:37 -0400 Received: from mail.kernel.org ([198.145.29.99]:46148 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388369AbfGSELR (ORCPT ); Fri, 19 Jul 2019 00:11:17 -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 22269218BB; Fri, 19 Jul 2019 04:11:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1563509475; bh=Mfltp2IS68y5mOk8uikVBsc6BNBCZQ2z6NJnRWs6dyU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eI+E86OFBoUUDIEonKGIY+JpkPqZ4bftNENUT9Rl5qlbkiEVDzjIOpJfwaJKN+556 Fkcmh/ZQn2U1jsKiAk/nV10iwRKX/G2ueNbVl8hwP6dmkcHMwmW2zLtq/L+ScugqKv pazlBkfksEwmEwPdn2xJkO89MTiFgirQBXSVj+BI= 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 4.14 03/60] tty: max310x: Fix invalid baudrate divisors calculator Date: Fri, 19 Jul 2019 00:10:12 -0400 Message-Id: <20190719041109.18262-3-sashal@kernel.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190719041109.18262-1-sashal@kernel.org> References: <20190719041109.18262-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 1a98b6631e90..0969a0d97b2b 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -494,37 +494,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