Commit f6d199c7 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'tty-6.10-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty/serial fixes from Greg KH:
 "Here are some small TTY and Serial driver fixes that missed the
  6.9-final merge window, but have been in my tree for weeks (my fault,
  travel caused me to miss this)

  These fixes include:

   - more n_gsm fixes for reported problems

   - 8520_mtk driver fix

   - 8250_bcm7271 driver fix

   - sc16is7xx driver fix

  All of these have been in linux-next for weeks without any reported
  problems"

* tag 'tty-6.10-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
  serial: sc16is7xx: fix bug in sc16is7xx_set_baud() when using prescaler
  serial: 8250_bcm7271: use default_mux_rate if possible
  serial: 8520_mtk: Set RTS on shutdown for Rx in-band wakeup
  tty: n_gsm: fix missing receive state reset after mode switch
  tty: n_gsm: fix possible out-of-bounds in gsm0_receive()
parents b0a9ba13 8492bd91
...@@ -245,16 +245,18 @@ enum gsm_encoding { ...@@ -245,16 +245,18 @@ enum gsm_encoding {
enum gsm_mux_state { enum gsm_mux_state {
GSM_SEARCH, GSM_SEARCH,
GSM_START, GSM0_ADDRESS,
GSM_ADDRESS, GSM0_CONTROL,
GSM_CONTROL, GSM0_LEN0,
GSM_LEN, GSM0_LEN1,
GSM_DATA, GSM0_DATA,
GSM_FCS, GSM0_FCS,
GSM_OVERRUN, GSM0_SSOF,
GSM_LEN0, GSM1_START,
GSM_LEN1, GSM1_ADDRESS,
GSM_SSOF, GSM1_CONTROL,
GSM1_DATA,
GSM1_OVERRUN,
}; };
/* /*
...@@ -2847,6 +2849,30 @@ static void gsm_queue(struct gsm_mux *gsm) ...@@ -2847,6 +2849,30 @@ static void gsm_queue(struct gsm_mux *gsm)
return; return;
} }
/**
* gsm0_receive_state_check_and_fix - check and correct receive state
* @gsm: gsm data for this ldisc instance
*
* Ensures that the current receive state is valid for basic option mode.
*/
static void gsm0_receive_state_check_and_fix(struct gsm_mux *gsm)
{
switch (gsm->state) {
case GSM_SEARCH:
case GSM0_ADDRESS:
case GSM0_CONTROL:
case GSM0_LEN0:
case GSM0_LEN1:
case GSM0_DATA:
case GSM0_FCS:
case GSM0_SSOF:
break;
default:
gsm->state = GSM_SEARCH;
break;
}
}
/** /**
* gsm0_receive - perform processing for non-transparency * gsm0_receive - perform processing for non-transparency
...@@ -2860,26 +2886,27 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c) ...@@ -2860,26 +2886,27 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c)
{ {
unsigned int len; unsigned int len;
gsm0_receive_state_check_and_fix(gsm);
switch (gsm->state) { switch (gsm->state) {
case GSM_SEARCH: /* SOF marker */ case GSM_SEARCH: /* SOF marker */
if (c == GSM0_SOF) { if (c == GSM0_SOF) {
gsm->state = GSM_ADDRESS; gsm->state = GSM0_ADDRESS;
gsm->address = 0; gsm->address = 0;
gsm->len = 0; gsm->len = 0;
gsm->fcs = INIT_FCS; gsm->fcs = INIT_FCS;
} }
break; break;
case GSM_ADDRESS: /* Address EA */ case GSM0_ADDRESS: /* Address EA */
gsm->fcs = gsm_fcs_add(gsm->fcs, c); gsm->fcs = gsm_fcs_add(gsm->fcs, c);
if (gsm_read_ea(&gsm->address, c)) if (gsm_read_ea(&gsm->address, c))
gsm->state = GSM_CONTROL; gsm->state = GSM0_CONTROL;
break; break;
case GSM_CONTROL: /* Control Byte */ case GSM0_CONTROL: /* Control Byte */
gsm->fcs = gsm_fcs_add(gsm->fcs, c); gsm->fcs = gsm_fcs_add(gsm->fcs, c);
gsm->control = c; gsm->control = c;
gsm->state = GSM_LEN0; gsm->state = GSM0_LEN0;
break; break;
case GSM_LEN0: /* Length EA */ case GSM0_LEN0: /* Length EA */
gsm->fcs = gsm_fcs_add(gsm->fcs, c); gsm->fcs = gsm_fcs_add(gsm->fcs, c);
if (gsm_read_ea(&gsm->len, c)) { if (gsm_read_ea(&gsm->len, c)) {
if (gsm->len > gsm->mru) { if (gsm->len > gsm->mru) {
...@@ -2889,14 +2916,14 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c) ...@@ -2889,14 +2916,14 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c)
} }
gsm->count = 0; gsm->count = 0;
if (!gsm->len) if (!gsm->len)
gsm->state = GSM_FCS; gsm->state = GSM0_FCS;
else else
gsm->state = GSM_DATA; gsm->state = GSM0_DATA;
break; break;
} }
gsm->state = GSM_LEN1; gsm->state = GSM0_LEN1;
break; break;
case GSM_LEN1: case GSM0_LEN1:
gsm->fcs = gsm_fcs_add(gsm->fcs, c); gsm->fcs = gsm_fcs_add(gsm->fcs, c);
len = c; len = c;
gsm->len |= len << 7; gsm->len |= len << 7;
...@@ -2907,26 +2934,29 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c) ...@@ -2907,26 +2934,29 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c)
} }
gsm->count = 0; gsm->count = 0;
if (!gsm->len) if (!gsm->len)
gsm->state = GSM_FCS; gsm->state = GSM0_FCS;
else else
gsm->state = GSM_DATA; gsm->state = GSM0_DATA;
break; break;
case GSM_DATA: /* Data */ case GSM0_DATA: /* Data */
gsm->buf[gsm->count++] = c; gsm->buf[gsm->count++] = c;
if (gsm->count == gsm->len) { if (gsm->count >= MAX_MRU) {
gsm->bad_size++;
gsm->state = GSM_SEARCH;
} else if (gsm->count >= gsm->len) {
/* Calculate final FCS for UI frames over all data */ /* Calculate final FCS for UI frames over all data */
if ((gsm->control & ~PF) != UIH) { if ((gsm->control & ~PF) != UIH) {
gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf,
gsm->count); gsm->count);
} }
gsm->state = GSM_FCS; gsm->state = GSM0_FCS;
} }
break; break;
case GSM_FCS: /* FCS follows the packet */ case GSM0_FCS: /* FCS follows the packet */
gsm->fcs = gsm_fcs_add(gsm->fcs, c); gsm->fcs = gsm_fcs_add(gsm->fcs, c);
gsm->state = GSM_SSOF; gsm->state = GSM0_SSOF;
break; break;
case GSM_SSOF: case GSM0_SSOF:
gsm->state = GSM_SEARCH; gsm->state = GSM_SEARCH;
if (c == GSM0_SOF) if (c == GSM0_SOF)
gsm_queue(gsm); gsm_queue(gsm);
...@@ -2939,6 +2969,29 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c) ...@@ -2939,6 +2969,29 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c)
} }
} }
/**
* gsm1_receive_state_check_and_fix - check and correct receive state
* @gsm: gsm data for this ldisc instance
*
* Ensures that the current receive state is valid for advanced option mode.
*/
static void gsm1_receive_state_check_and_fix(struct gsm_mux *gsm)
{
switch (gsm->state) {
case GSM_SEARCH:
case GSM1_START:
case GSM1_ADDRESS:
case GSM1_CONTROL:
case GSM1_DATA:
case GSM1_OVERRUN:
break;
default:
gsm->state = GSM_SEARCH;
break;
}
}
/** /**
* gsm1_receive - perform processing for non-transparency * gsm1_receive - perform processing for non-transparency
* @gsm: gsm data for this ldisc instance * @gsm: gsm data for this ldisc instance
...@@ -2949,6 +3002,7 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c) ...@@ -2949,6 +3002,7 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c)
static void gsm1_receive(struct gsm_mux *gsm, u8 c) static void gsm1_receive(struct gsm_mux *gsm, u8 c)
{ {
gsm1_receive_state_check_and_fix(gsm);
/* handle XON/XOFF */ /* handle XON/XOFF */
if ((c & ISO_IEC_646_MASK) == XON) { if ((c & ISO_IEC_646_MASK) == XON) {
gsm->constipated = true; gsm->constipated = true;
...@@ -2961,11 +3015,11 @@ static void gsm1_receive(struct gsm_mux *gsm, u8 c) ...@@ -2961,11 +3015,11 @@ static void gsm1_receive(struct gsm_mux *gsm, u8 c)
} }
if (c == GSM1_SOF) { if (c == GSM1_SOF) {
/* EOF is only valid in frame if we have got to the data state */ /* EOF is only valid in frame if we have got to the data state */
if (gsm->state == GSM_DATA) { if (gsm->state == GSM1_DATA) {
if (gsm->count < 1) { if (gsm->count < 1) {
/* Missing FSC */ /* Missing FSC */
gsm->malformed++; gsm->malformed++;
gsm->state = GSM_START; gsm->state = GSM1_START;
return; return;
} }
/* Remove the FCS from data */ /* Remove the FCS from data */
...@@ -2981,14 +3035,14 @@ static void gsm1_receive(struct gsm_mux *gsm, u8 c) ...@@ -2981,14 +3035,14 @@ static void gsm1_receive(struct gsm_mux *gsm, u8 c)
gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]); gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]);
gsm->len = gsm->count; gsm->len = gsm->count;
gsm_queue(gsm); gsm_queue(gsm);
gsm->state = GSM_START; gsm->state = GSM1_START;
return; return;
} }
/* Any partial frame was a runt so go back to start */ /* Any partial frame was a runt so go back to start */
if (gsm->state != GSM_START) { if (gsm->state != GSM1_START) {
if (gsm->state != GSM_SEARCH) if (gsm->state != GSM_SEARCH)
gsm->malformed++; gsm->malformed++;
gsm->state = GSM_START; gsm->state = GSM1_START;
} }
/* A SOF in GSM_START means we are still reading idling or /* A SOF in GSM_START means we are still reading idling or
framing bytes */ framing bytes */
...@@ -3009,30 +3063,30 @@ static void gsm1_receive(struct gsm_mux *gsm, u8 c) ...@@ -3009,30 +3063,30 @@ static void gsm1_receive(struct gsm_mux *gsm, u8 c)
gsm->escape = false; gsm->escape = false;
} }
switch (gsm->state) { switch (gsm->state) {
case GSM_START: /* First byte after SOF */ case GSM1_START: /* First byte after SOF */
gsm->address = 0; gsm->address = 0;
gsm->state = GSM_ADDRESS; gsm->state = GSM1_ADDRESS;
gsm->fcs = INIT_FCS; gsm->fcs = INIT_FCS;
fallthrough; fallthrough;
case GSM_ADDRESS: /* Address continuation */ case GSM1_ADDRESS: /* Address continuation */
gsm->fcs = gsm_fcs_add(gsm->fcs, c); gsm->fcs = gsm_fcs_add(gsm->fcs, c);
if (gsm_read_ea(&gsm->address, c)) if (gsm_read_ea(&gsm->address, c))
gsm->state = GSM_CONTROL; gsm->state = GSM1_CONTROL;
break; break;
case GSM_CONTROL: /* Control Byte */ case GSM1_CONTROL: /* Control Byte */
gsm->fcs = gsm_fcs_add(gsm->fcs, c); gsm->fcs = gsm_fcs_add(gsm->fcs, c);
gsm->control = c; gsm->control = c;
gsm->count = 0; gsm->count = 0;
gsm->state = GSM_DATA; gsm->state = GSM1_DATA;
break; break;
case GSM_DATA: /* Data */ case GSM1_DATA: /* Data */
if (gsm->count > gsm->mru) { /* Allow one for the FCS */ if (gsm->count > gsm->mru || gsm->count > MAX_MRU) { /* Allow one for the FCS */
gsm->state = GSM_OVERRUN; gsm->state = GSM1_OVERRUN;
gsm->bad_size++; gsm->bad_size++;
} else } else
gsm->buf[gsm->count++] = c; gsm->buf[gsm->count++] = c;
break; break;
case GSM_OVERRUN: /* Over-long - eg a dropped SOF */ case GSM1_OVERRUN: /* Over-long - eg a dropped SOF */
break; break;
default: default:
pr_debug("%s: unhandled state: %d\n", __func__, gsm->state); pr_debug("%s: unhandled state: %d\n", __func__, gsm->state);
......
...@@ -673,18 +673,46 @@ static void init_real_clk_rates(struct device *dev, struct brcmuart_priv *priv) ...@@ -673,18 +673,46 @@ static void init_real_clk_rates(struct device *dev, struct brcmuart_priv *priv)
clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate); clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate);
} }
static u32 find_quot(struct device *dev, u32 freq, u32 baud, u32 *percent)
{
u32 quot;
u32 rate;
u64 hires_rate;
u64 hires_baud;
u64 hires_err;
rate = freq / 16;
quot = DIV_ROUND_CLOSEST(rate, baud);
if (!quot)
return 0;
/* increase resolution to get xx.xx percent */
hires_rate = div_u64((u64)rate * 10000, (u64)quot);
hires_baud = (u64)baud * 10000;
/* get the delta */
if (hires_rate > hires_baud)
hires_err = (hires_rate - hires_baud);
else
hires_err = (hires_baud - hires_rate);
*percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud);
dev_dbg(dev, "Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n",
baud, freq, *percent / 100, *percent % 100);
return quot;
}
static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
u32 baud) u32 baud)
{ {
u32 percent; u32 percent;
u32 best_percent = UINT_MAX; u32 best_percent = UINT_MAX;
u32 quot; u32 quot;
u32 freq;
u32 best_quot = 1; u32 best_quot = 1;
u32 rate; u32 best_freq = 0;
int best_index = -1;
u64 hires_rate;
u64 hires_baud;
u64 hires_err;
int rc; int rc;
int i; int i;
int real_baud; int real_baud;
...@@ -693,44 +721,35 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, ...@@ -693,44 +721,35 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
if (priv->baud_mux_clk == NULL) if (priv->baud_mux_clk == NULL)
return; return;
/* Find the closest match for specified baud */ /* Try default_mux_rate first */
quot = find_quot(up->dev, priv->default_mux_rate, baud, &percent);
if (quot) {
best_percent = percent;
best_freq = priv->default_mux_rate;
best_quot = quot;
}
/* If more than 1% error, find the closest match for specified baud */
if (best_percent > 100) {
for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) { for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) {
if (priv->real_rates[i] == 0) freq = priv->real_rates[i];
if (freq == 0 || freq == priv->default_mux_rate)
continue; continue;
rate = priv->real_rates[i] / 16; quot = find_quot(up->dev, freq, baud, &percent);
quot = DIV_ROUND_CLOSEST(rate, baud);
if (!quot) if (!quot)
continue; continue;
/* increase resolution to get xx.xx percent */
hires_rate = (u64)rate * 10000;
hires_baud = (u64)baud * 10000;
hires_err = div_u64(hires_rate, (u64)quot);
/* get the delta */
if (hires_err > hires_baud)
hires_err = (hires_err - hires_baud);
else
hires_err = (hires_baud - hires_err);
percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud);
dev_dbg(up->dev,
"Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n",
baud, priv->real_rates[i], percent / 100,
percent % 100);
if (percent < best_percent) { if (percent < best_percent) {
best_percent = percent; best_percent = percent;
best_index = i; best_freq = freq;
best_quot = quot; best_quot = quot;
} }
} }
if (best_index == -1) { }
if (!best_freq) {
dev_err(up->dev, "Error, %d BAUD rate is too fast.\n", baud); dev_err(up->dev, "Error, %d BAUD rate is too fast.\n", baud);
return; return;
} }
rate = priv->real_rates[best_index]; rc = clk_set_rate(priv->baud_mux_clk, best_freq);
rc = clk_set_rate(priv->baud_mux_clk, rate);
if (rc) if (rc)
dev_err(up->dev, "Error selecting BAUD MUX clock\n"); dev_err(up->dev, "Error selecting BAUD MUX clock\n");
...@@ -739,8 +758,8 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, ...@@ -739,8 +758,8 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
dev_err(up->dev, "Error, baud: %d has %u.%u%% error\n", dev_err(up->dev, "Error, baud: %d has %u.%u%% error\n",
baud, percent / 100, percent % 100); baud, percent / 100, percent % 100);
real_baud = rate / 16 / best_quot; real_baud = best_freq / 16 / best_quot;
dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", rate); dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", best_freq);
dev_dbg(up->dev, "Requested baud: %u, Actual baud: %u\n", dev_dbg(up->dev, "Requested baud: %u, Actual baud: %u\n",
baud, real_baud); baud, real_baud);
...@@ -749,7 +768,7 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, ...@@ -749,7 +768,7 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
i += (i / 2); i += (i / 2);
priv->char_wait = ns_to_ktime(i); priv->char_wait = ns_to_ktime(i);
up->uartclk = rate; up->uartclk = best_freq;
} }
static void brcmstb_set_termios(struct uart_port *up, static void brcmstb_set_termios(struct uart_port *up,
......
...@@ -209,15 +209,19 @@ static int mtk8250_startup(struct uart_port *port) ...@@ -209,15 +209,19 @@ static int mtk8250_startup(struct uart_port *port)
static void mtk8250_shutdown(struct uart_port *port) static void mtk8250_shutdown(struct uart_port *port)
{ {
#ifdef CONFIG_SERIAL_8250_DMA
struct uart_8250_port *up = up_to_u8250p(port); struct uart_8250_port *up = up_to_u8250p(port);
struct mtk8250_data *data = port->private_data; struct mtk8250_data *data = port->private_data;
int irq = data->rx_wakeup_irq;
#ifdef CONFIG_SERIAL_8250_DMA
if (up->dma) if (up->dma)
data->rx_status = DMA_RX_SHUTDOWN; data->rx_status = DMA_RX_SHUTDOWN;
#endif #endif
return serial8250_do_shutdown(port); serial8250_do_shutdown(port);
if (irq >= 0)
serial8250_do_set_mctrl(&up->port, TIOCM_RTS);
} }
static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask) static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask)
......
...@@ -555,16 +555,28 @@ static bool sc16is7xx_regmap_noinc(struct device *dev, unsigned int reg) ...@@ -555,16 +555,28 @@ static bool sc16is7xx_regmap_noinc(struct device *dev, unsigned int reg)
return reg == SC16IS7XX_RHR_REG; return reg == SC16IS7XX_RHR_REG;
} }
/*
* Configure programmable baud rate generator (divisor) according to the
* desired baud rate.
*
* From the datasheet, the divisor is computed according to:
*
* XTAL1 input frequency
* -----------------------
* prescaler
* divisor = ---------------------------
* baud-rate x sampling-rate
*/
static int sc16is7xx_set_baud(struct uart_port *port, int baud) static int sc16is7xx_set_baud(struct uart_port *port, int baud)
{ {
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
u8 lcr; u8 lcr;
u8 prescaler = 0; unsigned int prescaler = 1;
unsigned long clk = port->uartclk, div = clk / 16 / baud; unsigned long clk = port->uartclk, div = clk / 16 / baud;
if (div >= BIT(16)) { if (div >= BIT(16)) {
prescaler = SC16IS7XX_MCR_CLKSEL_BIT; prescaler = 4;
div /= 4; div /= prescaler;
} }
/* Enable enhanced features */ /* Enable enhanced features */
...@@ -574,9 +586,10 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud) ...@@ -574,9 +586,10 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
SC16IS7XX_EFR_ENABLE_BIT); SC16IS7XX_EFR_ENABLE_BIT);
sc16is7xx_efr_unlock(port); sc16is7xx_efr_unlock(port);
/* If bit MCR_CLKSEL is set, the divide by 4 prescaler is activated. */
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
SC16IS7XX_MCR_CLKSEL_BIT, SC16IS7XX_MCR_CLKSEL_BIT,
prescaler); prescaler == 1 ? 0 : SC16IS7XX_MCR_CLKSEL_BIT);
/* Backup LCR and access special register set (DLL/DLH) */ /* Backup LCR and access special register set (DLL/DLH) */
lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG); lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
...@@ -592,7 +605,7 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud) ...@@ -592,7 +605,7 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
/* Restore LCR and access to general register set */ /* Restore LCR and access to general register set */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
return DIV_ROUND_CLOSEST(clk / 16, div); return DIV_ROUND_CLOSEST((clk / prescaler) / 16, div);
} }
static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment