summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/8250/8250_dw.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/8250/8250_dw.c')
-rw-r--r--drivers/tty/serial/8250/8250_dw.c30
1 files changed, 18 insertions, 12 deletions
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 7e638997bfc2..10b0aca8ae19 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -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;