diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/tty/serial/sh-sci.c | 69 |
1 files changed, 66 insertions, 3 deletions
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index fa3fd876105b..229162481fd6 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -79,6 +79,7 @@ enum { enum SCI_CLKS { SCI_FCK, /* Functional Clock */ + SCI_SCK, /* Optional External Clock */ SCI_NUM_CLKS }; @@ -1924,6 +1925,39 @@ static void sci_shutdown(struct uart_port *port) sci_free_irq(s); } +static int sci_sck_calc(struct sci_port *s, unsigned int bps, + unsigned int *srr) +{ + unsigned long freq = s->clk_rates[SCI_SCK]; + unsigned int min_sr, max_sr, sr; + int err, min_err = INT_MAX; + + if (s->sampling_rate) { + /* SCI(F) has a fixed sampling rate */ + min_sr = max_sr = s->sampling_rate / 2; + } else { + /* HSCIF has a variable 1/(8..32) sampling rate */ + min_sr = 8; + max_sr = 32; + } + + for (sr = max_sr; sr >= min_sr; sr--) { + err = DIV_ROUND_CLOSEST(freq, sr) - bps; + if (abs(err) >= abs(min_err)) + continue; + + min_err = err; + *srr = sr - 1; + + if (!err) + break; + } + + dev_dbg(s->port.dev, "SCK: %u%+d bps using SR %u\n", bps, min_err, + *srr + 1); + return min_err; +} + /* calculate sample rate, BRR, and clock select */ static int sci_scbrr_calc(struct sci_port *s, unsigned int bps, unsigned int *brr, unsigned int *srr, @@ -2019,7 +2053,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { unsigned int baud, smr_val = 0, scr_val = 0, i; - unsigned int brr = 255, cks = 0, srr = 15; + unsigned int brr = 255, cks = 0, srr = 15, sccks = 0; unsigned int brr1 = 255, cks1 = 0, srr1 = 15; struct sci_port *s = to_sci_port(port); const struct plat_sci_reg *reg; @@ -2062,10 +2096,26 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, * that gives us the smallest deviation from the desired baud rate. */ + /* Optional Undivided External Clock */ + if (s->clk_rates[SCI_SCK] && port->type != PORT_SCIFA && + port->type != PORT_SCIFB) { + err = sci_sck_calc(s, baud, &srr1); + if (abs(err) < abs(min_err)) { + best_clk = SCI_SCK; + scr_val = SCSCR_CKE1; + sccks = SCCKS_CKS; + min_err = err; + srr = srr1; + if (!err) + goto done; + } + } + /* Divided Functional Clock using standard Bit Rate Register */ err = sci_scbrr_calc(s, baud, &brr1, &srr1, &cks1); if (abs(err) < abs(min_err)) { best_clk = SCI_FCK; + scr_val = 0; min_err = err; brr = brr1; srr = srr1; @@ -2079,14 +2129,23 @@ done: sci_port_enable(s); + /* + * Program the optional External Baud Rate Generator (BRG) first. + * It controls the mux to select (H)SCK or frequency divided clock. + */ + if (best_clk >= 0 && sci_getreg(port, SCCKS)->size) + serial_port_out(port, SCCKS, sccks); + sci_reset(port); uart_update_timeout(port, termios->c_cflag, baud); if (best_clk >= 0) { smr_val |= cks; - dev_dbg(port->dev, "SMR 0x%x BRR %u SRR %u\n", smr_val, brr, - srr); + dev_dbg(port->dev, + "SCR 0x%x SMR 0x%x BRR %u CKS 0x%x SRR %u\n", + scr_val, smr_val, brr, sccks, srr); + serial_port_out(port, SCSCR, scr_val); serial_port_out(port, SCSMR, smr_val); serial_port_out(port, SCBRR, brr); if (sci_getreg(port, HSSRR)->size) @@ -2322,10 +2381,14 @@ static int sci_init_clocks(struct sci_port *sci_port, struct device *dev) { const char *clk_names[] = { [SCI_FCK] = "fck", + [SCI_SCK] = "sck", }; struct clk *clk; unsigned int i; + if (sci_port->cfg->type == PORT_HSCIF) + clk_names[SCI_SCK] = "hsck"; + for (i = 0; i < SCI_NUM_CLKS; i++) { clk = devm_clk_get(dev, clk_names[i]); if (PTR_ERR(clk) == -EPROBE_DEFER) |