mirror of https://gitee.com/openkylin/linux.git
serial: 8250_dw: Improve clock rate setting
Currently dw8250_set_termios sets the input clock to the nearest achievable rate to baudx16. If necessary, the input clock is then divided down to baudx16 using an integer divider within the UART device, with the divisor calculated in the 8250 core driver. However, the clock rate set by dw8250_set_termios and subsequently divided down could be considerably different to the target baudx16 rate, resulting in incorrect operation. This patch fixes this by iteratively searching for an input clock rate that is within +/-1.6% of an integer multiple of the target baudx16 rate. Signed-off-by: Ed Blake <ed.blake@sondrel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
6263368c5b
commit
de9e33bdfa
|
@ -256,25 +256,31 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
|
|||
struct ktermios *old)
|
||||
{
|
||||
unsigned int baud = tty_termios_baud_rate(termios);
|
||||
unsigned int target_rate, min_rate, max_rate;
|
||||
struct dw8250_data *d = p->private_data;
|
||||
long rate;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
if (IS_ERR(d->clk) || !old)
|
||||
goto out;
|
||||
|
||||
clk_disable_unprepare(d->clk);
|
||||
rate = clk_round_rate(d->clk, baud * 16);
|
||||
if (rate < 0)
|
||||
ret = rate;
|
||||
else if (rate == 0)
|
||||
ret = -ENOENT;
|
||||
else
|
||||
ret = clk_set_rate(d->clk, rate);
|
||||
clk_prepare_enable(d->clk);
|
||||
/* Find a clk rate within +/-1.6% of an integer multiple of baudx16 */
|
||||
target_rate = baud * 16;
|
||||
min_rate = target_rate - (target_rate >> 6);
|
||||
max_rate = target_rate + (target_rate >> 6);
|
||||
|
||||
if (!ret)
|
||||
p->uartclk = rate;
|
||||
for (i = 1; i <= UART_DIV_MAX; i++) {
|
||||
rate = clk_round_rate(d->clk, i * target_rate);
|
||||
if (rate >= i * min_rate && rate <= i * max_rate)
|
||||
break;
|
||||
}
|
||||
if (i <= UART_DIV_MAX) {
|
||||
clk_disable_unprepare(d->clk);
|
||||
ret = clk_set_rate(d->clk, rate);
|
||||
clk_prepare_enable(d->clk);
|
||||
if (!ret)
|
||||
p->uartclk = rate;
|
||||
}
|
||||
|
||||
out:
|
||||
p->status &= ~UPSTAT_AUTOCTS;
|
||||
|
|
Loading…
Reference in New Issue