TTY/Serial fixes for 6.10-rc1
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. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZlBGKQ8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ylOkwCfUOa00YQt3jJwBEC9bQUprW1z95MAoKW00V5g UJgQ7+1d+o4bT/ib5xpj =/O0m -----END PGP SIGNATURE----- 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()
This commit is contained in:
commit
f6d199c774
@ -245,16 +245,18 @@ enum gsm_encoding {
|
||||
|
||||
enum gsm_mux_state {
|
||||
GSM_SEARCH,
|
||||
GSM_START,
|
||||
GSM_ADDRESS,
|
||||
GSM_CONTROL,
|
||||
GSM_LEN,
|
||||
GSM_DATA,
|
||||
GSM_FCS,
|
||||
GSM_OVERRUN,
|
||||
GSM_LEN0,
|
||||
GSM_LEN1,
|
||||
GSM_SSOF,
|
||||
GSM0_ADDRESS,
|
||||
GSM0_CONTROL,
|
||||
GSM0_LEN0,
|
||||
GSM0_LEN1,
|
||||
GSM0_DATA,
|
||||
GSM0_FCS,
|
||||
GSM0_SSOF,
|
||||
GSM1_START,
|
||||
GSM1_ADDRESS,
|
||||
GSM1_CONTROL,
|
||||
GSM1_DATA,
|
||||
GSM1_OVERRUN,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -2847,6 +2849,30 @@ invalid:
|
||||
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
|
||||
@ -2860,26 +2886,27 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c)
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
gsm0_receive_state_check_and_fix(gsm);
|
||||
switch (gsm->state) {
|
||||
case GSM_SEARCH: /* SOF marker */
|
||||
if (c == GSM0_SOF) {
|
||||
gsm->state = GSM_ADDRESS;
|
||||
gsm->state = GSM0_ADDRESS;
|
||||
gsm->address = 0;
|
||||
gsm->len = 0;
|
||||
gsm->fcs = INIT_FCS;
|
||||
}
|
||||
break;
|
||||
case GSM_ADDRESS: /* Address EA */
|
||||
case GSM0_ADDRESS: /* Address EA */
|
||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||
if (gsm_read_ea(&gsm->address, c))
|
||||
gsm->state = GSM_CONTROL;
|
||||
gsm->state = GSM0_CONTROL;
|
||||
break;
|
||||
case GSM_CONTROL: /* Control Byte */
|
||||
case GSM0_CONTROL: /* Control Byte */
|
||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||
gsm->control = c;
|
||||
gsm->state = GSM_LEN0;
|
||||
gsm->state = GSM0_LEN0;
|
||||
break;
|
||||
case GSM_LEN0: /* Length EA */
|
||||
case GSM0_LEN0: /* Length EA */
|
||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||
if (gsm_read_ea(&gsm->len, c)) {
|
||||
if (gsm->len > gsm->mru) {
|
||||
@ -2889,14 +2916,14 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c)
|
||||
}
|
||||
gsm->count = 0;
|
||||
if (!gsm->len)
|
||||
gsm->state = GSM_FCS;
|
||||
gsm->state = GSM0_FCS;
|
||||
else
|
||||
gsm->state = GSM_DATA;
|
||||
gsm->state = GSM0_DATA;
|
||||
break;
|
||||
}
|
||||
gsm->state = GSM_LEN1;
|
||||
gsm->state = GSM0_LEN1;
|
||||
break;
|
||||
case GSM_LEN1:
|
||||
case GSM0_LEN1:
|
||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||
len = c;
|
||||
gsm->len |= len << 7;
|
||||
@ -2907,26 +2934,29 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c)
|
||||
}
|
||||
gsm->count = 0;
|
||||
if (!gsm->len)
|
||||
gsm->state = GSM_FCS;
|
||||
gsm->state = GSM0_FCS;
|
||||
else
|
||||
gsm->state = GSM_DATA;
|
||||
gsm->state = GSM0_DATA;
|
||||
break;
|
||||
case GSM_DATA: /* Data */
|
||||
case GSM0_DATA: /* Data */
|
||||
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 */
|
||||
if ((gsm->control & ~PF) != UIH) {
|
||||
gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf,
|
||||
gsm->count);
|
||||
}
|
||||
gsm->state = GSM_FCS;
|
||||
gsm->state = GSM0_FCS;
|
||||
}
|
||||
break;
|
||||
case GSM_FCS: /* FCS follows the packet */
|
||||
case GSM0_FCS: /* FCS follows the packet */
|
||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||
gsm->state = GSM_SSOF;
|
||||
gsm->state = GSM0_SSOF;
|
||||
break;
|
||||
case GSM_SSOF:
|
||||
case GSM0_SSOF:
|
||||
gsm->state = GSM_SEARCH;
|
||||
if (c == GSM0_SOF)
|
||||
gsm_queue(gsm);
|
||||
@ -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
|
||||
* @gsm: gsm data for this ldisc instance
|
||||
@ -2949,6 +3002,7 @@ static void gsm0_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 */
|
||||
if ((c & ISO_IEC_646_MASK) == XON) {
|
||||
gsm->constipated = true;
|
||||
@ -2961,11 +3015,11 @@ static void gsm1_receive(struct gsm_mux *gsm, u8 c)
|
||||
}
|
||||
if (c == GSM1_SOF) {
|
||||
/* 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) {
|
||||
/* Missing FSC */
|
||||
gsm->malformed++;
|
||||
gsm->state = GSM_START;
|
||||
gsm->state = GSM1_START;
|
||||
return;
|
||||
}
|
||||
/* Remove the FCS from data */
|
||||
@ -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->len = gsm->count;
|
||||
gsm_queue(gsm);
|
||||
gsm->state = GSM_START;
|
||||
gsm->state = GSM1_START;
|
||||
return;
|
||||
}
|
||||
/* 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)
|
||||
gsm->malformed++;
|
||||
gsm->state = GSM_START;
|
||||
gsm->state = GSM1_START;
|
||||
}
|
||||
/* A SOF in GSM_START means we are still reading idling or
|
||||
framing bytes */
|
||||
@ -3009,30 +3063,30 @@ static void gsm1_receive(struct gsm_mux *gsm, u8 c)
|
||||
gsm->escape = false;
|
||||
}
|
||||
switch (gsm->state) {
|
||||
case GSM_START: /* First byte after SOF */
|
||||
case GSM1_START: /* First byte after SOF */
|
||||
gsm->address = 0;
|
||||
gsm->state = GSM_ADDRESS;
|
||||
gsm->state = GSM1_ADDRESS;
|
||||
gsm->fcs = INIT_FCS;
|
||||
fallthrough;
|
||||
case GSM_ADDRESS: /* Address continuation */
|
||||
case GSM1_ADDRESS: /* Address continuation */
|
||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||
if (gsm_read_ea(&gsm->address, c))
|
||||
gsm->state = GSM_CONTROL;
|
||||
gsm->state = GSM1_CONTROL;
|
||||
break;
|
||||
case GSM_CONTROL: /* Control Byte */
|
||||
case GSM1_CONTROL: /* Control Byte */
|
||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||
gsm->control = c;
|
||||
gsm->count = 0;
|
||||
gsm->state = GSM_DATA;
|
||||
gsm->state = GSM1_DATA;
|
||||
break;
|
||||
case GSM_DATA: /* Data */
|
||||
if (gsm->count > gsm->mru) { /* Allow one for the FCS */
|
||||
gsm->state = GSM_OVERRUN;
|
||||
case GSM1_DATA: /* Data */
|
||||
if (gsm->count > gsm->mru || gsm->count > MAX_MRU) { /* Allow one for the FCS */
|
||||
gsm->state = GSM1_OVERRUN;
|
||||
gsm->bad_size++;
|
||||
} else
|
||||
gsm->buf[gsm->count++] = c;
|
||||
break;
|
||||
case GSM_OVERRUN: /* Over-long - eg a dropped SOF */
|
||||
case GSM1_OVERRUN: /* Over-long - eg a dropped SOF */
|
||||
break;
|
||||
default:
|
||||
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)
|
||||
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,
|
||||
u32 baud)
|
||||
{
|
||||
u32 percent;
|
||||
u32 best_percent = UINT_MAX;
|
||||
u32 quot;
|
||||
u32 freq;
|
||||
u32 best_quot = 1;
|
||||
u32 rate;
|
||||
int best_index = -1;
|
||||
u64 hires_rate;
|
||||
u64 hires_baud;
|
||||
u64 hires_err;
|
||||
u32 best_freq = 0;
|
||||
int rc;
|
||||
int i;
|
||||
int real_baud;
|
||||
@ -693,44 +721,35 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
|
||||
if (priv->baud_mux_clk == NULL)
|
||||
return;
|
||||
|
||||
/* Find the closest match for specified baud */
|
||||
for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) {
|
||||
if (priv->real_rates[i] == 0)
|
||||
continue;
|
||||
rate = priv->real_rates[i] / 16;
|
||||
quot = DIV_ROUND_CLOSEST(rate, baud);
|
||||
if (!quot)
|
||||
continue;
|
||||
/* 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++) {
|
||||
freq = priv->real_rates[i];
|
||||
if (freq == 0 || freq == priv->default_mux_rate)
|
||||
continue;
|
||||
quot = find_quot(up->dev, freq, baud, &percent);
|
||||
if (!quot)
|
||||
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) {
|
||||
best_percent = percent;
|
||||
best_index = i;
|
||||
best_quot = quot;
|
||||
if (percent < best_percent) {
|
||||
best_percent = percent;
|
||||
best_freq = freq;
|
||||
best_quot = quot;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (best_index == -1) {
|
||||
if (!best_freq) {
|
||||
dev_err(up->dev, "Error, %d BAUD rate is too fast.\n", baud);
|
||||
return;
|
||||
}
|
||||
rate = priv->real_rates[best_index];
|
||||
rc = clk_set_rate(priv->baud_mux_clk, rate);
|
||||
rc = clk_set_rate(priv->baud_mux_clk, best_freq);
|
||||
if (rc)
|
||||
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,
|
||||
dev_err(up->dev, "Error, baud: %d has %u.%u%% error\n",
|
||||
baud, percent / 100, percent % 100);
|
||||
|
||||
real_baud = rate / 16 / best_quot;
|
||||
dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", rate);
|
||||
real_baud = best_freq / 16 / best_quot;
|
||||
dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", best_freq);
|
||||
dev_dbg(up->dev, "Requested baud: %u, Actual baud: %u\n",
|
||||
baud, real_baud);
|
||||
|
||||
@ -749,7 +768,7 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
|
||||
i += (i / 2);
|
||||
priv->char_wait = ns_to_ktime(i);
|
||||
|
||||
up->uartclk = rate;
|
||||
up->uartclk = best_freq;
|
||||
}
|
||||
|
||||
static void brcmstb_set_termios(struct uart_port *up,
|
||||
|
@ -209,15 +209,19 @@ static int mtk8250_startup(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 mtk8250_data *data = port->private_data;
|
||||
int irq = data->rx_wakeup_irq;
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
if (up->dma)
|
||||
data->rx_status = DMA_RX_SHUTDOWN;
|
||||
#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)
|
||||
|
@ -555,16 +555,28 @@ static bool sc16is7xx_regmap_noinc(struct device *dev, unsigned int 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)
|
||||
{
|
||||
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
|
||||
u8 lcr;
|
||||
u8 prescaler = 0;
|
||||
unsigned int prescaler = 1;
|
||||
unsigned long clk = port->uartclk, div = clk / 16 / baud;
|
||||
|
||||
if (div >= BIT(16)) {
|
||||
prescaler = SC16IS7XX_MCR_CLKSEL_BIT;
|
||||
div /= 4;
|
||||
prescaler = 4;
|
||||
div /= prescaler;
|
||||
}
|
||||
|
||||
/* Enable enhanced features */
|
||||
@ -574,9 +586,10 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
|
||||
SC16IS7XX_EFR_ENABLE_BIT);
|
||||
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_MCR_CLKSEL_BIT,
|
||||
prescaler);
|
||||
prescaler == 1 ? 0 : SC16IS7XX_MCR_CLKSEL_BIT);
|
||||
|
||||
/* Backup LCR and access special register set (DLL/DLH) */
|
||||
lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
|
||||
@ -592,7 +605,7 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
|
||||
/* Restore LCR and access to general register set */
|
||||
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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user