Skip to content

Commit d11053e

Browse files
fancergregkh
authored andcommitted
tty: max310x: Fix invalid baudrate divisors calculator
[ Upstream commit 35240ba ] 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 <fancer.lancer@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent d04ba4c commit d11053e

1 file changed

Lines changed: 31 additions & 20 deletions

File tree

drivers/tty/serial/max310x.c

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -486,37 +486,48 @@ static bool max310x_reg_precious(struct device *dev, unsigned int reg)
486486

487487
static int max310x_set_baud(struct uart_port *port, int baud)
488488
{
489-
unsigned int mode = 0, clk = port->uartclk, div = clk / baud;
489+
unsigned int mode = 0, div = 0, frac = 0, c = 0, F = 0;
490490

491-
/* Check for minimal value for divider */
492-
if (div < 16)
493-
div = 16;
494-
495-
if (clk % baud && (div / 16) < 0x8000) {
491+
/*
492+
* Calculate the integer divisor first. Select a proper mode
493+
* in case if the requested baud is too high for the pre-defined
494+
* clocks frequency.
495+
*/
496+
div = port->uartclk / baud;
497+
if (div < 8) {
498+
/* Mode x4 */
499+
c = 4;
500+
mode = MAX310X_BRGCFG_4XMODE_BIT;
501+
} else if (div < 16) {
496502
/* Mode x2 */
503+
c = 8;
497504
mode = MAX310X_BRGCFG_2XMODE_BIT;
498-
clk = port->uartclk * 2;
499-
div = clk / baud;
500-
501-
if (clk % baud && (div / 16) < 0x8000) {
502-
/* Mode x4 */
503-
mode = MAX310X_BRGCFG_4XMODE_BIT;
504-
clk = port->uartclk * 4;
505-
div = clk / baud;
506-
}
505+
} else {
506+
c = 16;
507507
}
508508

509-
max310x_port_write(port, MAX310X_BRGDIVMSB_REG, (div / 16) >> 8);
510-
max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div / 16);
511-
max310x_port_write(port, MAX310X_BRGCFG_REG, (div % 16) | mode);
509+
/* Calculate the divisor in accordance with the fraction coefficient */
510+
div /= c;
511+
F = c*baud;
512+
513+
/* Calculate the baud rate fraction */
514+
if (div > 0)
515+
frac = (16*(port->uartclk % F)) / F;
516+
else
517+
div = 1;
518+
519+
max310x_port_write(port, MAX310X_BRGDIVMSB_REG, div >> 8);
520+
max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div);
521+
max310x_port_write(port, MAX310X_BRGCFG_REG, frac | mode);
512522

513-
return DIV_ROUND_CLOSEST(clk, div);
523+
/* Return the actual baud rate we just programmed */
524+
return (16*port->uartclk) / (c*(16*div + frac));
514525
}
515526

516527
static int max310x_update_best_err(unsigned long f, long *besterr)
517528
{
518529
/* Use baudrate 115200 for calculate error */
519-
long err = f % (115200 * 16);
530+
long err = f % (460800 * 16);
520531

521532
if ((*besterr < 0) || (*besterr > err)) {
522533
*besterr = err;

0 commit comments

Comments
 (0)