Commit f6d199c7 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
Loading
Loading
Loading
Loading
+97 −43
Original line number Diff line number Diff line
@@ -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 @@ static void gsm_queue(struct gsm_mux *gsm)
	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);
+60 −41
Original line number Diff line number Diff line
@@ -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 */
	/* 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++) {
		if (priv->real_rates[i] == 0)
			freq = priv->real_rates[i];
			if (freq == 0 || freq == priv->default_mux_rate)
				continue;
		rate = priv->real_rates[i] / 16;
		quot = DIV_ROUND_CLOSEST(rate, baud);
			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_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,
+6 −2
Original line number Diff line number Diff line
@@ -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)
+18 −5
Original line number Diff line number Diff line
@@ -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,