|
@@ -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)
|