From 525667c0259d6072016436b189ece597b32c1648 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 30 Apr 2019 21:45:59 +0300 Subject: serial: 8250_of: Use of_device_get_match_data() Use of_device_get_match_data() to simplify the code a bit. Signed-off-by: Andy Shevchenko Tested-by: John Garry Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_of.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 0277479c87e9..1308b3d1e17a 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -205,18 +205,16 @@ err_pmruntime: /* * Try to register a serial port */ -static const struct of_device_id of_platform_serial_table[]; static int of_platform_serial_probe(struct platform_device *ofdev) { - const struct of_device_id *match; struct of_serial_info *info; struct uart_8250_port port8250; + unsigned int port_type; u32 tx_threshold; - int port_type; int ret; - match = of_match_device(of_platform_serial_table, &ofdev->dev); - if (!match) + port_type = (unsigned long)of_device_get_match_data(&ofdev->dev); + if (port_type == PORT_UNKNOWN) return -EINVAL; if (of_property_read_bool(ofdev->dev.of_node, "used-by-rtas")) @@ -226,7 +224,6 @@ static int of_platform_serial_probe(struct platform_device *ofdev) if (info == NULL) return -ENOMEM; - port_type = (unsigned long)match->data; memset(&port8250, 0, sizeof(port8250)); ret = of_platform_serial_setup(ofdev, port_type, &port8250.port, info); if (ret) -- cgit v1.2.3-55-g7522 From dfaffb9dca90638f5e2e80f430283b7ecaeb1bb0 Mon Sep 17 00:00:00 2001 From: Hao Lee Date: Tue, 30 Apr 2019 00:24:02 +0800 Subject: tty: serial: 8250: Fix type field in format string The dev_dbg statement should print the value of uart.port.mapbase instead of its address. Besides that, uart.port.irq and uart.port.iotype are all unsigned types, so using %u is more appropriate. Signed-off-by: Hao Lee Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pnp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 431e69a5a6a0..dfca33141fcc 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -462,8 +462,8 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) return -ENODEV; dev_dbg(&dev->dev, - "Setup PNP port: port %lx, mem %pa, irq %d, type %d\n", - uart.port.iobase, &uart.port.mapbase, + "Setup PNP port: port %#lx, mem %#llx, irq %u, type %u\n", + uart.port.iobase, (unsigned long long)uart.port.mapbase, uart.port.irq, uart.port.iotype); if (flags & CIR_PORT) { -- cgit v1.2.3-55-g7522 From 9f20e8843edf35a0e4869f9e52b0494cfb1c91fc Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Thu, 9 May 2019 14:11:58 -0700 Subject: tty: amba-pl011: allow shared interrupt The PL011 register space includes all necessary status bits to determine whether a device instance requires handling in response to an interrupt. Therefore, multiple instances of the device could be serviced by a single shared interrupt, which is the case on BCM7211. Signed-off-by: Doug Berger Signed-off-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 89ade213a1a9..5921a33b2a07 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1717,7 +1717,7 @@ static int pl011_allocate_irq(struct uart_amba_port *uap) { pl011_write(uap->im, uap, REG_IMSC); - return request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap); + return request_irq(uap->port.irq, pl011_int, IRQF_SHARED, "uart-pl011", uap); } /* -- cgit v1.2.3-55-g7522 From 2987590b7846bc55530d02fcb568ce338a1e5bdd Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Tue, 14 May 2019 13:14:09 +0300 Subject: tty: max310x: Simplify tx-work item code Since cmwq introduction in the kernel, workqueues've been turned into non-reentrant execution contexts [1]. It means any work item is guaranteed to be executed by at most one worker system-wide at any given time. Since tx-handler max310x_handle_tx() is called by a single work item we don't need it to be self-protected by the mutex. We also don't need to check the tx work item pending state before scheduling it (which in the first place was racy btw), since cmwq will make sure to reschedule the item if it wasn't pending at the moment of schedule_work() call. [1] Documentation/core-api/workqueue.rst Signed-off-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 450ba6d7996c..4ee805862b68 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -263,7 +263,6 @@ struct max310x_one { struct max310x_port { struct max310x_devtype *devtype; struct regmap *regmap; - struct mutex mutex; struct clk *clk; #ifdef CONFIG_GPIOLIB struct gpio_chip gpio; @@ -768,8 +767,7 @@ static void max310x_start_tx(struct uart_port *port) { struct max310x_one *one = container_of(port, struct max310x_one, port); - if (!work_pending(&one->tx_work)) - schedule_work(&one->tx_work); + schedule_work(&one->tx_work); } static irqreturn_t max310x_port_irq(struct max310x_port *s, int portno) @@ -826,14 +824,11 @@ static irqreturn_t max310x_ist(int irq, void *dev_id) return IRQ_RETVAL(handled); } -static void max310x_wq_proc(struct work_struct *ws) +static void max310x_tx_proc(struct work_struct *ws) { struct max310x_one *one = container_of(ws, struct max310x_one, tx_work); - struct max310x_port *s = dev_get_drvdata(one->port.dev); - mutex_lock(&s->mutex); max310x_handle_tx(&one->port); - mutex_unlock(&s->mutex); } static unsigned int max310x_tx_empty(struct uart_port *port) @@ -1269,8 +1264,6 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, uartclk = max310x_set_ref_clk(dev, s, freq, xtal); dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk); - mutex_init(&s->mutex); - for (i = 0; i < devtype->nr; i++) { unsigned int line; @@ -1298,7 +1291,7 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, /* Clear IRQ status register */ max310x_port_read(&s->p[i].port, MAX310X_IRQSTS_REG); /* Initialize queue for start TX */ - INIT_WORK(&s->p[i].tx_work, max310x_wq_proc); + INIT_WORK(&s->p[i].tx_work, max310x_tx_proc); /* Initialize queue for changing LOOPBACK mode */ INIT_WORK(&s->p[i].md_work, max310x_md_proc); /* Initialize queue for changing RS485 mode */ @@ -1350,8 +1343,6 @@ out_uart: } } - mutex_destroy(&s->mutex); - out_clk: clk_disable_unprepare(s->clk); @@ -1372,7 +1363,6 @@ static int max310x_remove(struct device *dev) s->devtype->power(&s->p[i].port, 0); } - mutex_destroy(&s->mutex); clk_disable_unprepare(s->clk); return 0; -- cgit v1.2.3-55-g7522 From 1b5d239af3dad5b2572c9dab2e70f6735410e18f Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Tue, 14 May 2019 13:14:10 +0300 Subject: tty: max310x: Introduce max310x_one port macro-wrapper uart_port structure instance is embedded into the max310x_one super-structure, which is accessed by some of the uart-port callback methods. In order to improve the callback's code readability lets define the to_max310x_port() wrapper which just translates the passed uart_port pointer to the max310x_one one. It is also going to be handy in future commits. Signed-off-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 4ee805862b68..527f1476c24a 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -259,6 +259,8 @@ struct max310x_one { struct work_struct md_work; struct work_struct rs_work; }; +#define to_max310x_port(_port) \ + container_of(_port, struct max310x_one, port) struct max310x_port { struct max310x_devtype *devtype; @@ -765,7 +767,7 @@ static void max310x_handle_tx(struct uart_port *port) static void max310x_start_tx(struct uart_port *port) { - struct max310x_one *one = container_of(port, struct max310x_one, port); + struct max310x_one *one = to_max310x_port(port); schedule_work(&one->tx_work); } @@ -858,7 +860,7 @@ static void max310x_md_proc(struct work_struct *ws) static void max310x_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct max310x_one *one = container_of(port, struct max310x_one, port); + struct max310x_one *one = to_max310x_port(port); schedule_work(&one->md_work); } @@ -981,7 +983,7 @@ static void max310x_rs_proc(struct work_struct *ws) static int max310x_rs485_config(struct uart_port *port, struct serial_rs485 *rs485) { - struct max310x_one *one = container_of(port, struct max310x_one, port); + struct max310x_one *one = to_max310x_port(port); if ((rs485->delay_rts_before_send > 0x0f) || (rs485->delay_rts_after_send > 0x0f)) -- cgit v1.2.3-55-g7522 From b7382c73b2d7102154cb077dde9aa9370b776874 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Tue, 14 May 2019 13:14:11 +0300 Subject: tty: max310x: Don't pass stacked buffers to SPI SPI transfer tx/rx buffers must be DMA-safe and the structure documentation clearly states this. Data declared on the system stack isn't DMA-safe [1]. Instead at least kernel memory should be used for the buffers. In order to fix this here we can create the buffers at the device probing stage and use them without any synchronization, since batch read/write methods are called from non-reentrant contexts - either from rx-event IRQ threaded handler or from the tx workqueue item. [1] Documentation/DMA-API-HOWTO.txt Signed-off-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 527f1476c24a..0e3dc89c459b 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -258,6 +258,10 @@ struct max310x_one { struct work_struct tx_work; struct work_struct md_work; struct work_struct rs_work; + + u8 wr_header; + u8 rd_header; + u8 rx_buf[MAX310X_FIFO_SIZE]; }; #define to_max310x_port(_port) \ container_of(_port, struct max310x_one, port) @@ -608,11 +612,11 @@ static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s, static void max310x_batch_write(struct uart_port *port, u8 *txbuf, unsigned int len) { - u8 header[] = { (port->iobase + MAX310X_THR_REG) | MAX310X_WRITE_BIT }; + struct max310x_one *one = to_max310x_port(port); struct spi_transfer xfer[] = { { - .tx_buf = &header, - .len = sizeof(header), + .tx_buf = &one->wr_header, + .len = sizeof(one->wr_header), }, { .tx_buf = txbuf, .len = len, @@ -623,11 +627,11 @@ static void max310x_batch_write(struct uart_port *port, u8 *txbuf, unsigned int static void max310x_batch_read(struct uart_port *port, u8 *rxbuf, unsigned int len) { - u8 header[] = { port->iobase + MAX310X_RHR_REG }; + struct max310x_one *one = to_max310x_port(port); struct spi_transfer xfer[] = { { - .tx_buf = &header, - .len = sizeof(header), + .tx_buf = &one->rd_header, + .len = sizeof(one->rd_header), }, { .rx_buf = rxbuf, .len = len, @@ -638,8 +642,8 @@ static void max310x_batch_read(struct uart_port *port, u8 *rxbuf, unsigned int l static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) { + struct max310x_one *one = to_max310x_port(port); unsigned int sts, ch, flag, i; - u8 buf[MAX310X_FIFO_SIZE]; if (port->read_status_mask == MAX310X_LSR_RXOVR_BIT) { /* We are just reading, happily ignoring any error conditions. @@ -654,7 +658,7 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) * */ sts = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG); - max310x_batch_read(port, buf, rxlen); + max310x_batch_read(port, one->rx_buf, rxlen); port->icount.rx += rxlen; flag = TTY_NORMAL; @@ -666,7 +670,8 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) } for (i = 0; i < rxlen; ++i) { - uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, buf[i], flag); + uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, + one->rx_buf[i], flag); } } else { @@ -1298,6 +1303,10 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, INIT_WORK(&s->p[i].md_work, max310x_md_proc); /* Initialize queue for changing RS485 mode */ INIT_WORK(&s->p[i].rs_work, max310x_rs_proc); + /* Initialize SPI-transfer buffers */ + s->p[i].wr_header = (s->p[i].port.iobase + MAX310X_THR_REG) | + MAX310X_WRITE_BIT; + s->p[i].rd_header = (s->p[i].port.iobase + MAX310X_RHR_REG); /* Register port */ ret = uart_add_one_port(&max310x_uart, &s->p[i].port); -- cgit v1.2.3-55-g7522 From 35240ba26a932b279a513f66fa4cabfd7af55221 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Tue, 14 May 2019 13:14:12 +0300 Subject: tty: max310x: Fix invalid baudrate divisors calculator Current calculator doesn't do it' job quite correct. First of all the max310x baud-rates generator supports the divisor being less than 16. In this case the x2/x4 modes can be used to double or quadruple the reference frequency. But the current baud-rate setter function just filters all these modes out by the first condition and setups these modes only if there is a clocks-baud division remainder. The former doesn't seem right at all, since enabling the x2/x4 modes causes the line noise tolerance reduction and should be only used as a last resort to enable a requested too high baud-rate. Finally the fraction is supposed to be calculated from D = Fref/(c*baud) formulae, but not from D % 16, which causes the precision loss. So to speak the current baud-rate calculator code works well only if the baud perfectly fits to the uart reference input frequency. Lets fix the calculator by implementing the algo fully compliant with the fractional baud-rate generator described in the datasheet: D = Fref / (c*baud), where c={16,8,4} is the x1/x2/x4 rate mode respectively, Fref - reference input frequency. The divisor fraction is calculated from the same formulae, but making sure it is found with a resolution of 0.0625 (four bits). Signed-off-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 51 +++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 0e3dc89c459b..ca044f96c5cc 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -501,37 +501,48 @@ static bool max310x_reg_precious(struct device *dev, unsigned int reg) static int max310x_set_baud(struct uart_port *port, int baud) { - unsigned int mode = 0, clk = port->uartclk, div = clk / baud; + unsigned int mode = 0, div = 0, frac = 0, c = 0, F = 0; - /* Check for minimal value for divider */ - if (div < 16) - div = 16; - - if (clk % baud && (div / 16) < 0x8000) { + /* + * Calculate the integer divisor first. Select a proper mode + * in case if the requested baud is too high for the pre-defined + * clocks frequency. + */ + div = port->uartclk / baud; + if (div < 8) { + /* Mode x4 */ + c = 4; + mode = MAX310X_BRGCFG_4XMODE_BIT; + } else if (div < 16) { /* Mode x2 */ + c = 8; mode = MAX310X_BRGCFG_2XMODE_BIT; - clk = port->uartclk * 2; - div = clk / baud; - - if (clk % baud && (div / 16) < 0x8000) { - /* Mode x4 */ - mode = MAX310X_BRGCFG_4XMODE_BIT; - clk = port->uartclk * 4; - div = clk / baud; - } + } else { + c = 16; } - max310x_port_write(port, MAX310X_BRGDIVMSB_REG, (div / 16) >> 8); - max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div / 16); - max310x_port_write(port, MAX310X_BRGCFG_REG, (div % 16) | mode); + /* Calculate the divisor in accordance with the fraction coefficient */ + div /= c; + F = c*baud; + + /* Calculate the baud rate fraction */ + if (div > 0) + frac = (16*(port->uartclk % F)) / F; + else + div = 1; + + max310x_port_write(port, MAX310X_BRGDIVMSB_REG, div >> 8); + max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div); + max310x_port_write(port, MAX310X_BRGCFG_REG, frac | mode); - return DIV_ROUND_CLOSEST(clk, div); + /* Return the actual baud rate we just programmed */ + return (16*port->uartclk) / (c*(16*div + frac)); } static int max310x_update_best_err(unsigned long f, long *besterr) { /* Use baudrate 115200 for calculate error */ - long err = f % (115200 * 16); + long err = f % (460800 * 16); if ((*besterr < 0) || (*besterr > err)) { *besterr = err; -- cgit v1.2.3-55-g7522 From 68f22c0c99d9c3534b88d1cd1215753a7a3655aa Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Tue, 14 May 2019 13:14:13 +0300 Subject: tty: max310x: Add rx-during-tx rs485 flag support The driver currently sets the echo suppression bit by default when rs485 is enabled. Naturally it disables any data retrieval in rs485 mode while RTSn is pushed up. The receiver gate (RX_) can be enabled just by clearing (or not setting) the EchoSuprs bit of mode2 register. So by setting or clearing the bit we implement the SER_RS485_RX_DURING_TX rs485 flag support. Signed-off-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index ca044f96c5cc..2255300404bd 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -975,25 +975,23 @@ static void max310x_set_termios(struct uart_port *port, static void max310x_rs_proc(struct work_struct *ws) { struct max310x_one *one = container_of(ws, struct max310x_one, rs_work); - unsigned int val; + unsigned int delay, mode1 = 0, mode2 = 0; - val = (one->port.rs485.delay_rts_before_send << 4) | + delay = (one->port.rs485.delay_rts_before_send << 4) | one->port.rs485.delay_rts_after_send; - max310x_port_write(&one->port, MAX310X_HDPIXDELAY_REG, val); + max310x_port_write(&one->port, MAX310X_HDPIXDELAY_REG, delay); if (one->port.rs485.flags & SER_RS485_ENABLED) { - max310x_port_update(&one->port, MAX310X_MODE1_REG, - MAX310X_MODE1_TRNSCVCTRL_BIT, - MAX310X_MODE1_TRNSCVCTRL_BIT); - max310x_port_update(&one->port, MAX310X_MODE2_REG, - MAX310X_MODE2_ECHOSUPR_BIT, - MAX310X_MODE2_ECHOSUPR_BIT); - } else { - max310x_port_update(&one->port, MAX310X_MODE1_REG, - MAX310X_MODE1_TRNSCVCTRL_BIT, 0); - max310x_port_update(&one->port, MAX310X_MODE2_REG, - MAX310X_MODE2_ECHOSUPR_BIT, 0); + mode1 = MAX310X_MODE1_TRNSCVCTRL_BIT; + + if (!(one->port.rs485.flags & SER_RS485_RX_DURING_TX)) + mode2 = MAX310X_MODE2_ECHOSUPR_BIT; } + + max310x_port_update(&one->port, MAX310X_MODE1_REG, + MAX310X_MODE1_TRNSCVCTRL_BIT, mode1); + max310x_port_update(&one->port, MAX310X_MODE2_REG, + MAX310X_MODE2_ECHOSUPR_BIT, mode2); } static int max310x_rs485_config(struct uart_port *port, @@ -1005,7 +1003,8 @@ static int max310x_rs485_config(struct uart_port *port, (rs485->delay_rts_after_send > 0x0f)) return -ERANGE; - rs485->flags &= SER_RS485_RTS_ON_SEND | SER_RS485_ENABLED; + rs485->flags &= SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX | + SER_RS485_ENABLED; memset(rs485->padding, 0, sizeof(rs485->padding)); port->rs485 = *rs485; -- cgit v1.2.3-55-g7522 From 2b9e6f0a400b6be1a408cd63ed09cfeb8f5dd627 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Tue, 14 May 2019 13:14:14 +0300 Subject: tty: max310x: Optionally enable rs485 on startup UART port might be pre-configured with rs485 enabled flag at the time of the port starting up process. In this case we need to have the hardware rs485-related registers initialized in accordance with the rs485 flags and settings provided by the configs descriptor. Signed-off-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 2255300404bd..36943f6c198c 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1030,6 +1030,22 @@ static int max310x_startup(struct uart_port *port) max310x_port_update(port, MAX310X_MODE2_REG, MAX310X_MODE2_FIFORST_BIT, 0); + /* Configure mode1/mode2 to have rs485/rs232 enabled at startup */ + val = (clamp(port->rs485.delay_rts_before_send, 0U, 15U) << 4) | + clamp(port->rs485.delay_rts_after_send, 0U, 15U); + max310x_port_write(port, MAX310X_HDPIXDELAY_REG, val); + + if (port->rs485.flags & SER_RS485_ENABLED) { + max310x_port_update(port, MAX310X_MODE1_REG, + MAX310X_MODE1_TRNSCVCTRL_BIT, + MAX310X_MODE1_TRNSCVCTRL_BIT); + + if (!(port->rs485.flags & SER_RS485_RX_DURING_TX)) + max310x_port_update(port, MAX310X_MODE2_REG, + MAX310X_MODE2_ECHOSUPR_BIT, + MAX310X_MODE2_ECHOSUPR_BIT); + } + /* Configure flow control levels */ /* Flow control halt level 96, resume level 48 */ max310x_port_write(port, MAX310X_FLOWLVL_REG, -- cgit v1.2.3-55-g7522 From 9c12d739d69b80847e99d24692ff20de993bf172 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Tue, 14 May 2019 13:14:15 +0300 Subject: tty: max310x: Split uart characters insertion loop Batch read mode doesn't check any conditions or flags except the Rx overflow one. But it may only happen after the last character is pushed into the RHR register. In this case we shouldn't push all the read characters with overrun flag set, but only the last one caused the FIFO overflow. This commit splits the characters retrieval loop into two parts. First one is ordinary intsert-chars procedure without taking the overrun status into account. Second part inserts the last character checking whether the overrun happened and pushing a '\0' character with TTY_OVERRUN flag to a flip-buffer. If we left the loop the way it was the '\0' character would be inserted after each character retrieved at the overrun occasion. Signed-off-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 36943f6c198c..81b2413c3da4 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -680,10 +680,16 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) port->icount.overrun++; } - for (i = 0; i < rxlen; ++i) { - uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, - one->rx_buf[i], flag); - } + for (i = 0; i < (rxlen - 1); ++i) + uart_insert_char(port, sts, 0, one->rx_buf[i], flag); + + /* + * Handle the overrun case for the last character only, since + * the RxFIFO overflow happens after it is pushed to the FIFO + * tail. + */ + uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, + one->rx_buf[rxlen], flag); } else { if (unlikely(rxlen >= port->fifosize)) { -- cgit v1.2.3-55-g7522 From c8a9d043947b4acb19a65f7fac2bd0893e581cd5 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Tue, 21 May 2019 17:45:41 +0200 Subject: serial: stm32: fix word length configuration STM32 supports either: - 8 and 9 bits word length (including parity bit) for stm32f4 compatible devices - 7, 8 and 9 bits word length (including parity bit) for stm32f7 and stm32h7 compatible devices. As a consequence STM32 supports the following termios configurations: - CS7 with parity bit, and CS8 (with or without parity bit) for stm32f4 compatible devices. - CS6 with parity bit, CS7 and CS8 (with or without parity bit) for stm32f7 and stm32h7 compatible devices. This patch is fixing word length by configuring correctly the SoC with supported configurations. Fixes: ada8618ff3bf ("serial: stm32: adding support for stm32f7") Signed-off-by: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 56 +++++++++++++++++++++++++++++++++++----- drivers/tty/serial/stm32-usart.h | 3 +-- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index e8d7a7bb4339..e8321850938a 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -599,6 +599,36 @@ static void stm32_shutdown(struct uart_port *port) free_irq(port->irq, port); } +unsigned int stm32_get_databits(struct ktermios *termios) +{ + unsigned int bits; + + tcflag_t cflag = termios->c_cflag; + + switch (cflag & CSIZE) { + /* + * CSIZE settings are not necessarily supported in hardware. + * CSIZE unsupported configurations are handled here to set word length + * to 8 bits word as default configuration and to print debug message. + */ + case CS5: + bits = 5; + break; + case CS6: + bits = 6; + break; + case CS7: + bits = 7; + break; + /* default including CS8 */ + default: + bits = 8; + break; + } + + return bits; +} + static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { @@ -606,7 +636,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct stm32_usart_config *cfg = &stm32_port->info->cfg; struct serial_rs485 *rs485conf = &port->rs485; - unsigned int baud; + unsigned int baud, bits; u32 usartdiv, mantissa, fraction, oversampling; tcflag_t cflag = termios->c_cflag; u32 cr1, cr2, cr3; @@ -632,16 +662,28 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if (cflag & CSTOPB) cr2 |= USART_CR2_STOP_2B; + bits = stm32_get_databits(termios); + if (cflag & PARENB) { + bits++; cr1 |= USART_CR1_PCE; - if ((cflag & CSIZE) == CS8) { - if (cfg->has_7bits_data) - cr1 |= USART_CR1_M0; - else - cr1 |= USART_CR1_M; - } } + /* + * Word length configuration: + * CS8 + parity, 9 bits word aka [M1:M0] = 0b01 + * CS7 or (CS6 + parity), 7 bits word aka [M1:M0] = 0b10 + * CS8 or (CS7 + parity), 8 bits word aka [M1:M0] = 0b00 + * M0 and M1 already cleared by cr1 initialization. + */ + if (bits == 9) + cr1 |= USART_CR1_M0; + else if ((bits == 7) && cfg->has_7bits_data) + cr1 |= USART_CR1_M1; + else if (bits != 8) + dev_dbg(port->dev, "Unsupported data bits config: %u bits\n" + , bits); + if (cflag & PARODD) cr1 |= USART_CR1_PS; diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index 6f294e280ea3..a70aa5006ab9 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -151,8 +151,7 @@ struct stm32_usart_info stm32h7_info = { #define USART_CR1_PS BIT(9) #define USART_CR1_PCE BIT(10) #define USART_CR1_WAKE BIT(11) -#define USART_CR1_M BIT(12) -#define USART_CR1_M0 BIT(12) /* F7 */ +#define USART_CR1_M0 BIT(12) /* F7 (CR1_M for F4) */ #define USART_CR1_MME BIT(13) /* F7 */ #define USART_CR1_CMIE BIT(14) /* F7 */ #define USART_CR1_OVER8 BIT(15) -- cgit v1.2.3-55-g7522 From 4f01d833fdcdd6f9b85d9e5d5d7568eb683626a7 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Tue, 21 May 2019 17:45:42 +0200 Subject: serial: stm32: fix rx error handling - Fixes parity and framing error bit by clearing parity and framing error flag. The current implementation doesn't clear the error bits when an error is detected. - Fixes the incorrect name of framing error clearing flag in header file. - Fixes misalignement between data frame and errors status. The status read for "n" frame was the status of "n+1" frame". - Fixes break detection was not triggered by the expected register. Fixes: 48a6092fb41f ("serial: stm32-usart: Add STM32 USART Driver") Signed-off-by: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 54 ++++++++++++++++++++++++++-------------- drivers/tty/serial/stm32-usart.h | 10 ++------ 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index e8321850938a..f6b739351dde 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -225,35 +225,51 @@ static void stm32_receive_chars(struct uart_port *port, bool threaded) while (stm32_pending_rx(port, &sr, &stm32_port->last_res, threaded)) { sr |= USART_SR_DUMMY_RX; - c = stm32_get_char(port, &sr, &stm32_port->last_res); flag = TTY_NORMAL; - port->icount.rx++; + /* + * Status bits has to be cleared before reading the RDR: + * In FIFO mode, reading the RDR will pop the next data + * (if any) along with its status bits into the SR. + * Not doing so leads to misalignement between RDR and SR, + * and clear status bits of the next rx data. + * + * Clear errors flags for stm32f7 and stm32h7 compatible + * devices. On stm32f4 compatible devices, the error bit is + * cleared by the sequence [read SR - read DR]. + */ + if ((sr & USART_SR_ERR_MASK) && ofs->icr != UNDEF_REG) + stm32_clr_bits(port, ofs->icr, USART_ICR_ORECF | + USART_ICR_PECF | USART_ICR_FECF); + + c = stm32_get_char(port, &sr, &stm32_port->last_res); + port->icount.rx++; if (sr & USART_SR_ERR_MASK) { - if (sr & USART_SR_LBD) { - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } else if (sr & USART_SR_ORE) { - if (ofs->icr != UNDEF_REG) - writel_relaxed(USART_ICR_ORECF, - port->membase + - ofs->icr); + if (sr & USART_SR_ORE) { port->icount.overrun++; } else if (sr & USART_SR_PE) { port->icount.parity++; } else if (sr & USART_SR_FE) { - port->icount.frame++; + /* Break detection if character is null */ + if (!c) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else { + port->icount.frame++; + } } sr &= port->read_status_mask; - if (sr & USART_SR_LBD) - flag = TTY_BREAK; - else if (sr & USART_SR_PE) + if (sr & USART_SR_PE) { flag = TTY_PARITY; - else if (sr & USART_SR_FE) - flag = TTY_FRAME; + } else if (sr & USART_SR_FE) { + if (!c) + flag = TTY_BREAK; + else + flag = TTY_FRAME; + } } if (uart_handle_sysrq_char(port, c)) @@ -721,14 +737,14 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if (termios->c_iflag & INPCK) port->read_status_mask |= USART_SR_PE | USART_SR_FE; if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) - port->read_status_mask |= USART_SR_LBD; + port->read_status_mask |= USART_SR_FE; /* Characters to ignore */ port->ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) port->ignore_status_mask = USART_SR_PE | USART_SR_FE; if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= USART_SR_LBD; + port->ignore_status_mask |= USART_SR_FE; /* * If we're ignoring parity and break indicators, * ignore overruns too (for real raw support). diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index a70aa5006ab9..8d34802e572e 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -108,7 +108,6 @@ struct stm32_usart_info stm32h7_info = { #define USART_SR_RXNE BIT(5) #define USART_SR_TC BIT(6) #define USART_SR_TXE BIT(7) -#define USART_SR_LBD BIT(8) #define USART_SR_CTSIF BIT(9) #define USART_SR_CTS BIT(10) /* F7 */ #define USART_SR_RTOF BIT(11) /* F7 */ @@ -120,8 +119,7 @@ struct stm32_usart_info stm32h7_info = { #define USART_SR_SBKF BIT(18) /* F7 */ #define USART_SR_WUF BIT(20) /* H7 */ #define USART_SR_TEACK BIT(21) /* F7 */ -#define USART_SR_ERR_MASK (USART_SR_LBD | USART_SR_ORE | \ - USART_SR_FE | USART_SR_PE) +#define USART_SR_ERR_MASK (USART_SR_ORE | USART_SR_FE | USART_SR_PE) /* Dummy bits */ #define USART_SR_DUMMY_RX BIT(16) @@ -168,8 +166,6 @@ struct stm32_usart_info stm32h7_info = { /* USART_CR2 */ #define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */ #define USART_CR2_ADDM7 BIT(4) /* F7 */ -#define USART_CR2_LBDL BIT(5) -#define USART_CR2_LBDIE BIT(6) #define USART_CR2_LBCL BIT(8) #define USART_CR2_CPHA BIT(9) #define USART_CR2_CPOL BIT(10) @@ -226,12 +222,10 @@ struct stm32_usart_info stm32h7_info = { /* USART_ICR */ #define USART_ICR_PECF BIT(0) /* F7 */ -#define USART_ICR_FFECF BIT(1) /* F7 */ -#define USART_ICR_NCF BIT(2) /* F7 */ +#define USART_ICR_FECF BIT(1) /* F7 */ #define USART_ICR_ORECF BIT(3) /* F7 */ #define USART_ICR_IDLECF BIT(4) /* F7 */ #define USART_ICR_TCCF BIT(6) /* F7 */ -#define USART_ICR_LBDCF BIT(8) /* F7 */ #define USART_ICR_CTSCF BIT(9) /* F7 */ #define USART_ICR_RTOCF BIT(11) /* F7 */ #define USART_ICR_EOBCF BIT(12) /* F7 */ -- cgit v1.2.3-55-g7522 From 6c5962f30bce147b1c83869085f3ddde3b34c9e3 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Tue, 21 May 2019 17:45:43 +0200 Subject: serial: stm32: fix rx data length when parity enabled - Fixes a rx data error when data length < 8 bits and parity is enabled. RDR register MSB is used for parity bit reception. - Adds a mask to ignore MSB when data is get from RDR. Fixes: 3489187204eb ("serial: stm32: adding dma support") Signed-off-by: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 12 ++++++++---- drivers/tty/serial/stm32-usart.h | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index f6b739351dde..0a7953e5ce47 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -194,8 +194,8 @@ static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, return 0; } -static unsigned long -stm32_get_char(struct uart_port *port, u32 *sr, int *last_res) +static unsigned long stm32_get_char(struct uart_port *port, u32 *sr, + int *last_res) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -205,10 +205,13 @@ stm32_get_char(struct uart_port *port, u32 *sr, int *last_res) c = stm32_port->rx_buf[RX_BUF_L - (*last_res)--]; if ((*last_res) == 0) *last_res = RX_BUF_L; - return c; } else { - return readl_relaxed(port->membase + ofs->rdr); + c = readl_relaxed(port->membase + ofs->rdr); + /* apply RDR data mask */ + c &= stm32_port->rdr_mask; } + + return c; } static void stm32_receive_chars(struct uart_port *port, bool threaded) @@ -679,6 +682,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, cr2 |= USART_CR2_STOP_2B; bits = stm32_get_databits(termios); + stm32_port->rdr_mask = (BIT(bits) - 1); if (cflag & PARENB) { bits++; diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index 8d34802e572e..30d2433e27c3 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -254,6 +254,7 @@ struct stm32_port { bool hw_flow_control; bool fifoen; int wakeirq; + int rdr_mask; /* receive data register mask */ }; static struct stm32_port stm32_ports[STM32_MAX_PORTS]; -- cgit v1.2.3-55-g7522 From b83b957c91f68e53f0dc596e129e8305761f2a32 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Tue, 21 May 2019 17:45:44 +0200 Subject: serial: stm32: fix transmit_chars when tx is stopped Disables the tx irq when the transmission is ended and updates stop_tx conditions for code cleanup. Fixes: 48a6092fb41f ("serial: stm32-usart: Add STM32 USART Driver") Signed-off-by: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 0a7953e5ce47..2e7757d5e5d8 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -420,13 +420,8 @@ static void stm32_transmit_chars(struct uart_port *port) return; } - if (uart_tx_stopped(port)) { - stm32_stop_tx(port); - return; - } - - if (uart_circ_empty(xmit)) { - stm32_stop_tx(port); + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); return; } @@ -439,7 +434,7 @@ static void stm32_transmit_chars(struct uart_port *port) uart_write_wakeup(port); if (uart_circ_empty(xmit)) - stm32_stop_tx(port); + stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); } static irqreturn_t stm32_interrupt(int irq, void *ptr) -- cgit v1.2.3-55-g7522 From 64c32eab660386f9904bb295a104c9c425e9f8b2 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Tue, 21 May 2019 17:45:45 +0200 Subject: serial: stm32: Add support of TC bit status check Adds a check on the Transmission Complete bit status before closing the com port. Prevents the port closure before the end of the transmission. TC poll loop is moved from stm32_tx_dma_complete to stm32_shutdown routine, in order to check TC before shutdown in both dma and PIO tx modes. TC clear is added in stm32_transmit_char routine, in order to be cleared before transmitting in both dma and PIO tx modes. Fixes: 3489187204eb ("serial: stm32: adding dma support") Signed-off-by: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 2e7757d5e5d8..d603be9669a9 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -290,21 +290,6 @@ static void stm32_tx_dma_complete(void *arg) struct uart_port *port = arg; struct stm32_port *stm32port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32port->info->ofs; - unsigned int isr; - int ret; - - ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, - isr, - (isr & USART_SR_TC), - 10, 100000); - - if (ret) - dev_err(port->dev, "terminal count not set\n"); - - if (ofs->icr == UNDEF_REG) - stm32_clr_bits(port, ofs->isr, USART_SR_TC); - else - stm32_set_bits(port, ofs->icr, USART_CR_TC); stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); stm32port->tx_dma_busy = false; @@ -396,7 +381,6 @@ static void stm32_transmit_chars_dma(struct uart_port *port) /* Issue pending DMA TX requests */ dma_async_issue_pending(stm32port->tx_ch); - stm32_clr_bits(port, ofs->isr, USART_SR_TC); stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT); xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); @@ -425,6 +409,11 @@ static void stm32_transmit_chars(struct uart_port *port) return; } + if (ofs->icr == UNDEF_REG) + stm32_clr_bits(port, ofs->isr, USART_SR_TC); + else + stm32_set_bits(port, ofs->icr, USART_ICR_TCCF); + if (stm32_port->tx_ch) stm32_transmit_chars_dma(port); else @@ -601,12 +590,21 @@ static void stm32_shutdown(struct uart_port *port) struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct stm32_usart_config *cfg = &stm32_port->info->cfg; - u32 val; + u32 val, isr; + int ret; val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; val |= BIT(cfg->uart_enable_bit); if (stm32_port->fifoen) val |= USART_CR1_FIFOEN; + + ret = readl_relaxed_poll_timeout(port->membase + ofs->isr, + isr, (isr & USART_SR_TC), + 10, 100000); + + if (ret) + dev_err(port->dev, "transmission complete not set\n"); + stm32_clr_bits(port, ofs->cr1, val); dev_pm_clear_wake_irq(port->dev); -- cgit v1.2.3-55-g7522 From 5297f274e8b61ceb9676cba6649d3de9d03387ad Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Tue, 21 May 2019 17:45:46 +0200 Subject: serial: stm32: fix wakeup source initialization Fixes dedicated_irq_wakeup issue and deactivated uart as wakeup source by default. Fixes: 270e5a74fe4c ("serial: stm32: add wakeup mechanism") Signed-off-by: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index d603be9669a9..1334e4293977 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -557,7 +557,6 @@ static int stm32_startup(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - struct stm32_usart_config *cfg = &stm32_port->info->cfg; const char *name = to_platform_device(port->dev)->name; u32 val; int ret; @@ -568,15 +567,6 @@ static int stm32_startup(struct uart_port *port) if (ret) return ret; - if (cfg->has_wakeup && stm32_port->wakeirq >= 0) { - ret = dev_pm_set_dedicated_wake_irq(port->dev, - stm32_port->wakeirq); - if (ret) { - free_irq(port->irq, port); - return ret; - } - } - val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; if (stm32_port->fifoen) val |= USART_CR1_FIFOEN; @@ -607,7 +597,6 @@ static void stm32_shutdown(struct uart_port *port) stm32_clr_bits(port, ofs->cr1, val); - dev_pm_clear_wake_irq(port->dev); free_irq(port->irq, port); } @@ -1079,11 +1068,18 @@ static int stm32_serial_probe(struct platform_device *pdev) ret = device_init_wakeup(&pdev->dev, true); if (ret) goto err_uninit; + + ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, + stm32port->wakeirq); + if (ret) + goto err_nowup; + + device_set_wakeup_enable(&pdev->dev, false); } ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port); if (ret) - goto err_nowup; + goto err_wirq; ret = stm32_of_dma_rx_probe(stm32port, pdev); if (ret) @@ -1097,6 +1093,10 @@ static int stm32_serial_probe(struct platform_device *pdev) return 0; +err_wirq: + if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) + dev_pm_clear_wake_irq(&pdev->dev); + err_nowup: if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) device_init_wakeup(&pdev->dev, false); @@ -1134,8 +1134,10 @@ static int stm32_serial_remove(struct platform_device *pdev) TX_BUF_L, stm32_port->tx_buf, stm32_port->tx_dma_buf); - if (cfg->has_wakeup && stm32_port->wakeirq >= 0) + if (cfg->has_wakeup && stm32_port->wakeirq >= 0) { + dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); + } clk_disable_unprepare(stm32_port->clk); -- cgit v1.2.3-55-g7522 From 2c58e56096dd011ea43f29a032d19b0e76fe2570 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Tue, 21 May 2019 17:45:47 +0200 Subject: serial: stm32: fix the get_irq error case Checks the returned values of platform_get_irq() for both required "event" and optional "wakeup" IRQs during probe. This allows the driver probe to be deferred if needed. Removes redundant checks for 'cfg.has_wakeup'. Signed-off-by: Fabien Dessenne Signed-off-by: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 1334e4293977..9c2b04e6f5b6 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -852,12 +852,31 @@ static int stm32_init_port(struct stm32_port *stm32port, port->flags = UPF_BOOT_AUTOCONF; port->ops = &stm32_uart_ops; port->dev = &pdev->dev; - port->irq = platform_get_irq(pdev, 0); + + ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Can't get event IRQ: %d\n", ret); + return ret ? ret : -ENODEV; + } + port->irq = ret; + port->rs485_config = stm32_config_rs485; stm32_init_rs485(port, pdev); - stm32port->wakeirq = platform_get_irq(pdev, 1); + if (stm32port->info->cfg.has_wakeup) { + stm32port->wakeirq = platform_get_irq(pdev, 1); + if (stm32port->wakeirq <= 0 && stm32port->wakeirq != -ENXIO) { + if (stm32port->wakeirq != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Can't get event wake IRQ: %d\n", + stm32port->wakeirq); + return stm32port->wakeirq ? stm32port->wakeirq : + -ENODEV; + } + } + stm32port->fifoen = stm32port->info->cfg.has_fifo; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1064,7 +1083,7 @@ static int stm32_serial_probe(struct platform_device *pdev) if (ret) return ret; - if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) { + if (stm32port->wakeirq > 0) { ret = device_init_wakeup(&pdev->dev, true); if (ret) goto err_uninit; @@ -1094,11 +1113,11 @@ static int stm32_serial_probe(struct platform_device *pdev) return 0; err_wirq: - if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) + if (stm32port->wakeirq > 0) dev_pm_clear_wake_irq(&pdev->dev); err_nowup: - if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) + if (stm32port->wakeirq > 0) device_init_wakeup(&pdev->dev, false); err_uninit: @@ -1112,7 +1131,6 @@ static int stm32_serial_remove(struct platform_device *pdev) struct uart_port *port = platform_get_drvdata(pdev); struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - struct stm32_usart_config *cfg = &stm32_port->info->cfg; stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAR); @@ -1134,7 +1152,7 @@ static int stm32_serial_remove(struct platform_device *pdev) TX_BUF_L, stm32_port->tx_buf, stm32_port->tx_dma_buf); - if (cfg->has_wakeup && stm32_port->wakeirq >= 0) { + if (stm32_port->wakeirq > 0) { dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); } @@ -1252,7 +1270,7 @@ static void stm32_serial_enable_wakeup(struct uart_port *port, bool enable) struct stm32_usart_config *cfg = &stm32_port->info->cfg; u32 val; - if (!cfg->has_wakeup || stm32_port->wakeirq < 0) + if (stm32_port->wakeirq <= 0) return; if (enable) { -- cgit v1.2.3-55-g7522 From 06aaa3d066db87e8478522d910285141d44b1e58 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Wed, 22 May 2019 12:17:11 +0000 Subject: tty: serial: cpm_uart - fix init when SMC is relocated SMC relocation can also be activated earlier by the bootloader, so the driver's behaviour cannot rely on selected kernel config. When the SMC is relocated, CPM_CR_INIT_TRX cannot be used. But the only thing CPM_CR_INIT_TRX does is to clear the rstate and tstate registers, so this can be done manually, even when SMC is not relocated. Signed-off-by: Christophe Leroy Fixes: 9ab921201444 ("cpm_uart: fix non-console port startup bug") Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/cpm_uart/cpm_uart_core.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index b929c7ae3a27..7bab9a3eda92 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -407,7 +407,16 @@ static int cpm_uart_startup(struct uart_port *port) clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX); } cpm_uart_initbd(pinfo); - cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX); + if (IS_SMC(pinfo)) { + out_be32(&pinfo->smcup->smc_rstate, 0); + out_be32(&pinfo->smcup->smc_tstate, 0); + out_be16(&pinfo->smcup->smc_rbptr, + in_be16(&pinfo->smcup->smc_rbase)); + out_be16(&pinfo->smcup->smc_tbptr, + in_be16(&pinfo->smcup->smc_tbase)); + } else { + cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX); + } } /* Install interrupt handler. */ retval = request_irq(port->irq, cpm_uart_int, 0, "cpm_uart", port); @@ -861,16 +870,14 @@ static void cpm_uart_init_smc(struct uart_cpm_port *pinfo) (u8 __iomem *)pinfo->tx_bd_base - DPRAM_BASE); /* - * In case SMC1 is being relocated... + * In case SMC is being relocated... */ -#if defined (CONFIG_I2C_SPI_SMC1_UCODE_PATCH) out_be16(&up->smc_rbptr, in_be16(&pinfo->smcup->smc_rbase)); out_be16(&up->smc_tbptr, in_be16(&pinfo->smcup->smc_tbase)); out_be32(&up->smc_rstate, 0); out_be32(&up->smc_tstate, 0); out_be16(&up->smc_brkcr, 1); /* number of break chars */ out_be16(&up->smc_brkec, 0); -#endif /* Set up the uart parameters in the * parameter ram. @@ -884,8 +891,6 @@ static void cpm_uart_init_smc(struct uart_cpm_port *pinfo) out_be16(&up->smc_brkec, 0); out_be16(&up->smc_brkcr, 1); - cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX); - /* Set UART mode, 8 bit, no parity, one stop. * Enable receive and transmit. */ -- cgit v1.2.3-55-g7522 From 84ecc2f6eb1cb12e6d44818f94fa49b50f06e6ac Mon Sep 17 00:00:00 2001 From: Gen Zhang Date: Thu, 23 May 2019 08:34:52 +0800 Subject: consolemap: Fix a memory leaking bug in drivers/tty/vt/consolemap.c In function con_insert_unipair(), when allocation for p2 and p1[n] fails, ENOMEM is returned, but previously allocated p1 is not freed, remains as leaking memory. Thus we should free p1 as well when this allocation fails. Signed-off-by: Gen Zhang Reviewed-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/consolemap.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index b28aa0d289f8..79fcc96cc7c0 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -489,7 +489,11 @@ con_insert_unipair(struct uni_pagedir *p, u_short unicode, u_short fontpos) p2 = p1[n = (unicode >> 6) & 0x1f]; if (!p2) { p2 = p1[n] = kmalloc_array(64, sizeof(u16), GFP_KERNEL); - if (!p2) return -ENOMEM; + if (!p2) { + kfree(p1); + p->uni_pgdir[n] = NULL; + return -ENOMEM; + } memset(p2, 0xff, 64*sizeof(u16)); /* No glyphs for the characters (yet) */ } -- cgit v1.2.3-55-g7522 From 5e309c6c5ea12515616a4835b7fa2141c8016c94 Mon Sep 17 00:00:00 2001 From: Grzegorz Halat Date: Fri, 26 Apr 2019 16:59:46 +0200 Subject: vt/fbcon: deinitialize resources in visual_init() after failed memory allocation After memory allocation failure vc_allocate() doesn't clean up data which has been initialized in visual_init(). In case of fbcon this leads to divide-by-0 in fbcon_init() on next open of the same tty. memory allocation in vc_allocate() may fail here: 1097: vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_KERNEL); on next open() fbcon_init() skips vc_font.data initialization: 1088: if (!p->fontdata) { division by zero in fbcon_init() happens here: 1149: new_cols /= vc->vc_font.width; Additional check is needed in fbcon_deinit() to prevent usage of uninitialized vc_screenbuf: 1251: if (vc->vc_hi_font_mask && vc->vc_screenbuf) 1252: set_vc_hi_font(vc, false); Crash: #6 [ffffc90001eafa60] divide_error at ffffffff81a00be4 [exception RIP: fbcon_init+463] RIP: ffffffff814b860f RSP: ffffc90001eafb18 RFLAGS: 00010246 ... #7 [ffffc90001eafb60] visual_init at ffffffff8154c36e #8 [ffffc90001eafb80] vc_allocate at ffffffff8154f53c #9 [ffffc90001eafbc8] con_install at ffffffff8154f624 ... Signed-off-by: Grzegorz Halat Reviewed-by: Oleksandr Natalenko Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 11 +++++++++-- drivers/video/fbdev/core/fbcon.c | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index fdd12f8c3deb..5c0ca1c24b6f 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1056,6 +1056,13 @@ static void visual_init(struct vc_data *vc, int num, int init) vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row; } + +static void visual_deinit(struct vc_data *vc) +{ + vc->vc_sw->con_deinit(vc); + module_put(vc->vc_sw->owner); +} + int vc_allocate(unsigned int currcons) /* return 0 on success */ { struct vt_notifier_param param; @@ -1103,6 +1110,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ return 0; err_free: + visual_deinit(vc); kfree(vc); vc_cons[currcons].d = NULL; return -ENOMEM; @@ -1331,9 +1339,8 @@ struct vc_data *vc_deallocate(unsigned int currcons) param.vc = vc = vc_cons[currcons].d; atomic_notifier_call_chain(&vt_notifier_list, VT_DEALLOCATE, ¶m); vcs_remove_sysfs(currcons); - vc->vc_sw->con_deinit(vc); + visual_deinit(vc); put_pid(vc->vt_pid); - module_put(vc->vc_sw->owner); vc_uniscr_set(vc, NULL); kfree(vc->vc_screenbuf); vc_cons[currcons].d = NULL; diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 786f9aab55df..a9c69ae30878 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -1248,7 +1248,7 @@ finished: if (free_font) vc->vc_font.data = NULL; - if (vc->vc_hi_font_mask) + if (vc->vc_hi_font_mask && vc->vc_screenbuf) set_vc_hi_font(vc, false); if (!con_is_bound(&fb_con)) -- cgit v1.2.3-55-g7522 From 8c8af41d24c73c6a8b24468bdc53e52b390e4582 Mon Sep 17 00:00:00 2001 From: Lin Yi Date: Fri, 10 May 2019 12:22:57 +0800 Subject: tty: tty_io: fix driver refcount imbalance on error path tty_lookup_driver take a reference to the struct tty_driver, but forget to release it on the error path, lead to a memory leak. add a tty_driver_kref_put before error return. Signed-off-by: Lin Yi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 033ac7e6a70d..566728fbaf3c 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1837,7 +1837,7 @@ static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp) static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, int *index) { - struct tty_driver *driver; + struct tty_driver *driver = NULL; switch (device) { #ifdef CONFIG_VT @@ -1858,6 +1858,8 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, break; } } + if (driver) + tty_driver_kref_put(driver); return ERR_PTR(-ENODEV); } default: -- cgit v1.2.3-55-g7522 From 92c42b492af5f37e7306c6fd42b46f7a0c5fd5e4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 26 Apr 2019 07:59:23 +0200 Subject: tty: simserial: drop unused iflag macro Drop the RELEVANT_IFLAG() macro which hasn't been used for over a decade. Cc: Fenghua Yu Signed-off-by: Johan Hovold Acked-by: Tony Luck Signed-off-by: Greg Kroah-Hartman --- arch/ia64/hp/sim/simserial.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index 7aeb48a18576..1a338e541334 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -324,8 +324,6 @@ static int rs_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) return -ENOIOCTLCMD; } -#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - /* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. -- cgit v1.2.3-55-g7522 From b95781666d138ebf08cec46e58666ab8fbc37b9f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 26 Apr 2019 07:59:24 +0200 Subject: tty: ipoctal: drop unused iflag macro Drop the RELEVANT_IFLAG() macro which has never been used. Cc: Samuel Iglesias Gonsalvez Cc: Jens Taprogge Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/ipack/devices/ipoctal.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/ipack/devices/ipoctal.h b/drivers/ipack/devices/ipoctal.h index 7fede0eb6a0c..78e4fc81fb03 100644 --- a/drivers/ipack/devices/ipoctal.h +++ b/drivers/ipack/devices/ipoctal.h @@ -18,7 +18,6 @@ #define NR_CHANNELS 8 #define IPOCTAL_MAX_BOARDS 16 #define MAX_DEVICES (NR_CHANNELS * IPOCTAL_MAX_BOARDS) -#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) /** * struct ipoctal_stats -- Stats since last reset -- cgit v1.2.3-55-g7522 From 324e8f5f5d9d4d0b9a608b2fdb88cd93b0fbb12a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 26 Apr 2019 07:59:25 +0200 Subject: tty: cpm_uart: drop unused iflag macro Drop the RELEVANT_IFLAG() macro which hasn't been used at least since the dawn of git. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/cpm_uart/cpm_uart_core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index 7bab9a3eda92..de6d02f7abe2 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -576,8 +576,6 @@ static void cpm_uart_set_termios(struct uart_port *port, /* * Set up parity check flag */ -#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - port->read_status_mask = (BD_SC_EMPTY | BD_SC_OV); if (termios->c_iflag & INPCK) port->read_status_mask |= BD_SC_FR | BD_SC_PR; -- cgit v1.2.3-55-g7522 From 15b3cd8ef46ad1b100e0d3c7e38774f330726820 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 4 Jun 2019 19:00:39 +0100 Subject: Revert "consolemap: Fix a memory leaking bug in drivers/tty/vt/consolemap.c" This reverts commit 84ecc2f6eb1cb12e6d44818f94fa49b50f06e6ac. con_insert_unipair() is working with a sparse 3-dimensional array: - p->uni_pgdir[] is the top layer - p1 points to a middle layer - p2 points to a bottom layer If it needs to allocate a new middle layer, and then fails to allocate a new bottom layer, it would previously free only p2, and now it frees both p1 and p2. But since the new middle layer was already registered in the top layer, it was not leaked. However, if it looks up an *existing* middle layer and then fails to allocate a bottom layer, it now frees both p1 and p2 but does *not* free any other bottom layers under p1. So it *introduces* a memory leak. The error path also cleared the wrong index in p->uni_pgdir[], introducing a use-after-free. Signed-off-by: Ben Hutchings Fixes: 84ecc2f6eb1c ("consolemap: Fix a memory leaking bug in drivers/tty/vt/consolemap.c") Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/consolemap.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 79fcc96cc7c0..b28aa0d289f8 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -489,11 +489,7 @@ con_insert_unipair(struct uni_pagedir *p, u_short unicode, u_short fontpos) p2 = p1[n = (unicode >> 6) & 0x1f]; if (!p2) { p2 = p1[n] = kmalloc_array(64, sizeof(u16), GFP_KERNEL); - if (!p2) { - kfree(p1); - p->uni_pgdir[n] = NULL; - return -ENOMEM; - } + if (!p2) return -ENOMEM; memset(p2, 0xff, 64*sizeof(u16)); /* No glyphs for the characters (yet) */ } -- cgit v1.2.3-55-g7522 From c70669ecef4e34af8913108fae4b8a9b1dc63fa1 Mon Sep 17 00:00:00 2001 From: Bich Hemon Date: Tue, 4 Jun 2019 10:55:12 +0200 Subject: serial: stm32: select pinctrl state in each suspend/resume function Select either pinctrl sleep state in suspend function or default state in resume function. Signed-off-by: Bich Hemon Signed-off-by: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 9c2b04e6f5b6..04eda75d671c 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -1298,6 +1298,8 @@ static int stm32_serial_suspend(struct device *dev) else stm32_serial_enable_wakeup(port, false); + pinctrl_pm_select_sleep_state(dev); + return 0; } @@ -1305,6 +1307,8 @@ static int stm32_serial_resume(struct device *dev) { struct uart_port *port = dev_get_drvdata(dev); + pinctrl_pm_select_default_state(dev); + if (device_may_wakeup(dev)) stm32_serial_enable_wakeup(port, false); -- cgit v1.2.3-55-g7522 From 929ffa4ade6ccc0059368ff55ca7b866b178ca47 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Tue, 28 May 2019 17:04:49 +0800 Subject: serial: stm32: Make stm32_get_databits static Fix sparse warning: drivers/tty/serial/stm32-usart.c:603:14: warning: symbol 'stm32_get_databits' was not declared. Should it be static? Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 04eda75d671c..4c217fb35bcb 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -600,7 +600,7 @@ static void stm32_shutdown(struct uart_port *port) free_irq(port->irq, port); } -unsigned int stm32_get_databits(struct ktermios *termios) +static unsigned int stm32_get_databits(struct ktermios *termios) { unsigned int bits; -- cgit v1.2.3-55-g7522 From c7ad9ba0611c53cfe194223db02e3bca015f0674 Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Fri, 31 May 2019 21:37:33 +0800 Subject: tty/serial: digicolor: Fix digicolor-usart already registered warning When modprobe/rmmod/modprobe module, if platform_driver_register() fails, the kernel complained, proc_dir_entry 'driver/digicolor-usart' already registered WARNING: CPU: 1 PID: 5636 at fs/proc/generic.c:360 proc_register+0x19d/0x270 Fix this by adding uart_unregister_driver() when platform_driver_register() fails. Reported-by: Hulk Robot Signed-off-by: Kefeng Wang Acked-by: Baruch Siach Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/digicolor-usart.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c index f460cca139e2..13ac36e2da4f 100644 --- a/drivers/tty/serial/digicolor-usart.c +++ b/drivers/tty/serial/digicolor-usart.c @@ -541,7 +541,11 @@ static int __init digicolor_uart_init(void) if (ret) return ret; - return platform_driver_register(&digicolor_uart_platform); + ret = platform_driver_register(&digicolor_uart_platform); + if (ret) + uart_unregister_driver(&digicolor_uart); + + return ret; } module_init(digicolor_uart_init); -- cgit v1.2.3-55-g7522 From 1f74dfa803e3e82978d45b36a70926256344399e Mon Sep 17 00:00:00 2001 From: Long Cheng Date: Thu, 23 May 2019 15:35:09 +0800 Subject: serial: 8250-mtk: modify uart DMA rx Modify uart rx and complete for DMA Signed-off-by: Long Cheng Reviewed-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mtk.c | 49 ++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 417c7c810df9..f470dedceba9 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -47,7 +47,6 @@ #define MTK_UART_DMA_EN_RX 0x5 #define MTK_UART_ESCAPE_CHAR 0x77 /* Escape char added under sw fc */ -#define MTK_UART_TX_SIZE UART_XMIT_SIZE #define MTK_UART_RX_SIZE 0x8000 #define MTK_UART_TX_TRIGGER 1 #define MTK_UART_RX_TRIGGER MTK_UART_RX_SIZE @@ -89,28 +88,30 @@ static void mtk8250_dma_rx_complete(void *param) struct mtk8250_data *data = up->port.private_data; struct tty_port *tty_port = &up->port.state->port; struct dma_tx_state state; + int copied, total, cnt; unsigned char *ptr; - int copied; - dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr, - dma->rx_size, DMA_FROM_DEVICE); + if (data->rx_status == DMA_RX_SHUTDOWN) + return; dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + total = dma->rx_size - state.residue; + cnt = total; - if (data->rx_status == DMA_RX_SHUTDOWN) - return; + if ((data->rx_pos + cnt) > dma->rx_size) + cnt = dma->rx_size - data->rx_pos; - if ((data->rx_pos + state.residue) <= dma->rx_size) { - ptr = (unsigned char *)(data->rx_pos + dma->rx_buf); - copied = tty_insert_flip_string(tty_port, ptr, state.residue); - } else { - ptr = (unsigned char *)(data->rx_pos + dma->rx_buf); - copied = tty_insert_flip_string(tty_port, ptr, - dma->rx_size - data->rx_pos); + ptr = (unsigned char *)(data->rx_pos + dma->rx_buf); + copied = tty_insert_flip_string(tty_port, ptr, cnt); + data->rx_pos += cnt; + + if (total > cnt) { ptr = (unsigned char *)(dma->rx_buf); - copied += tty_insert_flip_string(tty_port, ptr, - data->rx_pos + state.residue - dma->rx_size); + cnt = total - cnt; + copied += tty_insert_flip_string(tty_port, ptr, cnt); + data->rx_pos = cnt; } + up->port.icount.rx += copied; tty_flip_buffer_push(tty_port); @@ -121,9 +122,7 @@ static void mtk8250_dma_rx_complete(void *param) static void mtk8250_rx_dma(struct uart_8250_port *up) { struct uart_8250_dma *dma = up->dma; - struct mtk8250_data *data = up->port.private_data; struct dma_async_tx_descriptor *desc; - struct dma_tx_state state; desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, dma->rx_size, DMA_DEV_TO_MEM, @@ -138,12 +137,6 @@ static void mtk8250_rx_dma(struct uart_8250_port *up) dma->rx_cookie = dmaengine_submit(desc); - dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); - data->rx_pos = state.residue; - - dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr, - dma->rx_size, DMA_FROM_DEVICE); - dma_async_issue_pending(dma->rxchan); } @@ -156,13 +149,11 @@ static void mtk8250_dma_enable(struct uart_8250_port *up) if (data->rx_status != DMA_RX_START) return; - dma->rxconf.direction = DMA_DEV_TO_MEM; - dma->rxconf.src_addr_width = dma->rx_size / 1024; - dma->rxconf.src_addr = dma->rx_addr; + dma->rxconf.src_port_window_size = dma->rx_size; + dma->rxconf.src_addr = dma->rx_addr; - dma->txconf.direction = DMA_MEM_TO_DEV; - dma->txconf.dst_addr_width = MTK_UART_TX_SIZE / 1024; - dma->txconf.dst_addr = dma->tx_addr; + dma->txconf.dst_port_window_size = UART_XMIT_SIZE; + dma->txconf.dst_addr = dma->tx_addr; serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); -- cgit v1.2.3-55-g7522 From 107475685abfdee504bb0ef4824f15797f6d2d4d Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Fri, 24 May 2019 14:41:28 +0530 Subject: serial: uartps: Move the spinlock after the read of the tx empty Currently we are doing a read of the status register. Move the spinlock after that as the reads need not be spinlock protected. This patch prevents relaxing the cpu with spinlock held. Signed-off-by: Shubhrajyoti Datta Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 605354fd60b1..33ac898fb36e 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -686,8 +686,6 @@ static void cdns_uart_set_termios(struct uart_port *port, unsigned long flags; unsigned int ctrl_reg, mode_reg; - spin_lock_irqsave(&port->lock, flags); - /* Wait for the transmit FIFO to empty before making changes */ if (!(readl(port->membase + CDNS_UART_CR) & CDNS_UART_CR_TX_DIS)) { @@ -696,6 +694,7 @@ static void cdns_uart_set_termios(struct uart_port *port, cpu_relax(); } } + spin_lock_irqsave(&port->lock, flags); /* Disable the TX and RX to set baud rate */ ctrl_reg = readl(port->membase + CDNS_UART_CR); -- cgit v1.2.3-55-g7522 From 277375b864e8147975b064b513f491e2a910e66a Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Fri, 24 May 2019 14:41:29 +0530 Subject: serial: uartps: Add a timeout to the tx empty wait In case the cable is not connected then the target gets into an infinite wait for tx empty. Add a timeout to the tx empty wait. Reported-by: Jean-Francois Dagenais Signed-off-by: Shubhrajyoti Datta Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 33ac898fb36e..ac3fd58f7092 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -26,12 +26,14 @@ #include #include #include +#include #define CDNS_UART_TTY_NAME "ttyPS" #define CDNS_UART_NAME "xuartps" #define CDNS_UART_MAJOR 0 /* use dynamic node allocation */ #define CDNS_UART_FIFO_SIZE 64 /* FIFO size */ #define CDNS_UART_REGISTER_SPACE 0x1000 +#define TX_TIMEOUT 500000 /* Rx Trigger level */ static int rx_trigger_level = 56; @@ -684,14 +686,18 @@ static void cdns_uart_set_termios(struct uart_port *port, unsigned int cval = 0; unsigned int baud, minbaud, maxbaud; unsigned long flags; - unsigned int ctrl_reg, mode_reg; + unsigned int ctrl_reg, mode_reg, val; + int err; /* Wait for the transmit FIFO to empty before making changes */ if (!(readl(port->membase + CDNS_UART_CR) & CDNS_UART_CR_TX_DIS)) { - while (!(readl(port->membase + CDNS_UART_SR) & - CDNS_UART_SR_TXEMPTY)) { - cpu_relax(); + err = readl_poll_timeout(port->membase + CDNS_UART_SR, + val, (val & CDNS_UART_SR_TXEMPTY), + 1000, TX_TIMEOUT); + if (err) { + dev_err(port->dev, "timed out waiting for tx empty"); + return; } } spin_lock_irqsave(&port->lock, flags); -- cgit v1.2.3-55-g7522 From 9315ad99ed5bc3865cef0da56e705549cd04fe7e Mon Sep 17 00:00:00 2001 From: Claire Chang Date: Mon, 27 May 2019 16:31:50 +0800 Subject: uart: mediatek: support Rx in-band wakeup In order to support Rx in-band wakeup, we need to enable irq wake on an edge sensitive interrupt of Rx pin before suspend and disable it when resuming. This interrupt is used only as wake source to resume the system when suspended. Note that the sent character will be lost as the controller is actually suspended. We use this to support wakeup on bluetooth. Bluetooth will repeatedly send 0xFD to wakeup host. Once host detects Rx falling, an interrupt is triggered, and the system leaves sleep state. Then, the bluetooth driver will send 0xFC to bluetooth and bluetooth can start to send normal HCI packets. Signed-off-by: Claire Chang Reviewed-by: Nicolas Boichat Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mtk.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index f470dedceba9..b411ba4eb5e9 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,7 @@ struct mtk8250_data { #ifdef CONFIG_SERIAL_8250_DMA enum dma_rx_status rx_status; #endif + int rx_wakeup_irq; }; /* flow control mode */ @@ -542,6 +544,8 @@ static int mtk8250_probe(struct platform_device *pdev) pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); + data->rx_wakeup_irq = platform_get_irq(pdev, 1); + return 0; } @@ -563,15 +567,35 @@ static int mtk8250_remove(struct platform_device *pdev) static int __maybe_unused mtk8250_suspend(struct device *dev) { struct mtk8250_data *data = dev_get_drvdata(dev); + int irq = data->rx_wakeup_irq; + int err; serial8250_suspend_port(data->line); + pinctrl_pm_select_sleep_state(dev); + if (irq >= 0) { + err = enable_irq_wake(irq); + if (err) { + dev_err(dev, + "failed to enable irq wake on IRQ %d: %d\n", + irq, err); + pinctrl_pm_select_default_state(dev); + serial8250_resume_port(data->line); + return err; + } + } + return 0; } static int __maybe_unused mtk8250_resume(struct device *dev) { struct mtk8250_data *data = dev_get_drvdata(dev); + int irq = data->rx_wakeup_irq; + + if (irq >= 0) + disable_irq_wake(irq); + pinctrl_pm_select_default_state(dev); serial8250_resume_port(data->line); -- cgit v1.2.3-55-g7522 From 9edaf50b373d3fe94e360af16f7c0d024d9567bb Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 4 Jun 2019 00:31:38 -0300 Subject: serial: fsl_lpuart: Use dev_info() instead of printk() dev_info() is more appropriate for printing messages inside drivers, so switch to dev_info(). Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index ea1c85e3b432..08b52cca650c 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -2078,7 +2078,7 @@ lpuart_console_get_options(struct lpuart_port *sport, int *baud, baud_raw = uartclk / (16 * (sbr + brfa / 32)); if (*baud != baud_raw) - printk(KERN_INFO "Serial: Console lpuart rounded baud rate" + dev_info(sport->port.dev, "Serial: Console lpuart rounded baud rate" "from %d to %d\n", baud_raw, *baud); } @@ -2121,7 +2121,7 @@ lpuart32_console_get_options(struct lpuart_port *sport, int *baud, baud_raw = uartclk / (16 * sbr); if (*baud != baud_raw) - printk(KERN_INFO "Serial: Console lpuart rounded baud rate" + dev_info(sport->port.dev, "Serial: Console lpuart rounded baud rate" "from %d to %d\n", baud_raw, *baud); } -- cgit v1.2.3-55-g7522 From f5a9e5f7dd8c5ff0c32e6f86d56955aab64d6057 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 4 Jun 2019 00:31:39 -0300 Subject: serial: imx: Use dev_info() instead of pr_info() dev_info() is more appropriate for printing messages inside drivers, so switch to dev_info(). Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 8b752e895053..d8eadab02446 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -2015,7 +2015,7 @@ imx_uart_console_get_options(struct imx_port *sport, int *baud, } if (*baud != baud_raw) - pr_info("Console IMX rounded baud rate from %d to %d\n", + dev_info(sport->port.dev, "Console IMX rounded baud rate from %d to %d\n", baud_raw, *baud); } } -- cgit v1.2.3-55-g7522 From 87bb008c77a326855def246bf877e651d1fa74aa Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 5 Jun 2019 10:51:42 +0200 Subject: tty: serial: 8250-of: Do not warn on deferred probe Deferred probe is an expected return value for clk_get() on many platforms. The driver deals with it properly, so there's no need to output a warning that may potentially confuse users. Signed-off-by: Thierry Reding Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_of.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 1308b3d1e17a..0826cfdbd406 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -70,9 +70,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev, /* Get clk rate through clk driver if present */ info->clk = devm_clk_get(&ofdev->dev, NULL); if (IS_ERR(info->clk)) { - dev_warn(&ofdev->dev, - "clk or clock-frequency not defined\n"); ret = PTR_ERR(info->clk); + if (ret != -EPROBE_DEFER) + dev_warn(&ofdev->dev, + "failed to get clock: %d\n", ret); goto err_pmruntime; } -- cgit v1.2.3-55-g7522 From 707aeea13a9c85520262e11899d86df3c4b48262 Mon Sep 17 00:00:00 2001 From: Borut Seljak Date: Fri, 7 Jun 2019 12:53:06 +0200 Subject: serial: stm32: fix a recursive locking in stm32_config_rs485 Remove spin_lock_irqsave in stm32_config_rs485, it cause recursive locking. Already locked in uart_set_rs485_config. Fixes: 1bcda09d291081 ("serial: stm32: add support for RS485 hardware control mode") Signed-off-by: Borut Seljak Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 4c217fb35bcb..7a46d7ba967e 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -105,9 +105,7 @@ static int stm32_config_rs485(struct uart_port *port, struct stm32_usart_config *cfg = &stm32_port->info->cfg; u32 usartdiv, baud, cr1, cr3; bool over8; - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); port->rs485 = *rs485conf; @@ -147,7 +145,6 @@ static int stm32_config_rs485(struct uart_port *port, } stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); - spin_unlock_irqrestore(&port->lock, flags); return 0; } -- cgit v1.2.3-55-g7522 From db1b5bc047b3cadaedab3826bba82c3d9e023c4b Mon Sep 17 00:00:00 2001 From: Rautkoski Kimmo EXT Date: Fri, 24 May 2019 09:19:22 +0000 Subject: serial: 8250: Fix TX interrupt handling condition Interrupt handler checked THRE bit (transmitter holding register empty) in LSR to detect if TX fifo is empty. In case when there is only receive interrupts the TX handling got called because THRE bit in LSR is set when there is no transmission (FIFO empty). TX handling caused TX stop, which in RS-485 half-duplex mode actually resets receiver FIFO. This is not desired during reception because of possible data loss. The fix is to check if THRI is set in IER in addition of the TX fifo status. THRI in IER is set when TX is started and cleared when TX is stopped. This ensures that TX handling is only called when there is really transmission on going and an interrupt for THRE and not when there are only RX interrupts. Signed-off-by: Kimmo Rautkoski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index d2f3310abe54..fdb6fd084386 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1875,7 +1875,8 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) status = serial8250_rx_chars(up, status); } serial8250_modem_status(up); - if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE)) + if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE) && + (up->ier & UART_IER_THRI)) serial8250_tx_chars(up); uart_unlock_and_check_sysrq(port, flags); -- cgit v1.2.3-55-g7522 From ba3684f99f1b25d2a30b6956d02d339d7acb9799 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Mon, 10 Jun 2019 19:23:08 +0200 Subject: tty: serial: msm_serial: avoid system lockup condition The function msm_wait_for_xmitr can be taken with interrupts disabled. In order to avoid a potential system lockup - demonstrated under stress testing conditions on SoC QCS404/5 - make sure we wait for a bounded amount of time. Tested on SoC QCS404. Signed-off-by: Jorge Ramirez-Ortiz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 23833ad952ba..3657a24913fc 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -383,10 +383,14 @@ no_rx: static inline void msm_wait_for_xmitr(struct uart_port *port) { + unsigned int timeout = 500000; + while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) { if (msm_read(port, UART_ISR) & UART_ISR_TX_READY) break; udelay(1); + if (!timeout--) + break; } msm_write(port, UART_CR_CMD_RESET_TX_READY, UART_CR); } -- cgit v1.2.3-55-g7522 From da0f843e6e6ff23790e9d97a05dc555ee2e3fa11 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 13 Jun 2019 08:10:45 +0200 Subject: Revert "serial: stm32: select pinctrl state in each suspend/resume function" This reverts commit c70669ecef4e34af8913108fae4b8a9b1dc63fa1 as it breaks the build. Reported-by: Stephen Rothwell Reported-by: kbuild test robot Cc: Bich Hemon Cc: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 7a46d7ba967e..c6a9293a430f 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -1295,8 +1295,6 @@ static int stm32_serial_suspend(struct device *dev) else stm32_serial_enable_wakeup(port, false); - pinctrl_pm_select_sleep_state(dev); - return 0; } @@ -1304,8 +1302,6 @@ static int stm32_serial_resume(struct device *dev) { struct uart_port *port = dev_get_drvdata(dev); - pinctrl_pm_select_default_state(dev); - if (device_may_wakeup(dev)) stm32_serial_enable_wakeup(port, false); -- cgit v1.2.3-55-g7522 From 4e828c3e09201512be5ee162393f334321f7cf01 Mon Sep 17 00:00:00 2001 From: Sergey Organov Date: Tue, 11 Jun 2019 15:05:24 +0300 Subject: serial: imx: fix locking in set_termios() imx_uart_set_termios() called imx_uart_rts_active(), or imx_uart_rts_inactive() before taking port->port.lock. As a consequence, sport->port.mctrl that these functions modify could have been changed without holding port->port.lock. Moved locking of port->port.lock above the calls to fix the issue. Signed-off-by: Sergey Organov Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index d8eadab02446..52374b37d272 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -383,6 +383,7 @@ static void imx_uart_ucrs_restore(struct imx_port *sport, } #endif +/* called with port.lock taken and irqs caller dependent */ static void imx_uart_rts_active(struct imx_port *sport, u32 *ucr2) { *ucr2 &= ~(UCR2_CTSC | UCR2_CTS); @@ -391,6 +392,7 @@ static void imx_uart_rts_active(struct imx_port *sport, u32 *ucr2) mctrl_gpio_set(sport->gpios, sport->port.mctrl); } +/* called with port.lock taken and irqs caller dependent */ static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2) { *ucr2 &= ~UCR2_CTSC; @@ -400,6 +402,7 @@ static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2) mctrl_gpio_set(sport->gpios, sport->port.mctrl); } +/* called with port.lock taken and irqs caller dependent */ static void imx_uart_rts_auto(struct imx_port *sport, u32 *ucr2) { *ucr2 |= UCR2_CTSC; @@ -1549,6 +1552,16 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, old_csize = CS8; } + del_timer_sync(&sport->timer); + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); + quot = uart_get_divisor(port, baud); + + spin_lock_irqsave(&sport->port.lock, flags); + if ((termios->c_cflag & CSIZE) == CS8) ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS; else @@ -1592,16 +1605,6 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, ucr2 |= UCR2_PROE; } - del_timer_sync(&sport->timer); - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); - quot = uart_get_divisor(port, baud); - - spin_lock_irqsave(&sport->port.lock, flags); - sport->port.read_status_mask = 0; if (termios->c_iflag & INPCK) sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR); -- cgit v1.2.3-55-g7522 From 6bf4e42f1d19de10800f4483b4bb7945aab283cb Mon Sep 17 00:00:00 2001 From: Jay Dolan Date: Tue, 11 Jun 2019 04:47:15 -0700 Subject: serial: 8250: Add support for higher baud rates to Pericom chips The Pericom chips can achieve additional baud rates by programming the sample clock register. The baud rates can be described as 921600 * 16 / (16 - scr) for scr values 5 to 15. The divisor is set to 1 for these baud rates. Adds new quirk for Pericom chips other than the four port chips to use the Signed-off-by: Jay Dolan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 97 +++++++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 18 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index df41397de478..7ba1c3b2381d 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1326,13 +1326,66 @@ static int pci_default_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, board->reg_shift); } - +void +pericom_do_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot, unsigned int quot_frac) +{ + int scr; + int lcr; + int actual_baud; + int tolerance; + + for (scr = 5 ; scr <= 15 ; scr++) { + actual_baud = 921600 * 16 / scr; + tolerance = actual_baud / 50; + + if ((baud < actual_baud + tolerance) && + (baud > actual_baud - tolerance)) { + + lcr = serial_port_in(port, UART_LCR); + serial_port_out(port, UART_LCR, lcr | 0x80); + + serial_port_out(port, UART_DLL, 1); + serial_port_out(port, UART_DLM, 0); + serial_port_out(port, 2, 16 - scr); + serial_port_out(port, UART_LCR, lcr); + return; + } else if (baud > actual_baud) { + break; + } + } + serial8250_do_set_divisor(port, baud, quot, quot_frac); +} static int pci_pericom_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) { unsigned int bar, offset = board->first_offset, maxnr; + bar = FL_GET_BASE(board->flags); + if (board->flags & FL_BASE_BARS) + bar += idx; + else + offset += idx * board->uart_offset; + + + maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >> + (board->reg_shift + 3); + + if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) + return 1; + + port->port.set_divisor = pericom_do_set_divisor; + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +static int pci_pericom_setup_four_at_eight(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset, maxnr; + bar = FL_GET_BASE(board->flags); if (board->flags & FL_BASE_BARS) bar += idx; @@ -1348,6 +1401,8 @@ static int pci_pericom_setup(struct serial_private *priv, if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) return 1; + port->port.set_divisor = pericom_do_set_divisor; + return setup_port(priv, port, bar, offset, board->reg_shift); } @@ -1995,7 +2050,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .device = PCI_DEVICE_ID_PERICOM_PI7C9X7954, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, /* * PLX @@ -2032,107 +2087,113 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_VENDOR_ID_ACCESIO, .device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_VENDOR_ID_ACCESIO, .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_VENDOR_ID_ACCESIO, .device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_VENDOR_ID_ACCESIO, .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_VENDOR_ID_ACCESIO, .device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_VENDOR_ID_ACCESIO, .device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_VENDOR_ID_ACCESIO, .device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S, .device = PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_VENDOR_ID_ACCESIO, .device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_VENDOR_ID_ACCESIO, .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_VENDOR_ID_ACCESIO, .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_VENDOR_ID_ACCESIO, .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_VENDOR_ID_ACCESIO, .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, { .vendor = PCI_VENDOR_ID_ACCESIO, .device = PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_pericom_setup, + .setup = pci_pericom_setup_four_at_eight, }, - /* + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup, + }, /* * SBS Technologies, Inc., PMC-OCTALPRO 232 */ { -- cgit v1.2.3-55-g7522 From ab262666018de6f4e206b021386b93ed0c164316 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Wed, 12 Jun 2019 13:14:38 +0200 Subject: serial: uartps: Use the same dynamic major number for all ports Let kernel to find out major number dynamically for the first device and then reuse it for other instances. This fixes the issue that each uart is registered with a different major number. After the patch: crw------- 1 root root 253, 0 Jun 10 08:31 /dev/ttyPS0 crw--w---- 1 root root 253, 1 Jan 1 1970 /dev/ttyPS1 Fixes: 024ca329bfb9 ("serial: uartps: Register own uart console and driver structures") Signed-off-by: Shubhrajyoti Datta Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index ac3fd58f7092..73ae0cacc961 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -30,13 +30,13 @@ #define CDNS_UART_TTY_NAME "ttyPS" #define CDNS_UART_NAME "xuartps" -#define CDNS_UART_MAJOR 0 /* use dynamic node allocation */ #define CDNS_UART_FIFO_SIZE 64 /* FIFO size */ #define CDNS_UART_REGISTER_SPACE 0x1000 #define TX_TIMEOUT 500000 /* Rx Trigger level */ static int rx_trigger_level = 56; +static int uartps_major; module_param(rx_trigger_level, uint, S_IRUGO); MODULE_PARM_DESC(rx_trigger_level, "Rx trigger level, 1-63 bytes"); @@ -1522,7 +1522,7 @@ static int cdns_uart_probe(struct platform_device *pdev) cdns_uart_uart_driver->owner = THIS_MODULE; cdns_uart_uart_driver->driver_name = driver_name; cdns_uart_uart_driver->dev_name = CDNS_UART_TTY_NAME; - cdns_uart_uart_driver->major = CDNS_UART_MAJOR; + cdns_uart_uart_driver->major = uartps_major; cdns_uart_uart_driver->minor = cdns_uart_data->id; cdns_uart_uart_driver->nr = 1; @@ -1551,6 +1551,7 @@ static int cdns_uart_probe(struct platform_device *pdev) goto err_out_id; } + uartps_major = cdns_uart_uart_driver->tty_driver->major; cdns_uart_data->cdns_uart_driver = cdns_uart_uart_driver; /* -- cgit v1.2.3-55-g7522 From 26d5a3245b3f3ce406277e7584f7ccbbd5ea98bb Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Wed, 12 Jun 2019 13:14:39 +0200 Subject: serial: uartps: Use octal permission for module_param() Octal permission is preffered compare to symbolic one. This patch fixes checkpatch warnings: Symbolic permissions 'S_IRUGO' are not preferred. Consider using octal permissions '0444'. Fixes: 85baf542d54e ("tty: xuartps: support 64 byte FIFO size") Signed-off-by: Nava kishore Manne Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 73ae0cacc961..32cf67598bc3 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -37,12 +37,12 @@ /* Rx Trigger level */ static int rx_trigger_level = 56; static int uartps_major; -module_param(rx_trigger_level, uint, S_IRUGO); +module_param(rx_trigger_level, uint, 0444); MODULE_PARM_DESC(rx_trigger_level, "Rx trigger level, 1-63 bytes"); /* Rx Timeout */ static int rx_timeout = 10; -module_param(rx_timeout, uint, S_IRUGO); +module_param(rx_timeout, uint, 0444); MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); /* Register offsets for the UART. */ -- cgit v1.2.3-55-g7522 From b6415f243919703c97483a29ff918872f66f165c Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Wed, 12 Jun 2019 13:14:40 +0200 Subject: serial: uartps: Fix multiple line dereference Trivial patch which fixes this checkpatch warning: WARNING: Avoid multiple line dereference - prefer 'port->state->xmit.tail' + port->state->xmit.buf[port->state->xmit. + tail], port->membase + CDNS_UART_FIFO); Fixes: c8dbdc842d30 ("serial: xuartps: Rewrite the interrupt handling logic") Signed-off-by: Nava kishore Manne Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 32cf67598bc3..2dff879f988e 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -321,8 +321,8 @@ static void cdns_uart_handle_tx(void *dev_id) * register. */ writel( - port->state->xmit.buf[port->state->xmit. - tail], port->membase + CDNS_UART_FIFO); + port->state->xmit.buf[port->state->xmit.tail], + port->membase + CDNS_UART_FIFO); port->icount.tx++; -- cgit v1.2.3-55-g7522 From a53f82d6ccb5199ba4f3b54ae3ff0298ffe1c91d Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Wed, 12 Jun 2019 13:14:41 +0200 Subject: serial: uartps: Fix long line over 80 chars Trivial patch which fixes one checkpatch warning: WARNING: line over 80 characters + !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) { Fixes: c8dbdc842d30 ("serial: xuartps: Rewrite the interrupt handling logic") Signed-off-by: Nava kishore Manne Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 2dff879f988e..69ed81d1338b 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -314,7 +314,8 @@ static void cdns_uart_handle_tx(void *dev_id) } else { numbytes = port->fifosize; while (numbytes && !uart_circ_empty(&port->state->xmit) && - !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) { + !(readl(port->membase + CDNS_UART_SR) & + CDNS_UART_SR_TXFULL)) { /* * Get the data from the UART circular buffer * and write it to the cdns_uart's TX_FIFO -- cgit v1.2.3-55-g7522 From 7be7a0145f01393e487aef8123902e08e7286a45 Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Wed, 12 Jun 2019 13:14:42 +0200 Subject: serial: uartps: Do not add a trailing semicolon to macro This patch fixes this checkpatch warning: WARNING: macros should not use a trailing semicolon +#define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \ + clk_rate_change_nb); Fixes: d9bb3fb12685 ("tty: xuartps: Rebrand driver as Cadence UART") Signed-off-by: Nava kishore Manne Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 69ed81d1338b..e6f23fbf077a 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -201,7 +201,7 @@ struct cdns_platform_data { u32 quirks; }; #define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \ - clk_rate_change_nb); + clk_rate_change_nb) /** * cdns_uart_handle_rx - Handle the received bytes along with Rx errors. -- cgit v1.2.3-55-g7522 From 8d53935d9beb4b9b51fdae9296e4a7840214a64a Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Wed, 12 Jun 2019 13:14:43 +0200 Subject: serial: uartps: Remove useless return from cdns_uart_poll_put_char There is no reason to call return at the end of function which should return void. The patch is also remove one checkpatch warning: WARNING: void function return statements are not generally useful + return; +} Fixes: 6ee04c6c5488 ("tty: xuartps: Add polled mode support for xuartps") Signed-off-by: Nava kishore Manne Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index e6f23fbf077a..f145946f659b 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1079,8 +1079,6 @@ static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) cpu_relax(); spin_unlock_irqrestore(&port->lock, flags); - - return; } #endif -- cgit v1.2.3-55-g7522 From 099f79c019993a7c6071942e9d873f7298f3af7f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 11 Jun 2019 10:03:39 -0300 Subject: serial: fsl_lpuart: Remove unneeded OOM error message There is no need to add an out-of-memory error message inside the driver because the core MM code will take care of it. Remove the unneeded OOM error message. Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 08b52cca650c..2f24d9796d20 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1040,10 +1040,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) sport->rx_dma_rng_buf_len = 16; ring->buf = kmalloc(sport->rx_dma_rng_buf_len, GFP_ATOMIC); - if (!ring->buf) { - dev_err(sport->port.dev, "Ring buf alloc failed\n"); + if (!ring->buf) return -ENOMEM; - } sg_init_one(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len); sg_set_buf(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len); -- cgit v1.2.3-55-g7522 From 7e267b2956f122b3b86c09447e8791727f9d2624 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 17 Jun 2019 16:53:20 +0300 Subject: serial: 8250: factor out serial8250_{set,clear}_THRI() helpers Factor out similar code pieces that set or clear UART_IER_THRI bit to serial8250_{set,clear}_THRI() helpers. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.h | 18 ++++++++++++++++++ drivers/tty/serial/8250/8250_dma.c | 11 +++-------- drivers/tty/serial/8250/8250_omap.c | 14 +++----------- drivers/tty/serial/8250/8250_port.c | 10 ++-------- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index ebfb0bd5bef5..93756ea540d6 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -128,6 +128,24 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value) up->dl_write(up, value); } +static inline bool serial8250_set_THRI(struct uart_8250_port *up) +{ + if (up->ier & UART_IER_THRI) + return false; + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + return true; +} + +static inline bool serial8250_clear_THRI(struct uart_8250_port *up) +{ + if (!(up->ier & UART_IER_THRI)) + return false; + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + return true; +} + struct uart_8250_port *serial8250_get_port(int line); void serial8250_rpm_get(struct uart_8250_port *p); diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index bfa1a857f3ff..890fa7ddaa7f 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -34,10 +34,8 @@ static void __dma_tx_complete(void *param) uart_write_wakeup(&p->port); ret = serial8250_tx_dma(p); - if (ret) { - p->ier |= UART_IER_THRI; - serial_port_out(&p->port, UART_IER, p->ier); - } + if (ret) + serial8250_set_THRI(p); spin_unlock_irqrestore(&p->port.lock, flags); } @@ -100,10 +98,7 @@ int serial8250_tx_dma(struct uart_8250_port *p) dma_async_issue_pending(dma->txchan); if (dma->tx_err) { dma->tx_err = 0; - if (p->ier & UART_IER_THRI) { - p->ier &= ~UART_IER_THRI; - serial_out(p, UART_IER, p->ier); - } + serial8250_clear_THRI(p); } return 0; err: diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 0a8316632d75..ed25cfc3be13 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -923,15 +923,13 @@ static void omap_8250_dma_tx_complete(void *param) ret = omap_8250_tx_dma(p); if (ret) en_thri = true; - } else if (p->capabilities & UART_CAP_RPM) { en_thri = true; } if (en_thri) { dma->tx_err = 1; - p->ier |= UART_IER_THRI; - serial_port_out(&p->port, UART_IER, p->ier); + serial8250_set_THRI(p); } spin_unlock_irqrestore(&p->port.lock, flags); @@ -959,10 +957,7 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) ret = -EBUSY; goto err; } - if (p->ier & UART_IER_THRI) { - p->ier &= ~UART_IER_THRI; - serial_out(p, UART_IER, p->ier); - } + serial8250_clear_THRI(p); return 0; } @@ -1020,10 +1015,7 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) if (dma->tx_err) dma->tx_err = 0; - if (p->ier & UART_IER_THRI) { - p->ier &= ~UART_IER_THRI; - serial_out(p, UART_IER, p->ier); - } + serial8250_clear_THRI(p); if (skip_byte) serial_out(p, UART_TX, xmit->buf[xmit->tail]); return 0; diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index fdb6fd084386..e0d66891d8df 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1502,11 +1502,8 @@ static void __stop_tx_rs485(struct uart_8250_port *p) static inline void __do_stop_tx(struct uart_8250_port *p) { - if (p->ier & UART_IER_THRI) { - p->ier &= ~UART_IER_THRI; - serial_out(p, UART_IER, p->ier); + if (serial8250_clear_THRI(p)) serial8250_rpm_put_tx(p); - } } static inline void __stop_tx(struct uart_8250_port *p) @@ -1555,10 +1552,7 @@ static inline void __start_tx(struct uart_port *port) if (up->dma && !up->dma->tx_dma(up)) return; - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_port_out(port, UART_IER, up->ier); - + if (serial8250_set_THRI(up)) { if (up->bugs & UART_BUG_TXEN) { unsigned char lsr; -- cgit v1.2.3-55-g7522 From 42912081481c6303d880f315223f725ee813c8f1 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Thu, 13 Jun 2019 17:45:41 +0200 Subject: serial: 8250: Add MSR/MCR TIOCM conversion wrapper functions This patch adds wrapper functions to convert MSR <-> TIOCM and also MCR <-> TIOCM. These functions are used now in serial8250_do_set_mctrl() and serial8250_do_get_mctrl(). Signed-off-by: Stefan Roese Suggested-by: Andy Shevchenko Cc: Andy Shevchenko Cc: Yegor Yefremov Cc: Greg Kroah-Hartman Cc: Giulio Benetti Reviewed-by: Mika Westerberg Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.h | 54 +++++++++++++++++++++++++++++++++++++ drivers/tty/serial/8250/8250_port.c | 25 +++-------------- 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 93756ea540d6..57db8c1689af 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -157,6 +157,60 @@ void serial8250_rpm_put_tx(struct uart_8250_port *p); int serial8250_em485_init(struct uart_8250_port *p); void serial8250_em485_destroy(struct uart_8250_port *p); +/* MCR <-> TIOCM conversion */ +static inline int serial8250_TIOCM_to_MCR(int tiocm) +{ + int mcr = 0; + + if (tiocm & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (tiocm & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (tiocm & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (tiocm & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (tiocm & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + return mcr; +} + +static inline int serial8250_MCR_to_TIOCM(int mcr) +{ + int tiocm = 0; + + if (mcr & UART_MCR_RTS) + tiocm |= TIOCM_RTS; + if (mcr & UART_MCR_DTR) + tiocm |= TIOCM_DTR; + if (mcr & UART_MCR_OUT1) + tiocm |= TIOCM_OUT1; + if (mcr & UART_MCR_OUT2) + tiocm |= TIOCM_OUT2; + if (mcr & UART_MCR_LOOP) + tiocm |= TIOCM_LOOP; + + return tiocm; +} + +/* MSR <-> TIOCM conversion */ +static inline int serial8250_MSR_to_TIOCM(int msr) +{ + int tiocm = 0; + + if (msr & UART_MSR_DCD) + tiocm |= TIOCM_CAR; + if (msr & UART_MSR_RI) + tiocm |= TIOCM_RNG; + if (msr & UART_MSR_DSR) + tiocm |= TIOCM_DSR; + if (msr & UART_MSR_CTS) + tiocm |= TIOCM_CTS; + + return tiocm; +} + static inline void serial8250_out_MCR(struct uart_8250_port *up, int value) { serial_out(up, UART_MCR, value); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index e0d66891d8df..74d81f1701ed 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1939,22 +1939,12 @@ unsigned int serial8250_do_get_mctrl(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); unsigned int status; - unsigned int ret; serial8250_rpm_get(up); status = serial8250_modem_status(up); serial8250_rpm_put(up); - ret = 0; - if (status & UART_MSR_DCD) - ret |= TIOCM_CAR; - if (status & UART_MSR_RI) - ret |= TIOCM_RNG; - if (status & UART_MSR_DSR) - ret |= TIOCM_DSR; - if (status & UART_MSR_CTS) - ret |= TIOCM_CTS; - return ret; + return serial8250_MSR_to_TIOCM(status); } EXPORT_SYMBOL_GPL(serial8250_do_get_mctrl); @@ -1968,18 +1958,9 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port) void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct uart_8250_port *up = up_to_u8250p(port); - unsigned char mcr = 0; + unsigned char mcr; - if (mctrl & TIOCM_RTS) - mcr |= UART_MCR_RTS; - if (mctrl & TIOCM_DTR) - mcr |= UART_MCR_DTR; - if (mctrl & TIOCM_OUT1) - mcr |= UART_MCR_OUT1; - if (mctrl & TIOCM_OUT2) - mcr |= UART_MCR_OUT2; - if (mctrl & TIOCM_LOOP) - mcr |= UART_MCR_LOOP; + mcr = serial8250_TIOCM_to_MCR(mctrl); mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr; -- cgit v1.2.3-55-g7522 From 13b18d35909707571af9539f7731389fbf0feb31 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Wed, 8 May 2019 13:44:41 +0300 Subject: tty: serial_core: Set port active bit in uart_port_activate A bug was introduced by commit b3b576461864 ("tty: serial_core: convert uart_open to use tty_port_open"). It caused a constant warning printed into the system log regarding the tty and port counter mismatch: [ 21.644197] ttyS ttySx: tty_port_close_start: tty->count = 1 port count = 2 in case if session hangup was detected so the warning is printed starting from the second open-close iteration. Particularly the problem was discovered in situation when there is a serial tty device without hardware back-end being setup. It is considered by the tty-serial subsystems as a hardware problem with session hang up. In this case uart_startup() will return a positive value with TTY_IO_ERROR flag set in corresponding tty_struct instance. The same value will get passed to be returned from the activate() callback and then being returned from tty_port_open(). But since in this case tty_port_block_til_ready() isn't called the TTY_PORT_ACTIVE flag isn't set (while the method had been called before tty_port_open conversion was introduced and the rest of the subsystem code expected the bit being set in this case), which prevents the uart_hangup() method to perform any cleanups including the tty port counter setting to zero. So the next attempt to open/close the tty device will discover the counters mismatch. In order to fix the problem we need to manually set the TTY_PORT_ACTIVE flag in case if uart_startup() returned a positive value. In this case the hang up procedure will perform a full set of cleanup actions including the port ref-counter resetting. Fixes: b3b576461864 "tty: serial_core: convert uart_open to use tty_port_open" Signed-off-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 83f4dd0bfd74..4223cb496764 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1777,6 +1777,7 @@ static int uart_port_activate(struct tty_port *port, struct tty_struct *tty) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport; + int ret; uport = uart_port_check(state); if (!uport || uport->flags & UPF_DEAD) @@ -1787,7 +1788,11 @@ static int uart_port_activate(struct tty_port *port, struct tty_struct *tty) /* * Start up the serial port. */ - return uart_startup(tty, state, 0); + ret = uart_startup(tty, state, 0); + if (ret > 0) + tty_port_set_active(port, 1); + + return ret; } static const char *uart_type(struct uart_port *port) -- cgit v1.2.3-55-g7522 From 607ea69d2621b08b03a5c87de099be68a4e2bcf7 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Tue, 18 Jun 2019 19:23:51 +0800 Subject: serial: 8250: pericom_do_set_divisor can be static Fixes: 6bf4e42f1d19 ("serial: 8250: Add support for higher baud rates to Pericom chips") Signed-off-by: kbuild test robot Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 7ba1c3b2381d..7f740b37700b 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1326,7 +1326,7 @@ static int pci_default_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, board->reg_shift); } -void +static void pericom_do_set_divisor(struct uart_port *port, unsigned int baud, unsigned int quot, unsigned int quot_frac) { -- cgit v1.2.3-55-g7522 From d99482673f950817b30caf3fcdfb31179b050ce1 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Thu, 20 Jun 2019 08:24:19 +0200 Subject: serial: mctrl_gpio: Check if GPIO property exisits before requesting it This patch adds a check for the GPIOs property existence, before the GPIO is requested. This fixes an issue seen when the 8250 mctrl_gpio support is added (2nd patch in this patch series) on x86 platforms using ACPI. Here Mika's comments from 2016-08-09: " I noticed that with v4.8-rc1 serial console of some of our Broxton systems does not work properly anymore. I'm able to see output but input does not work. I bisected it down to commit 4ef03d328769eddbfeca1f1c958fdb181a69c341 ("tty/serial/8250: use mctrl_gpio helpers"). The reason why it fails is that in ACPI we do not have names for GPIOs (except when _DSD is used) so we use the "idx" to index into _CRS GPIO resources. Now mctrl_gpio_init_noauto() goes through a list of GPIOs calling devm_gpiod_get_index_optional() passing "idx" of 0 for each. The UART device in Broxton has following (simplified) ACPI description: Device (URT4) { ... Name (_CRS, ResourceTemplate () { GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\_SB.GPO0", 0x00, ResourceConsumer) { 0x003A } GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\_SB.GPO0", 0x00, ResourceConsumer) { 0x003D } }) In this case it finds the first GPIO (0x003A which happens to be RX pin for that UART), turns it into GPIO which then breaks input for the UART device. This also breaks systems with bluetooth connected to UART (those typically have some GPIOs in their _CRS). Any ideas how to fix this? We cannot just drop the _CRS index lookup fallback because that would break many existing machines out there so maybe we can limit this to only DT enabled machines. Or alternatively probe if the property first exists before trying to acquire the GPIOs (using device_property_present()). " This patch implements the fix suggested by Mika in his statement above. Signed-off-by: Stefan Roese Reviewed-by: Mika Westerberg Reviewed-by: Andy Shevchenko Tested-by: Yegor Yefremov Cc: Mika Westerberg Cc: Andy Shevchenko Cc: Yegor Yefremov Cc: Greg Kroah-Hartman Cc: Giulio Benetti Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_mctrl_gpio.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 39ed56214cd3..2b400189be91 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "serial_mctrl_gpio.h" @@ -116,6 +117,19 @@ struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx) for (i = 0; i < UART_GPIO_MAX; i++) { enum gpiod_flags flags; + char *gpio_str; + bool present; + + /* Check if GPIO property exists and continue if not */ + gpio_str = kasprintf(GFP_KERNEL, "%s-gpios", + mctrl_gpios_desc[i].name); + if (!gpio_str) + continue; + + present = device_property_present(dev, gpio_str); + kfree(gpio_str); + if (!present) + continue; if (mctrl_gpios_desc[i].dir_out) flags = GPIOD_OUT_LOW; -- cgit v1.2.3-55-g7522 From 4a96895f74c9633b51427fd080ab70fa62b65bc4 Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Thu, 20 Jun 2019 08:24:20 +0200 Subject: tty/serial/8250: use mctrl_gpio helpers This patch permits the usage for GPIOs to control the CTS/RTS/DTR/DSR/DCD/RI signals. Changed by Stefan: Only call mctrl_gpio_init(), if the device has no ACPI companion device to not break existing ACPI based systems. Also only use the mctrl_gpio_ functions when "gpios" is available. Use MSR / MCR <-> TIOCM wrapper functions. Signed-off-by: Yegor Yefremov Signed-off-by: Stefan Roese Reviewed-by: Andy Shevchenko Reviewed-by: Mika Westerberg Tested-by: Yegor Yefremov Cc: Mika Westerberg Cc: Andy Shevchenko Cc: Giulio Benetti Cc: Yegor Yefremov Cc: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/8250.txt | 19 +++++++++++++++ drivers/tty/serial/8250/8250.h | 18 +++++++++++++- drivers/tty/serial/8250/8250_core.c | 17 +++++++++++++ drivers/tty/serial/8250/8250_omap.c | 29 +++++++++++++---------- drivers/tty/serial/8250/8250_port.c | 11 ++++++++- drivers/tty/serial/8250/Kconfig | 1 + include/linux/serial_8250.h | 1 + 7 files changed, 81 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/serial/8250.txt b/Documentation/devicetree/bindings/serial/8250.txt index 3cba12f855b7..20d351f268ef 100644 --- a/Documentation/devicetree/bindings/serial/8250.txt +++ b/Documentation/devicetree/bindings/serial/8250.txt @@ -53,6 +53,9 @@ Optional properties: programmable TX FIFO thresholds. - resets : phandle + reset specifier pairs - overrun-throttle-ms : how long to pause uart rx when input overrun is encountered. +- {rts,cts,dtr,dsr,rng,dcd}-gpios: specify a GPIO for RTS/CTS/DTR/DSR/RI/DCD + line respectively. It will use specified GPIO instead of the peripheral + function pin for the UART feature. If unsure, don't specify this property. Note: * fsl,ns16550: @@ -74,3 +77,19 @@ Example: interrupts = <10>; reg-shift = <2>; }; + +Example for OMAP UART using GPIO-based modem control signals: + + uart4: serial@49042000 { + compatible = "ti,omap3-uart"; + reg = <0x49042000 0x400>; + interrupts = <80>; + ti,hwmods = "uart4"; + clock-frequency = <48000000>; + cts-gpios = <&gpio3 5 GPIO_ACTIVE_LOW>; + rts-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>; + dtr-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>; + dsr-gpios = <&gpio1 13 GPIO_ACTIVE_LOW>; + dcd-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; + rng-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; + }; diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 57db8c1689af..33ad9d6de532 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -11,6 +11,8 @@ #include #include +#include "../serial_mctrl_gpio.h" + struct uart_8250_dma { int (*tx_dma)(struct uart_8250_port *p); int (*rx_dma)(struct uart_8250_port *p); @@ -214,11 +216,25 @@ static inline int serial8250_MSR_to_TIOCM(int msr) static inline void serial8250_out_MCR(struct uart_8250_port *up, int value) { serial_out(up, UART_MCR, value); + + if (up->gpios) + mctrl_gpio_set(up->gpios, serial8250_MCR_to_TIOCM(value)); } static inline int serial8250_in_MCR(struct uart_8250_port *up) { - return serial_in(up, UART_MCR); + int mctrl; + + mctrl = serial_in(up, UART_MCR); + + if (up->gpios) { + unsigned int mctrl_gpio = 0; + + mctrl_gpio = mctrl_gpio_get_outputs(up->gpios, &mctrl_gpio); + mctrl |= serial8250_TIOCM_to_MCR(mctrl_gpio); + } + + return mctrl; } #if defined(__alpha__) && !defined(CONFIG_PCI) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index e441221e04b9..a4470771005f 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -14,6 +14,7 @@ * serial8250_register_8250_port() ports */ +#include #include #include #include @@ -982,6 +983,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart = serial8250_find_match_or_unused(&up->port); if (uart && uart->port.type != PORT_8250_CIR) { + struct mctrl_gpios *gpios; + if (uart->port.dev) uart_remove_one_port(&serial8250_reg, &uart->port); @@ -1016,6 +1019,20 @@ int serial8250_register_8250_port(struct uart_8250_port *up) if (up->port.flags & UPF_FIXED_TYPE) uart->port.type = up->port.type; + /* + * Only call mctrl_gpio_init(), if the device has no ACPI + * companion device + */ + if (!has_acpi_companion(uart->port.dev)) { + gpios = mctrl_gpio_init(&uart->port, 0); + if (IS_ERR(gpios)) { + if (PTR_ERR(gpios) != -ENOSYS) + return PTR_ERR(gpios); + } else { + uart->gpios = gpios; + } + } + serial8250_set_defaults(uart); /* Possibly override default I/O functions. */ diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index ed25cfc3be13..3ef65cbd2478 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -141,18 +141,20 @@ static void omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl) serial8250_do_set_mctrl(port, mctrl); - /* - * Turn off autoRTS if RTS is lowered and restore autoRTS setting - * if RTS is raised - */ - lcr = serial_in(up, UART_LCR); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS)) - priv->efr |= UART_EFR_RTS; - else - priv->efr &= ~UART_EFR_RTS; - serial_out(up, UART_EFR, priv->efr); - serial_out(up, UART_LCR, lcr); + if (!up->gpios) { + /* + * Turn off autoRTS if RTS is lowered and restore autoRTS + * setting if RTS is raised + */ + lcr = serial_in(up, UART_LCR); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS)) + priv->efr |= UART_EFR_RTS; + else + priv->efr &= ~UART_EFR_RTS; + serial_out(up, UART_EFR, priv->efr); + serial_out(up, UART_LCR, lcr); + } } /* @@ -453,7 +455,8 @@ static void omap_8250_set_termios(struct uart_port *port, priv->efr = 0; up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF); - if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { + if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW && + !up->gpios) { /* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */ up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS; priv->efr |= UART_EFR_CTS; diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 74d81f1701ed..a6fabc7e3b13 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1656,6 +1656,8 @@ static void serial8250_disable_ms(struct uart_port *port) if (up->bugs & UART_BUG_NOMSR) return; + mctrl_gpio_disable_ms(up->gpios); + up->ier &= ~UART_IER_MSI; serial_port_out(port, UART_IER, up->ier); } @@ -1668,6 +1670,8 @@ static void serial8250_enable_ms(struct uart_port *port) if (up->bugs & UART_BUG_NOMSR) return; + mctrl_gpio_enable_ms(up->gpios); + up->ier |= UART_IER_MSI; serial8250_rpm_get(up); @@ -1939,12 +1943,17 @@ unsigned int serial8250_do_get_mctrl(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); unsigned int status; + unsigned int val; serial8250_rpm_get(up); status = serial8250_modem_status(up); serial8250_rpm_put(up); - return serial8250_MSR_to_TIOCM(status); + val = serial8250_MSR_to_TIOCM(status); + if (up->gpios) + return mctrl_gpio_get(up->gpios, &val); + + return val; } EXPORT_SYMBOL_GPL(serial8250_do_get_mctrl); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 296115f6a4d8..509f6a3bb9ff 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -8,6 +8,7 @@ config SERIAL_8250 tristate "8250/16550 and compatible serial support" depends on !S390 select SERIAL_CORE + select SERIAL_MCTRL_GPIO if GPIOLIB ---help--- This selects whether you want to include the driver for the standard serial ports. The standard answer is Y. People who might say N diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 5e0b59422a68..bb2bc99388ca 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -110,6 +110,7 @@ struct uart_8250_port { * if no_console_suspend */ unsigned char probe; + struct mctrl_gpios *gpios; #define UART_PROBE_RSA (1 << 0) /* -- cgit v1.2.3-55-g7522 From 3f2640ed7be838c3f05c0d2b0f7c7508e7431e48 Mon Sep 17 00:00:00 2001 From: Oliver Barta Date: Wed, 19 Jun 2019 10:16:39 +0200 Subject: Revert "serial: 8250: Don't service RX FIFO if interrupts are disabled" This reverts commit 2e9fe539108320820016f78ca7704a7342788380. Reading LSR unconditionally but processing the error flags only if UART_IIR_RDI bit was set before in IIR may lead to a loss of transmission error information on UARTs where the transmission error flags are cleared by a read of LSR. Information are lost in case an error is detected right before the read of LSR while processing e.g. an UART_IIR_THRI interrupt. Signed-off-by: Oliver Barta Reviewed-by: Andy Shevchenko Fixes: 2e9fe5391083 ("serial: 8250: Don't service RX FIFO if interrupts are disabled") Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index a6fabc7e3b13..c1cec808571b 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1867,8 +1867,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) status = serial_port_in(port, UART_LSR); - if (status & (UART_LSR_DR | UART_LSR_BI) && - iir & UART_IIR_RDI) { + if (status & (UART_LSR_DR | UART_LSR_BI)) { if (!up->dma || handle_rx_dma(up, iir)) status = serial8250_rx_chars(up, status); } -- cgit v1.2.3-55-g7522 From 4cc0ed626f2ba36cd008d4b380613f0360133891 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Tue, 18 Jun 2019 12:02:22 +0200 Subject: serial: stm32: add support of timeout interrupt for RX Add support of RX timeout interrupts to limit the number of interrupts. RX timeout is a number of bits (baud clock cycles) without transmission seen in the receiver. One character is used as an arbitrary RX timeout value. If parity is enabled, the number of bits has to include parity bit. Signed-off-by: Gerald Baeza Signed-off-by: Fabrice Gasnier Signed-off-by: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 31 +++++++++++++++++++++++++------ drivers/tty/serial/stm32-usart.h | 1 + 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index c6a9293a430f..2e75b586e5bf 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -434,6 +434,10 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) sr = readl_relaxed(port->membase + ofs->isr); + if ((sr & USART_SR_RTOF) && ofs->icr != UNDEF_REG) + writel_relaxed(USART_ICR_RTOCF, + port->membase + ofs->icr); + if ((sr & USART_SR_WUF) && (ofs->icr != UNDEF_REG)) writel_relaxed(USART_ICR_WUCF, port->membase + ofs->icr); @@ -520,7 +524,7 @@ static void stm32_throttle(struct uart_port *port) unsigned long flags; spin_lock_irqsave(&port->lock, flags); - stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE); + stm32_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); spin_unlock_irqrestore(&port->lock, flags); } @@ -532,7 +536,7 @@ static void stm32_unthrottle(struct uart_port *port) unsigned long flags; spin_lock_irqsave(&port->lock, flags); - stm32_set_bits(port, ofs->cr1, USART_CR1_RXNEIE); + stm32_set_bits(port, ofs->cr1, stm32_port->cr1_irq); spin_unlock_irqrestore(&port->lock, flags); } @@ -542,7 +546,7 @@ static void stm32_stop_rx(struct uart_port *port) struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE); + stm32_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); } /* Handle breaks - ignored by us */ @@ -564,7 +568,7 @@ static int stm32_startup(struct uart_port *port) if (ret) return ret; - val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; + val = stm32_port->cr1_irq | USART_CR1_TE | USART_CR1_RE; if (stm32_port->fifoen) val |= USART_CR1_FIFOEN; stm32_set_bits(port, ofs->cr1, val); @@ -580,7 +584,8 @@ static void stm32_shutdown(struct uart_port *port) u32 val, isr; int ret; - val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; + val = USART_CR1_TXEIE | USART_CR1_TE; + val |= stm32_port->cr1_irq | USART_CR1_RE; val |= BIT(cfg->uart_enable_bit); if (stm32_port->fifoen) val |= USART_CR1_FIFOEN; @@ -650,7 +655,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, /* Stop serial port and reset value */ writel_relaxed(0, port->membase + ofs->cr1); - cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; + cr1 = USART_CR1_TE | USART_CR1_RE; if (stm32_port->fifoen) cr1 |= USART_CR1_FIFOEN; @@ -683,6 +688,19 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, dev_dbg(port->dev, "Unsupported data bits config: %u bits\n" , bits); + if (ofs->rtor != UNDEF_REG && (stm32_port->rx_ch || + stm32_port->fifoen)) { + if (cflag & CSTOPB) + bits = bits + 3; /* 1 start bit + 2 stop bits */ + else + bits = bits + 2; /* 1 start bit + 1 stop bit */ + + /* RX timeout irq to occur after last stop bit + bits */ + stm32_port->cr1_irq = USART_CR1_RTOIE; + writel_relaxed(bits, port->membase + ofs->rtor); + cr2 |= USART_CR2_RTOEN; + } + if (cflag & PARODD) cr1 |= USART_CR1_PS; @@ -922,6 +940,7 @@ static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev) stm32_ports[id].hw_flow_control = of_property_read_bool(np, "st,hw-flow-ctrl"); stm32_ports[id].port.line = id; + stm32_ports[id].cr1_irq = USART_CR1_RXNEIE; stm32_ports[id].last_res = RX_BUF_L; return &stm32_ports[id]; } diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index 30d2433e27c3..fcd01feeabec 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -249,6 +249,7 @@ struct stm32_port { struct dma_chan *tx_ch; /* dma tx channel */ dma_addr_t tx_dma_buf; /* dma tx buffer bus address */ unsigned char *tx_buf; /* dma tx buffer cpu address */ + u32 cr1_irq; /* USART_CR1_RXNEIE or RTOIE */ int last_res; bool tx_dma_busy; /* dma tx busy */ bool hw_flow_control; -- cgit v1.2.3-55-g7522 From 5d9176edacc29cadb9a0b52c8aaba67ce5ff4ed4 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Tue, 18 Jun 2019 12:02:23 +0200 Subject: serial: stm32: update PIO transmission Improves PIO transmission: - Replaces the FIFO filling per character by a filling per blocks of characters, which provides better performances - Replaces the active waiting loop by TX empty interrupt dynamic handling. TXE interrupt is now enabled when data has to be sent (ie when uart_circ is not empty), and inhibited when there is no more data to send (ie when uart_circ is empty). Signed-off-by: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 2e75b586e5bf..2085f10aabfa 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -300,27 +300,26 @@ static void stm32_transmit_chars_pio(struct uart_port *port) struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct circ_buf *xmit = &port->state->xmit; - unsigned int isr; - int ret; if (stm32_port->tx_dma_busy) { stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); stm32_port->tx_dma_busy = false; } - ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, - isr, - (isr & USART_SR_TXE), - 10, 100000); - - if (ret) - dev_err(port->dev, "tx empty not set\n"); - - stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE); + while (!uart_circ_empty(xmit)) { + /* Check that TDR is empty before filling FIFO */ + if (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE)) + break; + writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } - writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; + /* rely on TXE irq (mask or unmask) for sending remaining data */ + if (uart_circ_empty(xmit)) + stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); + else + stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE); } static void stm32_transmit_chars_dma(struct uart_port *port) -- cgit v1.2.3-55-g7522 From d075719e62ae7bc7fed3ff5be365c8c384483f15 Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Tue, 18 Jun 2019 12:02:24 +0200 Subject: serial: stm32: add support of TX FIFO threshold Adds the support of TX FIFO threshold in order to improve the TX FIFO management: - TX FIFO threshold irq enabling (instead of relying on tx empty / fifo not full irq that generates one irq/char) - TXCFG is set to half fifo size (e.g. 16/2 = 8 data for a 16 data depth FIFO) - irq rate may be reduced by up to 1/TXCFG, e.g. 1 over 8 with current TXCFG setting. Signed-off-by: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 51 +++++++++++++++++++++++++++++++++------- drivers/tty/serial/stm32-usart.h | 13 ++++++++++ 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 2085f10aabfa..959f33bb0eb6 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -295,6 +295,32 @@ static void stm32_tx_dma_complete(void *arg) stm32_transmit_chars(port); } +static void stm32_tx_interrupt_enable(struct uart_port *port) +{ + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + /* + * Enables TX FIFO threashold irq when FIFO is enabled, + * or TX empty irq when FIFO is disabled + */ + if (stm32_port->fifoen) + stm32_set_bits(port, ofs->cr3, USART_CR3_TXFTIE); + else + stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE); +} + +static void stm32_tx_interrupt_disable(struct uart_port *port) +{ + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + if (stm32_port->fifoen) + stm32_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE); + else + stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); +} + static void stm32_transmit_chars_pio(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); @@ -317,9 +343,9 @@ static void stm32_transmit_chars_pio(struct uart_port *port) /* rely on TXE irq (mask or unmask) for sending remaining data */ if (uart_circ_empty(xmit)) - stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); + stm32_tx_interrupt_disable(port); else - stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE); + stm32_tx_interrupt_enable(port); } static void stm32_transmit_chars_dma(struct uart_port *port) @@ -401,7 +427,7 @@ static void stm32_transmit_chars(struct uart_port *port) } if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); + stm32_tx_interrupt_disable(port); return; } @@ -419,7 +445,7 @@ static void stm32_transmit_chars(struct uart_port *port) uart_write_wakeup(port); if (uart_circ_empty(xmit)) - stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); + stm32_tx_interrupt_disable(port); } static irqreturn_t stm32_interrupt(int irq, void *ptr) @@ -498,10 +524,7 @@ static unsigned int stm32_get_mctrl(struct uart_port *port) /* Transmit stop */ static void stm32_stop_tx(struct uart_port *port) { - struct stm32_port *stm32_port = to_stm32_port(port); - struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - - stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); + stm32_tx_interrupt_disable(port); } /* There are probably characters waiting to be transmitted. */ @@ -572,6 +595,13 @@ static int stm32_startup(struct uart_port *port) val |= USART_CR1_FIFOEN; stm32_set_bits(port, ofs->cr1, val); + if (stm32_port->fifoen) { + val = readl_relaxed(port->membase + ofs->cr3); + val &= ~USART_CR3_TXFTCFG_MASK; + val |= USART_CR3_TXFTCFG_HALF << USART_CR3_TXFTCFG_SHIFT; + writel_relaxed(val, port->membase + ofs->cr3); + } + return 0; } @@ -659,7 +689,9 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if (stm32_port->fifoen) cr1 |= USART_CR1_FIFOEN; cr2 = 0; - cr3 = 0; + cr3 = readl_relaxed(port->membase + ofs->cr3); + cr3 &= USART_CR3_TXFTIE | USART_CR3_RXFTCFG | USART_CR3_RXFTIE + | USART_CR3_TXFTCFG_MASK; if (cflag & CSTOPB) cr2 |= USART_CR2_STOP_2B; @@ -866,6 +898,7 @@ static int stm32_init_port(struct stm32_port *stm32port, port->flags = UPF_BOOT_AUTOCONF; port->ops = &stm32_uart_ops; port->dev = &pdev->dev; + port->fifosize = stm32port->info->cfg.fifosize; ret = platform_get_irq(pdev, 0); if (ret <= 0) { diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index fcd01feeabec..a5984463e113 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -27,6 +27,7 @@ struct stm32_usart_config { bool has_7bits_data; bool has_wakeup; bool has_fifo; + int fifosize; }; struct stm32_usart_info { @@ -54,6 +55,7 @@ struct stm32_usart_info stm32f4_info = { .cfg = { .uart_enable_bit = 13, .has_7bits_data = false, + .fifosize = 1, } }; @@ -74,6 +76,7 @@ struct stm32_usart_info stm32f7_info = { .cfg = { .uart_enable_bit = 0, .has_7bits_data = true, + .fifosize = 1, } }; @@ -96,6 +99,7 @@ struct stm32_usart_info stm32h7_info = { .has_7bits_data = true, .has_wakeup = true, .has_fifo = true, + .fifosize = 16, } }; @@ -204,6 +208,15 @@ struct stm32_usart_info stm32h7_info = { #define USART_CR3_WUS_MASK GENMASK(21, 20) /* H7 */ #define USART_CR3_WUS_START_BIT BIT(21) /* H7 */ #define USART_CR3_WUFIE BIT(22) /* H7 */ +#define USART_CR3_TXFTIE BIT(23) /* H7 */ +#define USART_CR3_TCBGTIE BIT(24) /* H7 */ +#define USART_CR3_RXFTCFG GENMASK(27, 25) /* H7 */ +#define USART_CR3_RXFTIE BIT(28) /* H7 */ +#define USART_CR3_TXFTCFG_MASK GENMASK(31, 29) /* H7 */ +#define USART_CR3_TXFTCFG_SHIFT 29 /* H7 */ + +/* TX FIFO threashold set to half of its depth */ +#define USART_CR3_TXFTCFG_HALF 0x2 /* USART_GTPR */ #define USART_GTPR_PSC_MASK GENMASK(7, 0) -- cgit v1.2.3-55-g7522 From d0a6a7bcc3fa0e97efce1d9a5c4cca6f3474c6bb Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Tue, 18 Jun 2019 12:02:25 +0200 Subject: serial: stm32: add support of RX FIFO threshold Adds the support of RX FIFO threshold in order to improve the RX FIFO management. This is done by enabling fifo threshold interrupt, instead of relying on rx empty/fifo not full irq. That basically generates one irq/char currently. With this patch: - RXCFG is set to half fifo size (e.g. 16/2 = 8 data for a 16 data depth FIFO) - irq rate may be reduced by up to 1/RXCFG, e.g. 1 over 8 with current RXCFG setting. - Receiver timeout is used to gather chars when FIFO threshold isn't reached. Signed-off-by: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 21 +++++++++++++++++++-- drivers/tty/serial/stm32-usart.h | 7 ++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 959f33bb0eb6..fd9fb33ce4fe 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -547,6 +547,9 @@ static void stm32_throttle(struct uart_port *port) spin_lock_irqsave(&port->lock, flags); stm32_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); + if (stm32_port->cr3_irq) + stm32_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); + spin_unlock_irqrestore(&port->lock, flags); } @@ -559,6 +562,9 @@ static void stm32_unthrottle(struct uart_port *port) spin_lock_irqsave(&port->lock, flags); stm32_set_bits(port, ofs->cr1, stm32_port->cr1_irq); + if (stm32_port->cr3_irq) + stm32_set_bits(port, ofs->cr3, stm32_port->cr3_irq); + spin_unlock_irqrestore(&port->lock, flags); } @@ -569,6 +575,9 @@ static void stm32_stop_rx(struct uart_port *port) struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; stm32_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); + if (stm32_port->cr3_irq) + stm32_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); + } /* Handle breaks - ignored by us */ @@ -597,8 +606,9 @@ static int stm32_startup(struct uart_port *port) if (stm32_port->fifoen) { val = readl_relaxed(port->membase + ofs->cr3); - val &= ~USART_CR3_TXFTCFG_MASK; + val &= ~(USART_CR3_TXFTCFG_MASK | USART_CR3_RXFTCFG_MASK); val |= USART_CR3_TXFTCFG_HALF << USART_CR3_TXFTCFG_SHIFT; + val |= USART_CR3_RXFTCFG_HALF << USART_CR3_RXFTCFG_SHIFT; writel_relaxed(val, port->membase + ofs->cr3); } @@ -690,7 +700,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, cr1 |= USART_CR1_FIFOEN; cr2 = 0; cr3 = readl_relaxed(port->membase + ofs->cr3); - cr3 &= USART_CR3_TXFTIE | USART_CR3_RXFTCFG | USART_CR3_RXFTIE + cr3 &= USART_CR3_TXFTIE | USART_CR3_RXFTCFG_MASK | USART_CR3_RXFTIE | USART_CR3_TXFTCFG_MASK; if (cflag & CSTOPB) @@ -730,8 +740,14 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, stm32_port->cr1_irq = USART_CR1_RTOIE; writel_relaxed(bits, port->membase + ofs->rtor); cr2 |= USART_CR2_RTOEN; + /* Not using dma, enable fifo threshold irq */ + if (!stm32_port->rx_ch) + stm32_port->cr3_irq = USART_CR3_RXFTIE; } + cr1 |= stm32_port->cr1_irq; + cr3 |= stm32_port->cr3_irq; + if (cflag & PARODD) cr1 |= USART_CR1_PS; @@ -973,6 +989,7 @@ static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev) "st,hw-flow-ctrl"); stm32_ports[id].port.line = id; stm32_ports[id].cr1_irq = USART_CR1_RXNEIE; + stm32_ports[id].cr3_irq = 0; stm32_ports[id].last_res = RX_BUF_L; return &stm32_ports[id]; } diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index a5984463e113..a175c1094dc8 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -210,7 +210,8 @@ struct stm32_usart_info stm32h7_info = { #define USART_CR3_WUFIE BIT(22) /* H7 */ #define USART_CR3_TXFTIE BIT(23) /* H7 */ #define USART_CR3_TCBGTIE BIT(24) /* H7 */ -#define USART_CR3_RXFTCFG GENMASK(27, 25) /* H7 */ +#define USART_CR3_RXFTCFG_MASK GENMASK(27, 25) /* H7 */ +#define USART_CR3_RXFTCFG_SHIFT 25 /* H7 */ #define USART_CR3_RXFTIE BIT(28) /* H7 */ #define USART_CR3_TXFTCFG_MASK GENMASK(31, 29) /* H7 */ #define USART_CR3_TXFTCFG_SHIFT 29 /* H7 */ @@ -218,6 +219,9 @@ struct stm32_usart_info stm32h7_info = { /* TX FIFO threashold set to half of its depth */ #define USART_CR3_TXFTCFG_HALF 0x2 +/* RX FIFO threashold set to half of its depth */ +#define USART_CR3_RXFTCFG_HALF 0x2 + /* USART_GTPR */ #define USART_GTPR_PSC_MASK GENMASK(7, 0) #define USART_GTPR_GT_MASK GENMASK(15, 8) @@ -263,6 +267,7 @@ struct stm32_port { dma_addr_t tx_dma_buf; /* dma tx buffer bus address */ unsigned char *tx_buf; /* dma tx buffer cpu address */ u32 cr1_irq; /* USART_CR1_RXNEIE or RTOIE */ + u32 cr3_irq; /* USART_CR3_RXFTIE */ int last_res; bool tx_dma_busy; /* dma tx busy */ bool hw_flow_control; -- cgit v1.2.3-55-g7522 From 84872dc448fe0ae11fe8412f4966e9c431a45b8f Mon Sep 17 00:00:00 2001 From: Erwan Le Ray Date: Tue, 18 Jun 2019 12:02:26 +0200 Subject: serial: stm32: add RX and TX FIFO flush Adds a flush of RX and TX FIFOs, and fixes some errors: - adds RX FIFO flush in startup fonction - removes the useless transmitter enabling in startup fonction (e.g. receiver only, see Documentation/serial/driver) - configures FIFO threshold before enabling it, rather than after - flushes both TX and RX in set_termios function Signed-off-by: Erwan Le Ray Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index fd9fb33ce4fe..24a2261f879a 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -599,11 +599,11 @@ static int stm32_startup(struct uart_port *port) if (ret) return ret; - val = stm32_port->cr1_irq | USART_CR1_TE | USART_CR1_RE; - if (stm32_port->fifoen) - val |= USART_CR1_FIFOEN; - stm32_set_bits(port, ofs->cr1, val); + /* RX FIFO Flush */ + if (ofs->rqr != UNDEF_REG) + stm32_set_bits(port, ofs->rqr, USART_RQR_RXFRQ); + /* Tx and RX FIFO configuration */ if (stm32_port->fifoen) { val = readl_relaxed(port->membase + ofs->cr3); val &= ~(USART_CR3_TXFTCFG_MASK | USART_CR3_RXFTCFG_MASK); @@ -612,6 +612,12 @@ static int stm32_startup(struct uart_port *port) writel_relaxed(val, port->membase + ofs->cr3); } + /* RX FIFO enabling */ + val = stm32_port->cr1_irq | USART_CR1_RE; + if (stm32_port->fifoen) + val |= USART_CR1_FIFOEN; + stm32_set_bits(port, ofs->cr1, val); + return 0; } @@ -694,8 +700,12 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, /* Stop serial port and reset value */ writel_relaxed(0, port->membase + ofs->cr1); - cr1 = USART_CR1_TE | USART_CR1_RE; + /* flush RX & TX FIFO */ + if (ofs->rqr != UNDEF_REG) + stm32_set_bits(port, ofs->rqr, + USART_RQR_TXFRQ | USART_RQR_RXFRQ); + cr1 = USART_CR1_TE | USART_CR1_RE; if (stm32_port->fifoen) cr1 |= USART_CR1_FIFOEN; cr2 = 0; -- cgit v1.2.3-55-g7522 From b7a8f766482fa5893ca95cbf83195e60e96e389e Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 3 Jul 2019 05:59:08 +0000 Subject: serial: 8250: 8250_core: Fix missing unlock on error in serial8250_register_8250_port() Add the missing unlock before return from function serial8250_register_8250_port() in the error handling case. Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20190703055908.141294-1-weiyongjun1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index a4470771005f..df3bcc0b2d74 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1026,8 +1026,10 @@ int serial8250_register_8250_port(struct uart_8250_port *up) if (!has_acpi_companion(uart->port.dev)) { gpios = mctrl_gpio_init(&uart->port, 0); if (IS_ERR(gpios)) { - if (PTR_ERR(gpios) != -ENOSYS) - return PTR_ERR(gpios); + if (PTR_ERR(gpios) != -ENOSYS) { + ret = PTR_ERR(gpios); + goto out_unlock; + } } else { uart->gpios = gpios; } @@ -1099,6 +1101,7 @@ int serial8250_register_8250_port(struct uart_8250_port *up) } } +out_unlock: mutex_unlock(&serial_mutex); return ret; -- cgit v1.2.3-55-g7522 From ecd6bf67da3126e8ec731c2dd8cb6c2f17d9563a Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Wed, 26 Jun 2019 09:05:53 -0700 Subject: serial: mpsc: Remove obsolete MPSC driver Support for the Marvell MV64x60 line of bridge chips that contained MPSC controllers has been removed and there are no other components that have that controller so remove its driver. Signed-off-by: Mark Greer Link: https://lore.kernel.org/r/20190626160553.28518-1-mgreer@animalcreek.com Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/devices.txt | 4 +- drivers/tty/serial/Kconfig | 14 - drivers/tty/serial/Makefile | 1 - drivers/tty/serial/mpsc.c | 2138 --------------------------------- include/linux/mv643xx.h | 46 - include/uapi/linux/serial_core.h | 2 +- 6 files changed, 3 insertions(+), 2202 deletions(-) delete mode 100644 drivers/tty/serial/mpsc.c diff --git a/Documentation/admin-guide/devices.txt b/Documentation/admin-guide/devices.txt index 1649117e6087..e56e00655153 100644 --- a/Documentation/admin-guide/devices.txt +++ b/Documentation/admin-guide/devices.txt @@ -2693,8 +2693,8 @@ 41 = /dev/ttySMX0 Motorola i.MX - port 0 42 = /dev/ttySMX1 Motorola i.MX - port 1 43 = /dev/ttySMX2 Motorola i.MX - port 2 - 44 = /dev/ttyMM0 Marvell MPSC - port 0 - 45 = /dev/ttyMM1 Marvell MPSC - port 1 + 44 = /dev/ttyMM0 Marvell MPSC - port 0 (obsolete unused) + 45 = /dev/ttyMM1 Marvell MPSC - port 1 (obsolete unused) 46 = /dev/ttyCPM0 PPC CPM (SCC or SMC) - port 0 ... 47 = /dev/ttyCPM5 PPC CPM (SCC or SMC) - port 5 diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 0d31251e04cc..b416c7b33f49 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -457,20 +457,6 @@ config SERIAL_21285_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) -config SERIAL_MPSC - bool "Marvell MPSC serial port support" - depends on MV64X60 - select SERIAL_CORE - help - Say Y here if you want to use the Marvell MPSC serial controller. - -config SERIAL_MPSC_CONSOLE - bool "Support for console on Marvell MPSC serial port" - depends on SERIAL_MPSC - select SERIAL_CORE_CONSOLE - help - Say Y here if you want to support a serial console on a Marvell MPSC. - config SERIAL_PXA bool "PXA serial port support (DEPRECATED)" depends on ARCH_PXA || ARCH_MMP diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 79c3d513db7e..7cd7cabfa6c4 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -46,7 +46,6 @@ obj-$(CONFIG_SERIAL_CPM) += cpm_uart/ obj-$(CONFIG_SERIAL_IMX) += imx.o obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o obj-$(CONFIG_SERIAL_ICOM) += icom.o -obj-$(CONFIG_SERIAL_MPSC) += mpsc.o obj-$(CONFIG_SERIAL_MESON) += meson_uart.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c deleted file mode 100644 index 1f60d6fe4ff2..000000000000 --- a/drivers/tty/serial/mpsc.c +++ /dev/null @@ -1,2138 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Generic driver for the MPSC (UART mode) on Marvell parts (e.g., GT64240, - * GT64260, MV64340, MV64360, GT96100, ... ). - * - * Author: Mark A. Greer - * - * Based on an old MPSC driver that was in the linuxppc tree. It appears to - * have been created by Chris Zankel (formerly of MontaVista) but there - * is no proper Copyright so I'm not sure. Apparently, parts were also - * taken from PPCBoot (now U-Boot). Also based on drivers/serial/8250.c - * by Russell King. - * - * 2004 (c) MontaVista, Software, Inc. - */ -/* - * The MPSC interface is much like a typical network controller's interface. - * That is, you set up separate rings of descriptors for transmitting and - * receiving data. There is also a pool of buffers with (one buffer per - * descriptor) that incoming data are dma'd into or outgoing data are dma'd - * out of. - * - * The MPSC requires two other controllers to be able to work. The Baud Rate - * Generator (BRG) provides a clock at programmable frequencies which determines - * the baud rate. The Serial DMA Controller (SDMA) takes incoming data from the - * MPSC and DMA's it into memory or DMA's outgoing data and passes it to the - * MPSC. It is actually the SDMA interrupt that the driver uses to keep the - * transmit and receive "engines" going (i.e., indicate data has been - * transmitted or received). - * - * NOTES: - * - * 1) Some chips have an erratum where several regs cannot be - * read. To work around that, we keep a local copy of those regs in - * 'mpsc_port_info'. - * - * 2) Some chips have an erratum where the ctlr will hang when the SDMA ctlr - * accesses system mem with coherency enabled. For that reason, the driver - * assumes that coherency for that ctlr has been disabled. This means - * that when in a cache coherent system, the driver has to manually manage - * the data cache on the areas that it touches because the dma_* macro are - * basically no-ops. - * - * 3) There is an erratum (on PPC) where you can't use the instruction to do - * a DMA_TO_DEVICE/cache clean so DMA_BIDIRECTIONAL/flushes are used in places - * where a DMA_TO_DEVICE/clean would have [otherwise] sufficed. - * - * 4) AFAICT, hardware flow control isn't supported by the controller --MAG. - */ - - -#if defined(CONFIG_SERIAL_MPSC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define MPSC_NUM_CTLRS 2 - -/* - * Descriptors and buffers must be cache line aligned. - * Buffers lengths must be multiple of cache line size. - * Number of Tx & Rx descriptors must be powers of 2. - */ -#define MPSC_RXR_ENTRIES 32 -#define MPSC_RXRE_SIZE dma_get_cache_alignment() -#define MPSC_RXR_SIZE (MPSC_RXR_ENTRIES * MPSC_RXRE_SIZE) -#define MPSC_RXBE_SIZE dma_get_cache_alignment() -#define MPSC_RXB_SIZE (MPSC_RXR_ENTRIES * MPSC_RXBE_SIZE) - -#define MPSC_TXR_ENTRIES 32 -#define MPSC_TXRE_SIZE dma_get_cache_alignment() -#define MPSC_TXR_SIZE (MPSC_TXR_ENTRIES * MPSC_TXRE_SIZE) -#define MPSC_TXBE_SIZE dma_get_cache_alignment() -#define MPSC_TXB_SIZE (MPSC_TXR_ENTRIES * MPSC_TXBE_SIZE) - -#define MPSC_DMA_ALLOC_SIZE (MPSC_RXR_SIZE + MPSC_RXB_SIZE + MPSC_TXR_SIZE \ - + MPSC_TXB_SIZE + dma_get_cache_alignment() /* for alignment */) - -/* Rx and Tx Ring entry descriptors -- assume entry size is <= cacheline size */ -struct mpsc_rx_desc { - u16 bufsize; - u16 bytecnt; - u32 cmdstat; - u32 link; - u32 buf_ptr; -} __attribute((packed)); - -struct mpsc_tx_desc { - u16 bytecnt; - u16 shadow; - u32 cmdstat; - u32 link; - u32 buf_ptr; -} __attribute((packed)); - -/* - * Some regs that have the erratum that you can't read them are are shared - * between the two MPSC controllers. This struct contains those shared regs. - */ -struct mpsc_shared_regs { - phys_addr_t mpsc_routing_base_p; - phys_addr_t sdma_intr_base_p; - - void __iomem *mpsc_routing_base; - void __iomem *sdma_intr_base; - - u32 MPSC_MRR_m; - u32 MPSC_RCRR_m; - u32 MPSC_TCRR_m; - u32 SDMA_INTR_CAUSE_m; - u32 SDMA_INTR_MASK_m; -}; - -/* The main driver data structure */ -struct mpsc_port_info { - struct uart_port port; /* Overlay uart_port structure */ - - /* Internal driver state for this ctlr */ - u8 ready; - u8 rcv_data; - - /* Info passed in from platform */ - u8 mirror_regs; /* Need to mirror regs? */ - u8 cache_mgmt; /* Need manual cache mgmt? */ - u8 brg_can_tune; /* BRG has baud tuning? */ - u32 brg_clk_src; - u16 mpsc_max_idle; - int default_baud; - int default_bits; - int default_parity; - int default_flow; - - /* Physical addresses of various blocks of registers (from platform) */ - phys_addr_t mpsc_base_p; - phys_addr_t sdma_base_p; - phys_addr_t brg_base_p; - - /* Virtual addresses of various blocks of registers (from platform) */ - void __iomem *mpsc_base; - void __iomem *sdma_base; - void __iomem *brg_base; - - /* Descriptor ring and buffer allocations */ - void *dma_region; - dma_addr_t dma_region_p; - - dma_addr_t rxr; /* Rx descriptor ring */ - dma_addr_t rxr_p; /* Phys addr of rxr */ - u8 *rxb; /* Rx Ring I/O buf */ - u8 *rxb_p; /* Phys addr of rxb */ - u32 rxr_posn; /* First desc w/ Rx data */ - - dma_addr_t txr; /* Tx descriptor ring */ - dma_addr_t txr_p; /* Phys addr of txr */ - u8 *txb; /* Tx Ring I/O buf */ - u8 *txb_p; /* Phys addr of txb */ - int txr_head; /* Where new data goes */ - int txr_tail; /* Where sent data comes off */ - spinlock_t tx_lock; /* transmit lock */ - - /* Mirrored values of regs we can't read (if 'mirror_regs' set) */ - u32 MPSC_MPCR_m; - u32 MPSC_CHR_1_m; - u32 MPSC_CHR_2_m; - u32 MPSC_CHR_10_m; - u32 BRG_BCR_m; - struct mpsc_shared_regs *shared_regs; -}; - -/* Hooks to platform-specific code */ -int mpsc_platform_register_driver(void); -void mpsc_platform_unregister_driver(void); - -/* Hooks back in to mpsc common to be called by platform-specific code */ -struct mpsc_port_info *mpsc_device_probe(int index); -struct mpsc_port_info *mpsc_device_remove(int index); - -/* Main MPSC Configuration Register Offsets */ -#define MPSC_MMCRL 0x0000 -#define MPSC_MMCRH 0x0004 -#define MPSC_MPCR 0x0008 -#define MPSC_CHR_1 0x000c -#define MPSC_CHR_2 0x0010 -#define MPSC_CHR_3 0x0014 -#define MPSC_CHR_4 0x0018 -#define MPSC_CHR_5 0x001c -#define MPSC_CHR_6 0x0020 -#define MPSC_CHR_7 0x0024 -#define MPSC_CHR_8 0x0028 -#define MPSC_CHR_9 0x002c -#define MPSC_CHR_10 0x0030 -#define MPSC_CHR_11 0x0034 - -#define MPSC_MPCR_FRZ (1 << 9) -#define MPSC_MPCR_CL_5 0 -#define MPSC_MPCR_CL_6 1 -#define MPSC_MPCR_CL_7 2 -#define MPSC_MPCR_CL_8 3 -#define MPSC_MPCR_SBL_1 0 -#define MPSC_MPCR_SBL_2 1 - -#define MPSC_CHR_2_TEV (1<<1) -#define MPSC_CHR_2_TA (1<<7) -#define MPSC_CHR_2_TTCS (1<<9) -#define MPSC_CHR_2_REV (1<<17) -#define MPSC_CHR_2_RA (1<<23) -#define MPSC_CHR_2_CRD (1<<25) -#define MPSC_CHR_2_EH (1<<31) -#define MPSC_CHR_2_PAR_ODD 0 -#define MPSC_CHR_2_PAR_SPACE 1 -#define MPSC_CHR_2_PAR_EVEN 2 -#define MPSC_CHR_2_PAR_MARK 3 - -/* MPSC Signal Routing */ -#define MPSC_MRR 0x0000 -#define MPSC_RCRR 0x0004 -#define MPSC_TCRR 0x0008 - -/* Serial DMA Controller Interface Registers */ -#define SDMA_SDC 0x0000 -#define SDMA_SDCM 0x0008 -#define SDMA_RX_DESC 0x0800 -#define SDMA_RX_BUF_PTR 0x0808 -#define SDMA_SCRDP 0x0810 -#define SDMA_TX_DESC 0x0c00 -#define SDMA_SCTDP 0x0c10 -#define SDMA_SFTDP 0x0c14 - -#define SDMA_DESC_CMDSTAT_PE (1<<0) -#define SDMA_DESC_CMDSTAT_CDL (1<<1) -#define SDMA_DESC_CMDSTAT_FR (1<<3) -#define SDMA_DESC_CMDSTAT_OR (1<<6) -#define SDMA_DESC_CMDSTAT_BR (1<<9) -#define SDMA_DESC_CMDSTAT_MI (1<<10) -#define SDMA_DESC_CMDSTAT_A (1<<11) -#define SDMA_DESC_CMDSTAT_AM (1<<12) -#define SDMA_DESC_CMDSTAT_CT (1<<13) -#define SDMA_DESC_CMDSTAT_C (1<<14) -#define SDMA_DESC_CMDSTAT_ES (1<<15) -#define SDMA_DESC_CMDSTAT_L (1<<16) -#define SDMA_DESC_CMDSTAT_F (1<<17) -#define SDMA_DESC_CMDSTAT_P (1<<18) -#define SDMA_DESC_CMDSTAT_EI (1<<23) -#define SDMA_DESC_CMDSTAT_O (1<<31) - -#define SDMA_DESC_DFLT (SDMA_DESC_CMDSTAT_O \ - | SDMA_DESC_CMDSTAT_EI) - -#define SDMA_SDC_RFT (1<<0) -#define SDMA_SDC_SFM (1<<1) -#define SDMA_SDC_BLMR (1<<6) -#define SDMA_SDC_BLMT (1<<7) -#define SDMA_SDC_POVR (1<<8) -#define SDMA_SDC_RIFB (1<<9) - -#define SDMA_SDCM_ERD (1<<7) -#define SDMA_SDCM_AR (1<<15) -#define SDMA_SDCM_STD (1<<16) -#define SDMA_SDCM_TXD (1<<23) -#define SDMA_SDCM_AT (1<<31) - -#define SDMA_0_CAUSE_RXBUF (1<<0) -#define SDMA_0_CAUSE_RXERR (1<<1) -#define SDMA_0_CAUSE_TXBUF (1<<2) -#define SDMA_0_CAUSE_TXEND (1<<3) -#define SDMA_1_CAUSE_RXBUF (1<<8) -#define SDMA_1_CAUSE_RXERR (1<<9) -#define SDMA_1_CAUSE_TXBUF (1<<10) -#define SDMA_1_CAUSE_TXEND (1<<11) - -#define SDMA_CAUSE_RX_MASK (SDMA_0_CAUSE_RXBUF | SDMA_0_CAUSE_RXERR \ - | SDMA_1_CAUSE_RXBUF | SDMA_1_CAUSE_RXERR) -#define SDMA_CAUSE_TX_MASK (SDMA_0_CAUSE_TXBUF | SDMA_0_CAUSE_TXEND \ - | SDMA_1_CAUSE_TXBUF | SDMA_1_CAUSE_TXEND) - -/* SDMA Interrupt registers */ -#define SDMA_INTR_CAUSE 0x0000 -#define SDMA_INTR_MASK 0x0080 - -/* Baud Rate Generator Interface Registers */ -#define BRG_BCR 0x0000 -#define BRG_BTR 0x0004 - -/* - * Define how this driver is known to the outside (we've been assigned a - * range on the "Low-density serial ports" major). - */ -#define MPSC_MAJOR 204 -#define MPSC_MINOR_START 44 -#define MPSC_DRIVER_NAME "MPSC" -#define MPSC_DEV_NAME "ttyMM" -#define MPSC_VERSION "1.00" - -static struct mpsc_port_info mpsc_ports[MPSC_NUM_CTLRS]; -static struct mpsc_shared_regs mpsc_shared_regs; -static struct uart_driver mpsc_reg; - -static void mpsc_start_rx(struct mpsc_port_info *pi); -static void mpsc_free_ring_mem(struct mpsc_port_info *pi); -static void mpsc_release_port(struct uart_port *port); -/* - ****************************************************************************** - * - * Baud Rate Generator Routines (BRG) - * - ****************************************************************************** - */ -static void mpsc_brg_init(struct mpsc_port_info *pi, u32 clk_src) -{ - u32 v; - - v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR); - v = (v & ~(0xf << 18)) | ((clk_src & 0xf) << 18); - - if (pi->brg_can_tune) - v &= ~(1 << 25); - - if (pi->mirror_regs) - pi->BRG_BCR_m = v; - writel(v, pi->brg_base + BRG_BCR); - - writel(readl(pi->brg_base + BRG_BTR) & 0xffff0000, - pi->brg_base + BRG_BTR); -} - -static void mpsc_brg_enable(struct mpsc_port_info *pi) -{ - u32 v; - - v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR); - v |= (1 << 16); - - if (pi->mirror_regs) - pi->BRG_BCR_m = v; - writel(v, pi->brg_base + BRG_BCR); -} - -static void mpsc_brg_disable(struct mpsc_port_info *pi) -{ - u32 v; - - v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR); - v &= ~(1 << 16); - - if (pi->mirror_regs) - pi->BRG_BCR_m = v; - writel(v, pi->brg_base + BRG_BCR); -} - -/* - * To set the baud, we adjust the CDV field in the BRG_BCR reg. - * From manual: Baud = clk / ((CDV+1)*2) ==> CDV = (clk / (baud*2)) - 1. - * However, the input clock is divided by 16 in the MPSC b/c of how - * 'MPSC_MMCRH' was set up so we have to divide the 'clk' used in our - * calculation by 16 to account for that. So the real calculation - * that accounts for the way the mpsc is set up is: - * CDV = (clk / (baud*2*16)) - 1 ==> CDV = (clk / (baud << 5)) - 1. - */ -static void mpsc_set_baudrate(struct mpsc_port_info *pi, u32 baud) -{ - u32 cdv = (pi->port.uartclk / (baud << 5)) - 1; - u32 v; - - mpsc_brg_disable(pi); - v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR); - v = (v & 0xffff0000) | (cdv & 0xffff); - - if (pi->mirror_regs) - pi->BRG_BCR_m = v; - writel(v, pi->brg_base + BRG_BCR); - mpsc_brg_enable(pi); -} - -/* - ****************************************************************************** - * - * Serial DMA Routines (SDMA) - * - ****************************************************************************** - */ - -static void mpsc_sdma_burstsize(struct mpsc_port_info *pi, u32 burst_size) -{ - u32 v; - - pr_debug("mpsc_sdma_burstsize[%d]: burst_size: %d\n", - pi->port.line, burst_size); - - burst_size >>= 3; /* Divide by 8 b/c reg values are 8-byte chunks */ - - if (burst_size < 2) - v = 0x0; /* 1 64-bit word */ - else if (burst_size < 4) - v = 0x1; /* 2 64-bit words */ - else if (burst_size < 8) - v = 0x2; /* 4 64-bit words */ - else - v = 0x3; /* 8 64-bit words */ - - writel((readl(pi->sdma_base + SDMA_SDC) & (0x3 << 12)) | (v << 12), - pi->sdma_base + SDMA_SDC); -} - -static void mpsc_sdma_init(struct mpsc_port_info *pi, u32 burst_size) -{ - pr_debug("mpsc_sdma_init[%d]: burst_size: %d\n", pi->port.line, - burst_size); - - writel((readl(pi->sdma_base + SDMA_SDC) & 0x3ff) | 0x03f, - pi->sdma_base + SDMA_SDC); - mpsc_sdma_burstsize(pi, burst_size); -} - -static u32 mpsc_sdma_intr_mask(struct mpsc_port_info *pi, u32 mask) -{ - u32 old, v; - - pr_debug("mpsc_sdma_intr_mask[%d]: mask: 0x%x\n", pi->port.line, mask); - - old = v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m : - readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK); - - mask &= 0xf; - if (pi->port.line) - mask <<= 8; - v &= ~mask; - - if (pi->mirror_regs) - pi->shared_regs->SDMA_INTR_MASK_m = v; - writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK); - - if (pi->port.line) - old >>= 8; - return old & 0xf; -} - -static void mpsc_sdma_intr_unmask(struct mpsc_port_info *pi, u32 mask) -{ - u32 v; - - pr_debug("mpsc_sdma_intr_unmask[%d]: mask: 0x%x\n", pi->port.line,mask); - - v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m - : readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK); - - mask &= 0xf; - if (pi->port.line) - mask <<= 8; - v |= mask; - - if (pi->mirror_regs) - pi->shared_regs->SDMA_INTR_MASK_m = v; - writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK); -} - -static void mpsc_sdma_intr_ack(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_sdma_intr_ack[%d]: Acknowledging IRQ\n", pi->port.line); - - if (pi->mirror_regs) - pi->shared_regs->SDMA_INTR_CAUSE_m = 0; - writeb(0x00, pi->shared_regs->sdma_intr_base + SDMA_INTR_CAUSE - + pi->port.line); -} - -static void mpsc_sdma_set_rx_ring(struct mpsc_port_info *pi, - struct mpsc_rx_desc *rxre_p) -{ - pr_debug("mpsc_sdma_set_rx_ring[%d]: rxre_p: 0x%x\n", - pi->port.line, (u32)rxre_p); - - writel((u32)rxre_p, pi->sdma_base + SDMA_SCRDP); -} - -static void mpsc_sdma_set_tx_ring(struct mpsc_port_info *pi, - struct mpsc_tx_desc *txre_p) -{ - writel((u32)txre_p, pi->sdma_base + SDMA_SFTDP); - writel((u32)txre_p, pi->sdma_base + SDMA_SCTDP); -} - -static void mpsc_sdma_cmd(struct mpsc_port_info *pi, u32 val) -{ - u32 v; - - v = readl(pi->sdma_base + SDMA_SDCM); - if (val) - v |= val; - else - v = 0; - wmb(); - writel(v, pi->sdma_base + SDMA_SDCM); - wmb(); -} - -static uint mpsc_sdma_tx_active(struct mpsc_port_info *pi) -{ - return readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_TXD; -} - -static void mpsc_sdma_start_tx(struct mpsc_port_info *pi) -{ - struct mpsc_tx_desc *txre, *txre_p; - - /* If tx isn't running & there's a desc ready to go, start it */ - if (!mpsc_sdma_tx_active(pi)) { - txre = (struct mpsc_tx_desc *)(pi->txr - + (pi->txr_tail * MPSC_TXRE_SIZE)); - dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE, - DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)txre, - (ulong)txre + MPSC_TXRE_SIZE); -#endif - - if (be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O) { - txre_p = (struct mpsc_tx_desc *) - (pi->txr_p + (pi->txr_tail * MPSC_TXRE_SIZE)); - - mpsc_sdma_set_tx_ring(pi, txre_p); - mpsc_sdma_cmd(pi, SDMA_SDCM_STD | SDMA_SDCM_TXD); - } - } -} - -static void mpsc_sdma_stop(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_sdma_stop[%d]: Stopping SDMA\n", pi->port.line); - - /* Abort any SDMA transfers */ - mpsc_sdma_cmd(pi, 0); - mpsc_sdma_cmd(pi, SDMA_SDCM_AR | SDMA_SDCM_AT); - - /* Clear the SDMA current and first TX and RX pointers */ - mpsc_sdma_set_tx_ring(pi, NULL); - mpsc_sdma_set_rx_ring(pi, NULL); - - /* Disable interrupts */ - mpsc_sdma_intr_mask(pi, 0xf); - mpsc_sdma_intr_ack(pi); -} - -/* - ****************************************************************************** - * - * Multi-Protocol Serial Controller Routines (MPSC) - * - ****************************************************************************** - */ - -static void mpsc_hw_init(struct mpsc_port_info *pi) -{ - u32 v; - - pr_debug("mpsc_hw_init[%d]: Initializing hardware\n", pi->port.line); - - /* Set up clock routing */ - if (pi->mirror_regs) { - v = pi->shared_regs->MPSC_MRR_m; - v &= ~0x1c7; - pi->shared_regs->MPSC_MRR_m = v; - writel(v, pi->shared_regs->mpsc_routing_base + MPSC_MRR); - - v = pi->shared_regs->MPSC_RCRR_m; - v = (v & ~0xf0f) | 0x100; - pi->shared_regs->MPSC_RCRR_m = v; - writel(v, pi->shared_regs->mpsc_routing_base + MPSC_RCRR); - - v = pi->shared_regs->MPSC_TCRR_m; - v = (v & ~0xf0f) | 0x100; - pi->shared_regs->MPSC_TCRR_m = v; - writel(v, pi->shared_regs->mpsc_routing_base + MPSC_TCRR); - } else { - v = readl(pi->shared_regs->mpsc_routing_base + MPSC_MRR); - v &= ~0x1c7; - writel(v, pi->shared_regs->mpsc_routing_base + MPSC_MRR); - - v = readl(pi->shared_regs->mpsc_routing_base + MPSC_RCRR); - v = (v & ~0xf0f) | 0x100; - writel(v, pi->shared_regs->mpsc_routing_base + MPSC_RCRR); - - v = readl(pi->shared_regs->mpsc_routing_base + MPSC_TCRR); - v = (v & ~0xf0f) | 0x100; - writel(v, pi->shared_regs->mpsc_routing_base + MPSC_TCRR); - } - - /* Put MPSC in UART mode & enabel Tx/Rx egines */ - writel(0x000004c4, pi->mpsc_base + MPSC_MMCRL); - - /* No preamble, 16x divider, low-latency, */ - writel(0x04400400, pi->mpsc_base + MPSC_MMCRH); - mpsc_set_baudrate(pi, pi->default_baud); - - if (pi->mirror_regs) { - pi->MPSC_CHR_1_m = 0; - pi->MPSC_CHR_2_m = 0; - } - writel(0, pi->mpsc_base + MPSC_CHR_1); - writel(0, pi->mpsc_base + MPSC_CHR_2); - writel(pi->mpsc_max_idle, pi->mpsc_base + MPSC_CHR_3); - writel(0, pi->mpsc_base + MPSC_CHR_4); - writel(0, pi->mpsc_base + MPSC_CHR_5); - writel(0, pi->mpsc_base + MPSC_CHR_6); - writel(0, pi->mpsc_base + MPSC_CHR_7); - writel(0, pi->mpsc_base + MPSC_CHR_8); - writel(0, pi->mpsc_base + MPSC_CHR_9); - writel(0, pi->mpsc_base + MPSC_CHR_10); -} - -static void mpsc_enter_hunt(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_enter_hunt[%d]: Hunting...\n", pi->port.line); - - if (pi->mirror_regs) { - writel(pi->MPSC_CHR_2_m | MPSC_CHR_2_EH, - pi->mpsc_base + MPSC_CHR_2); - /* Erratum prevents reading CHR_2 so just delay for a while */ - udelay(100); - } else { - writel(readl(pi->mpsc_base + MPSC_CHR_2) | MPSC_CHR_2_EH, - pi->mpsc_base + MPSC_CHR_2); - - while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_EH) - udelay(10); - } -} - -static void mpsc_freeze(struct mpsc_port_info *pi) -{ - u32 v; - - pr_debug("mpsc_freeze[%d]: Freezing\n", pi->port.line); - - v = (pi->mirror_regs) ? pi->MPSC_MPCR_m : - readl(pi->mpsc_base + MPSC_MPCR); - v |= MPSC_MPCR_FRZ; - - if (pi->mirror_regs) - pi->MPSC_MPCR_m = v; - writel(v, pi->mpsc_base + MPSC_MPCR); -} - -static void mpsc_unfreeze(struct mpsc_port_info *pi) -{ - u32 v; - - v = (pi->mirror_regs) ? pi->MPSC_MPCR_m : - readl(pi->mpsc_base + MPSC_MPCR); - v &= ~MPSC_MPCR_FRZ; - - if (pi->mirror_regs) - pi->MPSC_MPCR_m = v; - writel(v, pi->mpsc_base + MPSC_MPCR); - - pr_debug("mpsc_unfreeze[%d]: Unfrozen\n", pi->port.line); -} - -static void mpsc_set_char_length(struct mpsc_port_info *pi, u32 len) -{ - u32 v; - - pr_debug("mpsc_set_char_length[%d]: char len: %d\n", pi->port.line,len); - - v = (pi->mirror_regs) ? pi->MPSC_MPCR_m : - readl(pi->mpsc_base + MPSC_MPCR); - v = (v & ~(0x3 << 12)) | ((len & 0x3) << 12); - - if (pi->mirror_regs) - pi->MPSC_MPCR_m = v; - writel(v, pi->mpsc_base + MPSC_MPCR); -} - -static void mpsc_set_stop_bit_length(struct mpsc_port_info *pi, u32 len) -{ - u32 v; - - pr_debug("mpsc_set_stop_bit_length[%d]: stop bits: %d\n", - pi->port.line, len); - - v = (pi->mirror_regs) ? pi->MPSC_MPCR_m : - readl(pi->mpsc_base + MPSC_MPCR); - - v = (v & ~(1 << 14)) | ((len & 0x1) << 14); - - if (pi->mirror_regs) - pi->MPSC_MPCR_m = v; - writel(v, pi->mpsc_base + MPSC_MPCR); -} - -static void mpsc_set_parity(struct mpsc_port_info *pi, u32 p) -{ - u32 v; - - pr_debug("mpsc_set_parity[%d]: parity bits: 0x%x\n", pi->port.line, p); - - v = (pi->mirror_regs) ? pi->MPSC_CHR_2_m : - readl(pi->mpsc_base + MPSC_CHR_2); - - p &= 0x3; - v = (v & ~0xc000c) | (p << 18) | (p << 2); - - if (pi->mirror_regs) - pi->MPSC_CHR_2_m = v; - writel(v, pi->mpsc_base + MPSC_CHR_2); -} - -/* - ****************************************************************************** - * - * Driver Init Routines - * - ****************************************************************************** - */ - -static void mpsc_init_hw(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_init_hw[%d]: Initializing\n", pi->port.line); - - mpsc_brg_init(pi, pi->brg_clk_src); - mpsc_brg_enable(pi); - mpsc_sdma_init(pi, dma_get_cache_alignment()); /* burst a cacheline */ - mpsc_sdma_stop(pi); - mpsc_hw_init(pi); -} - -static int mpsc_alloc_ring_mem(struct mpsc_port_info *pi) -{ - int rc = 0; - - pr_debug("mpsc_alloc_ring_mem[%d]: Allocating ring mem\n", - pi->port.line); - - if (!pi->dma_region) { - if (!dma_set_mask(pi->port.dev, 0xffffffff)) { - printk(KERN_ERR "MPSC: Inadequate DMA support\n"); - rc = -ENXIO; - } else if ((pi->dma_region = dma_alloc_attrs(pi->port.dev, - MPSC_DMA_ALLOC_SIZE, - &pi->dma_region_p, GFP_KERNEL, - DMA_ATTR_NON_CONSISTENT)) - == NULL) { - printk(KERN_ERR "MPSC: Can't alloc Desc region\n"); - rc = -ENOMEM; - } - } - - return rc; -} - -static void mpsc_free_ring_mem(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_free_ring_mem[%d]: Freeing ring mem\n", pi->port.line); - - if (pi->dma_region) { - dma_free_attrs(pi->port.dev, MPSC_DMA_ALLOC_SIZE, - pi->dma_region, pi->dma_region_p, - DMA_ATTR_NON_CONSISTENT); - pi->dma_region = NULL; - pi->dma_region_p = (dma_addr_t)NULL; - } -} - -static void mpsc_init_rings(struct mpsc_port_info *pi) -{ - struct mpsc_rx_desc *rxre; - struct mpsc_tx_desc *txre; - dma_addr_t dp, dp_p; - u8 *bp, *bp_p; - int i; - - pr_debug("mpsc_init_rings[%d]: Initializing rings\n", pi->port.line); - - BUG_ON(pi->dma_region == NULL); - - memset(pi->dma_region, 0, MPSC_DMA_ALLOC_SIZE); - - /* - * Descriptors & buffers are multiples of cacheline size and must be - * cacheline aligned. - */ - dp = ALIGN((u32)pi->dma_region, dma_get_cache_alignment()); - dp_p = ALIGN((u32)pi->dma_region_p, dma_get_cache_alignment()); - - /* - * Partition dma region into rx ring descriptor, rx buffers, - * tx ring descriptors, and tx buffers. - */ - pi->rxr = dp; - pi->rxr_p = dp_p; - dp += MPSC_RXR_SIZE; - dp_p += MPSC_RXR_SIZE; - - pi->rxb = (u8 *)dp; - pi->rxb_p = (u8 *)dp_p; - dp += MPSC_RXB_SIZE; - dp_p += MPSC_RXB_SIZE; - - pi->rxr_posn = 0; - - pi->txr = dp; - pi->txr_p = dp_p; - dp += MPSC_TXR_SIZE; - dp_p += MPSC_TXR_SIZE; - - pi->txb = (u8 *)dp; - pi->txb_p = (u8 *)dp_p; - - pi->txr_head = 0; - pi->txr_tail = 0; - - /* Init rx ring descriptors */ - dp = pi->rxr; - dp_p = pi->rxr_p; - bp = pi->rxb; - bp_p = pi->rxb_p; - - for (i = 0; i < MPSC_RXR_ENTRIES; i++) { - rxre = (struct mpsc_rx_desc *)dp; - - rxre->bufsize = cpu_to_be16(MPSC_RXBE_SIZE); - rxre->bytecnt = cpu_to_be16(0); - rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O - | SDMA_DESC_CMDSTAT_EI | SDMA_DESC_CMDSTAT_F - | SDMA_DESC_CMDSTAT_L); - rxre->link = cpu_to_be32(dp_p + MPSC_RXRE_SIZE); - rxre->buf_ptr = cpu_to_be32(bp_p); - - dp += MPSC_RXRE_SIZE; - dp_p += MPSC_RXRE_SIZE; - bp += MPSC_RXBE_SIZE; - bp_p += MPSC_RXBE_SIZE; - } - rxre->link = cpu_to_be32(pi->rxr_p); /* Wrap last back to first */ - - /* Init tx ring descriptors */ - dp = pi->txr; - dp_p = pi->txr_p; - bp = pi->txb; - bp_p = pi->txb_p; - - for (i = 0; i < MPSC_TXR_ENTRIES; i++) { - txre = (struct mpsc_tx_desc *)dp; - - txre->link = cpu_to_be32(dp_p + MPSC_TXRE_SIZE); - txre->buf_ptr = cpu_to_be32(bp_p); - - dp += MPSC_TXRE_SIZE; - dp_p += MPSC_TXRE_SIZE; - bp += MPSC_TXBE_SIZE; - bp_p += MPSC_TXBE_SIZE; - } - txre->link = cpu_to_be32(pi->txr_p); /* Wrap last back to first */ - - dma_cache_sync(pi->port.dev, (void *)pi->dma_region, - MPSC_DMA_ALLOC_SIZE, DMA_BIDIRECTIONAL); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - flush_dcache_range((ulong)pi->dma_region, - (ulong)pi->dma_region - + MPSC_DMA_ALLOC_SIZE); -#endif - - return; -} - -static void mpsc_uninit_rings(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_uninit_rings[%d]: Uninitializing rings\n",pi->port.line); - - BUG_ON(pi->dma_region == NULL); - - pi->rxr = 0; - pi->rxr_p = 0; - pi->rxb = NULL; - pi->rxb_p = NULL; - pi->rxr_posn = 0; - - pi->txr = 0; - pi->txr_p = 0; - pi->txb = NULL; - pi->txb_p = NULL; - pi->txr_head = 0; - pi->txr_tail = 0; -} - -static int mpsc_make_ready(struct mpsc_port_info *pi) -{ - int rc; - - pr_debug("mpsc_make_ready[%d]: Making cltr ready\n", pi->port.line); - - if (!pi->ready) { - mpsc_init_hw(pi); - rc = mpsc_alloc_ring_mem(pi); - if (rc) - return rc; - mpsc_init_rings(pi); - pi->ready = 1; - } - - return 0; -} - -#ifdef CONFIG_CONSOLE_POLL -static int serial_polled; -#endif - -/* - ****************************************************************************** - * - * Interrupt Handling Routines - * - ****************************************************************************** - */ - -static int mpsc_rx_intr(struct mpsc_port_info *pi, unsigned long *flags) -{ - struct mpsc_rx_desc *rxre; - struct tty_port *port = &pi->port.state->port; - u32 cmdstat, bytes_in, i; - int rc = 0; - u8 *bp; - char flag = TTY_NORMAL; - - pr_debug("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line); - - rxre = (struct mpsc_rx_desc *)(pi->rxr + (pi->rxr_posn*MPSC_RXRE_SIZE)); - - dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE, - DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)rxre, - (ulong)rxre + MPSC_RXRE_SIZE); -#endif - - /* - * Loop through Rx descriptors handling ones that have been completed. - */ - while (!((cmdstat = be32_to_cpu(rxre->cmdstat)) - & SDMA_DESC_CMDSTAT_O)) { - bytes_in = be16_to_cpu(rxre->bytecnt); -#ifdef CONFIG_CONSOLE_POLL - if (unlikely(serial_polled)) { - serial_polled = 0; - return 0; - } -#endif - /* Following use of tty struct directly is deprecated */ - if (tty_buffer_request_room(port, bytes_in) < bytes_in) { - if (port->low_latency) { - spin_unlock_irqrestore(&pi->port.lock, *flags); - tty_flip_buffer_push(port); - spin_lock_irqsave(&pi->port.lock, *flags); - } - /* - * If this failed then we will throw away the bytes - * but must do so to clear interrupts. - */ - } - - bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE); - dma_cache_sync(pi->port.dev, (void *)bp, MPSC_RXBE_SIZE, - DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)bp, - (ulong)bp + MPSC_RXBE_SIZE); -#endif - - /* - * Other than for parity error, the manual provides little - * info on what data will be in a frame flagged by any of - * these errors. For parity error, it is the last byte in - * the buffer that had the error. As for the rest, I guess - * we'll assume there is no data in the buffer. - * If there is...it gets lost. - */ - if (unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR - | SDMA_DESC_CMDSTAT_FR - | SDMA_DESC_CMDSTAT_OR))) { - - pi->port.icount.rx++; - - if (cmdstat & SDMA_DESC_CMDSTAT_BR) { /* Break */ - pi->port.icount.brk++; - - if (uart_handle_break(&pi->port)) - goto next_frame; - } else if (cmdstat & SDMA_DESC_CMDSTAT_FR) { - pi->port.icount.frame++; - } else if (cmdstat & SDMA_DESC_CMDSTAT_OR) { - pi->port.icount.overrun++; - } - - cmdstat &= pi->port.read_status_mask; - - if (cmdstat & SDMA_DESC_CMDSTAT_BR) - flag = TTY_BREAK; - else if (cmdstat & SDMA_DESC_CMDSTAT_FR) - flag = TTY_FRAME; - else if (cmdstat & SDMA_DESC_CMDSTAT_OR) - flag = TTY_OVERRUN; - else if (cmdstat & SDMA_DESC_CMDSTAT_PE) - flag = TTY_PARITY; - } - - if (uart_handle_sysrq_char(&pi->port, *bp)) { - bp++; - bytes_in--; -#ifdef CONFIG_CONSOLE_POLL - if (unlikely(serial_polled)) { - serial_polled = 0; - return 0; - } -#endif - goto next_frame; - } - - if ((unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR - | SDMA_DESC_CMDSTAT_FR - | SDMA_DESC_CMDSTAT_OR))) - && !(cmdstat & pi->port.ignore_status_mask)) { - tty_insert_flip_char(port, *bp, flag); - } else { - for (i=0; iport.icount.rx += bytes_in; - } - -next_frame: - rxre->bytecnt = cpu_to_be16(0); - wmb(); - rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O - | SDMA_DESC_CMDSTAT_EI | SDMA_DESC_CMDSTAT_F - | SDMA_DESC_CMDSTAT_L); - wmb(); - dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE, - DMA_BIDIRECTIONAL); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - flush_dcache_range((ulong)rxre, - (ulong)rxre + MPSC_RXRE_SIZE); -#endif - - /* Advance to next descriptor */ - pi->rxr_posn = (pi->rxr_posn + 1) & (MPSC_RXR_ENTRIES - 1); - rxre = (struct mpsc_rx_desc *) - (pi->rxr + (pi->rxr_posn * MPSC_RXRE_SIZE)); - dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE, - DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)rxre, - (ulong)rxre + MPSC_RXRE_SIZE); -#endif - rc = 1; - } - - /* Restart rx engine, if its stopped */ - if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0) - mpsc_start_rx(pi); - - spin_unlock_irqrestore(&pi->port.lock, *flags); - tty_flip_buffer_push(port); - spin_lock_irqsave(&pi->port.lock, *flags); - return rc; -} - -static void mpsc_setup_tx_desc(struct mpsc_port_info *pi, u32 count, u32 intr) -{ - struct mpsc_tx_desc *txre; - - txre = (struct mpsc_tx_desc *)(pi->txr - + (pi->txr_head * MPSC_TXRE_SIZE)); - - txre->bytecnt = cpu_to_be16(count); - txre->shadow = txre->bytecnt; - wmb(); /* ensure cmdstat is last field updated */ - txre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | SDMA_DESC_CMDSTAT_F - | SDMA_DESC_CMDSTAT_L - | ((intr) ? SDMA_DESC_CMDSTAT_EI : 0)); - wmb(); - dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE, - DMA_BIDIRECTIONAL); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - flush_dcache_range((ulong)txre, - (ulong)txre + MPSC_TXRE_SIZE); -#endif -} - -static void mpsc_copy_tx_data(struct mpsc_port_info *pi) -{ - struct circ_buf *xmit = &pi->port.state->xmit; - u8 *bp; - u32 i; - - /* Make sure the desc ring isn't full */ - while (CIRC_CNT(pi->txr_head, pi->txr_tail, MPSC_TXR_ENTRIES) - < (MPSC_TXR_ENTRIES - 1)) { - if (pi->port.x_char) { - /* - * Ideally, we should use the TCS field in - * CHR_1 to put the x_char out immediately but - * errata prevents us from being able to read - * CHR_2 to know that its safe to write to - * CHR_1. Instead, just put it in-band with - * all the other Tx data. - */ - bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); - *bp = pi->port.x_char; - pi->port.x_char = 0; - i = 1; - } else if (!uart_circ_empty(xmit) - && !uart_tx_stopped(&pi->port)) { - i = min((u32)MPSC_TXBE_SIZE, - (u32)uart_circ_chars_pending(xmit)); - i = min(i, (u32)CIRC_CNT_TO_END(xmit->head, xmit->tail, - UART_XMIT_SIZE)); - bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); - memcpy(bp, &xmit->buf[xmit->tail], i); - xmit->tail = (xmit->tail + i) & (UART_XMIT_SIZE - 1); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&pi->port); - } else { /* All tx data copied into ring bufs */ - return; - } - - dma_cache_sync(pi->port.dev, (void *)bp, MPSC_TXBE_SIZE, - DMA_BIDIRECTIONAL); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - flush_dcache_range((ulong)bp, - (ulong)bp + MPSC_TXBE_SIZE); -#endif - mpsc_setup_tx_desc(pi, i, 1); - - /* Advance to next descriptor */ - pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1); - } -} - -static int mpsc_tx_intr(struct mpsc_port_info *pi) -{ - struct mpsc_tx_desc *txre; - int rc = 0; - unsigned long iflags; - - spin_lock_irqsave(&pi->tx_lock, iflags); - - if (!mpsc_sdma_tx_active(pi)) { - txre = (struct mpsc_tx_desc *)(pi->txr - + (pi->txr_tail * MPSC_TXRE_SIZE)); - - dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE, - DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)txre, - (ulong)txre + MPSC_TXRE_SIZE); -#endif - - while (!(be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O)) { - rc = 1; - pi->port.icount.tx += be16_to_cpu(txre->bytecnt); - pi->txr_tail = (pi->txr_tail+1) & (MPSC_TXR_ENTRIES-1); - - /* If no more data to tx, fall out of loop */ - if (pi->txr_head == pi->txr_tail) - break; - - txre = (struct mpsc_tx_desc *)(pi->txr - + (pi->txr_tail * MPSC_TXRE_SIZE)); - dma_cache_sync(pi->port.dev, (void *)txre, - MPSC_TXRE_SIZE, DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)txre, - (ulong)txre + MPSC_TXRE_SIZE); -#endif - } - - mpsc_copy_tx_data(pi); - mpsc_sdma_start_tx(pi); /* start next desc if ready */ - } - - spin_unlock_irqrestore(&pi->tx_lock, iflags); - return rc; -} - -/* - * This is the driver's interrupt handler. To avoid a race, we first clear - * the interrupt, then handle any completed Rx/Tx descriptors. When done - * handling those descriptors, we restart the Rx/Tx engines if they're stopped. - */ -static irqreturn_t mpsc_sdma_intr(int irq, void *dev_id) -{ - struct mpsc_port_info *pi = dev_id; - ulong iflags; - int rc = IRQ_NONE; - - pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Received\n",pi->port.line); - - spin_lock_irqsave(&pi->port.lock, iflags); - mpsc_sdma_intr_ack(pi); - if (mpsc_rx_intr(pi, &iflags)) - rc = IRQ_HANDLED; - if (mpsc_tx_intr(pi)) - rc = IRQ_HANDLED; - spin_unlock_irqrestore(&pi->port.lock, iflags); - - pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Handled\n", pi->port.line); - return rc; -} - -/* - ****************************************************************************** - * - * serial_core.c Interface routines - * - ****************************************************************************** - */ -static uint mpsc_tx_empty(struct uart_port *port) -{ - struct mpsc_port_info *pi = - container_of(port, struct mpsc_port_info, port); - ulong iflags; - uint rc; - - spin_lock_irqsave(&pi->port.lock, iflags); - rc = mpsc_sdma_tx_active(pi) ? 0 : TIOCSER_TEMT; - spin_unlock_irqrestore(&pi->port.lock, iflags); - - return rc; -} - -static void mpsc_set_mctrl(struct uart_port *port, uint mctrl) -{ - /* Have no way to set modem control lines AFAICT */ -} - -static uint mpsc_get_mctrl(struct uart_port *port) -{ - struct mpsc_port_info *pi = - container_of(port, struct mpsc_port_info, port); - u32 mflags, status; - - status = (pi->mirror_regs) ? pi->MPSC_CHR_10_m - : readl(pi->mpsc_base + MPSC_CHR_10); - - mflags = 0; - if (status & 0x1) - mflags |= TIOCM_CTS; - if (status & 0x2) - mflags |= TIOCM_CAR; - - return mflags | TIOCM_DSR; /* No way to tell if DSR asserted */ -} - -static void mpsc_stop_tx(struct uart_port *port) -{ - struct mpsc_port_info *pi = - container_of(port, struct mpsc_port_info, port); - - pr_debug("mpsc_stop_tx[%d]\n", port->line); - - mpsc_freeze(pi); -} - -static void mpsc_start_tx(struct uart_port *port) -{ - struct mpsc_port_info *pi = - container_of(port, struct mpsc_port_info, port); - unsigned long iflags; - - spin_lock_irqsave(&pi->tx_lock, iflags); - - mpsc_unfreeze(pi); - mpsc_copy_tx_data(pi); - mpsc_sdma_start_tx(pi); - - spin_unlock_irqrestore(&pi->tx_lock, iflags); - - pr_debug("mpsc_start_tx[%d]\n", port->line); -} - -static void mpsc_start_rx(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_start_rx[%d]: Starting...\n", pi->port.line); - - if (pi->rcv_data) { - mpsc_enter_hunt(pi); - mpsc_sdma_cmd(pi, SDMA_SDCM_ERD); - } -} - -static void mpsc_stop_rx(struct uart_port *port) -{ - struct mpsc_port_info *pi = - container_of(port, struct mpsc_port_info, port); - - pr_debug("mpsc_stop_rx[%d]: Stopping...\n", port->line); - - if (pi->mirror_regs) { - writel(pi->MPSC_CHR_2_m | MPSC_CHR_2_RA, - pi->mpsc_base + MPSC_CHR_2); - /* Erratum prevents reading CHR_2 so just delay for a while */ - udelay(100); - } else { - writel(readl(pi->mpsc_base + MPSC_CHR_2) | MPSC_CHR_2_RA, - pi->mpsc_base + MPSC_CHR_2); - - while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_RA) - udelay(10); - } - - mpsc_sdma_cmd(pi, SDMA_SDCM_AR); -} - -static void mpsc_break_ctl(struct uart_port *port, int ctl) -{ - struct mpsc_port_info *pi = - container_of(port, struct mpsc_port_info, port); - ulong flags; - u32 v; - - v = ctl ? 0x00ff0000 : 0; - - spin_lock_irqsave(&pi->port.lock, flags); - if (pi->mirror_regs) - pi->MPSC_CHR_1_m = v; - writel(v, pi->mpsc_base + MPSC_CHR_1); - spin_unlock_irqrestore(&pi->port.lock, flags); -} - -static int mpsc_startup(struct uart_port *port) -{ - struct mpsc_port_info *pi = - container_of(port, struct mpsc_port_info, port); - u32 flag = 0; - int rc; - - pr_debug("mpsc_startup[%d]: Starting up MPSC, irq: %d\n", - port->line, pi->port.irq); - - if ((rc = mpsc_make_ready(pi)) == 0) { - /* Setup IRQ handler */ - mpsc_sdma_intr_ack(pi); - - /* If irq's are shared, need to set flag */ - if (mpsc_ports[0].port.irq == mpsc_ports[1].port.irq) - flag = IRQF_SHARED; - - if (request_irq(pi->port.irq, mpsc_sdma_intr, flag, - "mpsc-sdma", pi)) - printk(KERN_ERR "MPSC: Can't get SDMA IRQ %d\n", - pi->port.irq); - - mpsc_sdma_intr_unmask(pi, 0xf); - mpsc_sdma_set_rx_ring(pi, (struct mpsc_rx_desc *)(pi->rxr_p - + (pi->rxr_posn * MPSC_RXRE_SIZE))); - } - - return rc; -} - -static void mpsc_shutdown(struct uart_port *port) -{ - struct mpsc_port_info *pi = - container_of(port, struct mpsc_port_info, port); - - pr_debug("mpsc_shutdown[%d]: Shutting down MPSC\n", port->line); - - mpsc_sdma_stop(pi); - free_irq(pi->port.irq, pi); -} - -static void mpsc_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct mpsc_port_info *pi = - container_of(port, struct mpsc_port_info, port); - u32 baud; - ulong flags; - u32 chr_bits, stop_bits, par; - - switch (termios->c_cflag & CSIZE) { - case CS5: - chr_bits = MPSC_MPCR_CL_5; - break; - case CS6: - chr_bits = MPSC_MPCR_CL_6; - break; - case CS7: - chr_bits = MPSC_MPCR_CL_7; - break; - case CS8: - default: - chr_bits = MPSC_MPCR_CL_8; - break; - } - - if (termios->c_cflag & CSTOPB) - stop_bits = MPSC_MPCR_SBL_2; - else - stop_bits = MPSC_MPCR_SBL_1; - - par = MPSC_CHR_2_PAR_EVEN; - if (termios->c_cflag & PARENB) - if (termios->c_cflag & PARODD) - par = MPSC_CHR_2_PAR_ODD; -#ifdef CMSPAR - if (termios->c_cflag & CMSPAR) { - if (termios->c_cflag & PARODD) - par = MPSC_CHR_2_PAR_MARK; - else - par = MPSC_CHR_2_PAR_SPACE; - } -#endif - - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk); - - spin_lock_irqsave(&pi->port.lock, flags); - - uart_update_timeout(port, termios->c_cflag, baud); - - mpsc_set_char_length(pi, chr_bits); - mpsc_set_stop_bit_length(pi, stop_bits); - mpsc_set_parity(pi, par); - mpsc_set_baudrate(pi, baud); - - /* Characters/events to read */ - pi->port.read_status_mask = SDMA_DESC_CMDSTAT_OR; - - if (termios->c_iflag & INPCK) - pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_PE - | SDMA_DESC_CMDSTAT_FR; - - if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) - pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_BR; - - /* Characters/events to ignore */ - pi->port.ignore_status_mask = 0; - - if (termios->c_iflag & IGNPAR) - pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_PE - | SDMA_DESC_CMDSTAT_FR; - - if (termios->c_iflag & IGNBRK) { - pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_BR; - - if (termios->c_iflag & IGNPAR) - pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_OR; - } - - if ((termios->c_cflag & CREAD)) { - if (!pi->rcv_data) { - pi->rcv_data = 1; - mpsc_start_rx(pi); - } - } else if (pi->rcv_data) { - mpsc_stop_rx(port); - pi->rcv_data = 0; - } - - spin_unlock_irqrestore(&pi->port.lock, flags); -} - -static const char *mpsc_type(struct uart_port *port) -{ - pr_debug("mpsc_type[%d]: port type: %s\n", port->line,MPSC_DRIVER_NAME); - return MPSC_DRIVER_NAME; -} - -static int mpsc_request_port(struct uart_port *port) -{ - /* Should make chip/platform specific call */ - return 0; -} - -static void mpsc_release_port(struct uart_port *port) -{ - struct mpsc_port_info *pi = - container_of(port, struct mpsc_port_info, port); - - if (pi->ready) { - mpsc_uninit_rings(pi); - mpsc_free_ring_mem(pi); - pi->ready = 0; - } -} - -static void mpsc_config_port(struct uart_port *port, int flags) -{ -} - -static int mpsc_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - struct mpsc_port_info *pi = - container_of(port, struct mpsc_port_info, port); - int rc = 0; - - pr_debug("mpsc_verify_port[%d]: Verifying port data\n", pi->port.line); - - if (ser->type != PORT_UNKNOWN && ser->type != PORT_MPSC) - rc = -EINVAL; - else if (pi->port.irq != ser->irq) - rc = -EINVAL; - else if (ser->io_type != SERIAL_IO_MEM) - rc = -EINVAL; - else if (pi->port.uartclk / 16 != ser->baud_base) /* Not sure */ - rc = -EINVAL; - else if ((void *)pi->port.mapbase != ser->iomem_base) - rc = -EINVAL; - else if (pi->port.iobase != ser->port) - rc = -EINVAL; - else if (ser->hub6 != 0) - rc = -EINVAL; - - return rc; -} -#ifdef CONFIG_CONSOLE_POLL -/* Serial polling routines for writing and reading from the uart while - * in an interrupt or debug context. - */ - -static char poll_buf[2048]; -static int poll_ptr; -static int poll_cnt; -static void mpsc_put_poll_char(struct uart_port *port, - unsigned char c); - -static int mpsc_get_poll_char(struct uart_port *port) -{ - struct mpsc_port_info *pi = - container_of(port, struct mpsc_port_info, port); - struct mpsc_rx_desc *rxre; - u32 cmdstat, bytes_in, i; - u8 *bp; - - if (!serial_polled) - serial_polled = 1; - - pr_debug("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line); - - if (poll_cnt) { - poll_cnt--; - return poll_buf[poll_ptr++]; - } - poll_ptr = 0; - poll_cnt = 0; - - while (poll_cnt == 0) { - rxre = (struct mpsc_rx_desc *)(pi->rxr + - (pi->rxr_posn*MPSC_RXRE_SIZE)); - dma_cache_sync(pi->port.dev, (void *)rxre, - MPSC_RXRE_SIZE, DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)rxre, - (ulong)rxre + MPSC_RXRE_SIZE); -#endif - /* - * Loop through Rx descriptors handling ones that have - * been completed. - */ - while (poll_cnt == 0 && - !((cmdstat = be32_to_cpu(rxre->cmdstat)) & - SDMA_DESC_CMDSTAT_O)){ - bytes_in = be16_to_cpu(rxre->bytecnt); - bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE); - dma_cache_sync(pi->port.dev, (void *) bp, - MPSC_RXBE_SIZE, DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)bp, - (ulong)bp + MPSC_RXBE_SIZE); -#endif - if ((unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR | - SDMA_DESC_CMDSTAT_FR | SDMA_DESC_CMDSTAT_OR))) && - !(cmdstat & pi->port.ignore_status_mask)) { - poll_buf[poll_cnt] = *bp; - poll_cnt++; - } else { - for (i = 0; i < bytes_in; i++) { - poll_buf[poll_cnt] = *bp++; - poll_cnt++; - } - pi->port.icount.rx += bytes_in; - } - rxre->bytecnt = cpu_to_be16(0); - wmb(); - rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | - SDMA_DESC_CMDSTAT_EI | - SDMA_DESC_CMDSTAT_F | - SDMA_DESC_CMDSTAT_L); - wmb(); - dma_cache_sync(pi->port.dev, (void *)rxre, - MPSC_RXRE_SIZE, DMA_BIDIRECTIONAL); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - flush_dcache_range((ulong)rxre, - (ulong)rxre + MPSC_RXRE_SIZE); -#endif - - /* Advance to next descriptor */ - pi->rxr_posn = (pi->rxr_posn + 1) & - (MPSC_RXR_ENTRIES - 1); - rxre = (struct mpsc_rx_desc *)(pi->rxr + - (pi->rxr_posn * MPSC_RXRE_SIZE)); - dma_cache_sync(pi->port.dev, (void *)rxre, - MPSC_RXRE_SIZE, DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)rxre, - (ulong)rxre + MPSC_RXRE_SIZE); -#endif - } - - /* Restart rx engine, if its stopped */ - if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0) - mpsc_start_rx(pi); - } - if (poll_cnt) { - poll_cnt--; - return poll_buf[poll_ptr++]; - } - - return 0; -} - - -static void mpsc_put_poll_char(struct uart_port *port, - unsigned char c) -{ - struct mpsc_port_info *pi = - container_of(port, struct mpsc_port_info, port); - u32 data; - - data = readl(pi->mpsc_base + MPSC_MPCR); - writeb(c, pi->mpsc_base + MPSC_CHR_1); - mb(); - data = readl(pi->mpsc_base + MPSC_CHR_2); - data |= MPSC_CHR_2_TTCS; - writel(data, pi->mpsc_base + MPSC_CHR_2); - mb(); - - while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_TTCS); -} -#endif - -static const struct uart_ops mpsc_pops = { - .tx_empty = mpsc_tx_empty, - .set_mctrl = mpsc_set_mctrl, - .get_mctrl = mpsc_get_mctrl, - .stop_tx = mpsc_stop_tx, - .start_tx = mpsc_start_tx, - .stop_rx = mpsc_stop_rx, - .break_ctl = mpsc_break_ctl, - .startup = mpsc_startup, - .shutdown = mpsc_shutdown, - .set_termios = mpsc_set_termios, - .type = mpsc_type, - .release_port = mpsc_release_port, - .request_port = mpsc_request_port, - .config_port = mpsc_config_port, - .verify_port = mpsc_verify_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_get_char = mpsc_get_poll_char, - .poll_put_char = mpsc_put_poll_char, -#endif -}; - -/* - ****************************************************************************** - * - * Console Interface Routines - * - ****************************************************************************** - */ - -#ifdef CONFIG_SERIAL_MPSC_CONSOLE -static void mpsc_console_write(struct console *co, const char *s, uint count) -{ - struct mpsc_port_info *pi = &mpsc_ports[co->index]; - u8 *bp, *dp, add_cr = 0; - int i; - unsigned long iflags; - - spin_lock_irqsave(&pi->tx_lock, iflags); - - while (pi->txr_head != pi->txr_tail) { - while (mpsc_sdma_tx_active(pi)) - udelay(100); - mpsc_sdma_intr_ack(pi); - mpsc_tx_intr(pi); - } - - while (mpsc_sdma_tx_active(pi)) - udelay(100); - - while (count > 0) { - bp = dp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); - - for (i = 0; i < MPSC_TXBE_SIZE; i++) { - if (count == 0) - break; - - if (add_cr) { - *(dp++) = '\r'; - add_cr = 0; - } else { - *(dp++) = *s; - - if (*(s++) == '\n') { /* add '\r' after '\n' */ - add_cr = 1; - count++; - } - } - - count--; - } - - dma_cache_sync(pi->port.dev, (void *)bp, MPSC_TXBE_SIZE, - DMA_BIDIRECTIONAL); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - flush_dcache_range((ulong)bp, - (ulong)bp + MPSC_TXBE_SIZE); -#endif - mpsc_setup_tx_desc(pi, i, 0); - pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1); - mpsc_sdma_start_tx(pi); - - while (mpsc_sdma_tx_active(pi)) - udelay(100); - - pi->txr_tail = (pi->txr_tail + 1) & (MPSC_TXR_ENTRIES - 1); - } - - spin_unlock_irqrestore(&pi->tx_lock, iflags); -} - -static int __init mpsc_console_setup(struct console *co, char *options) -{ - struct mpsc_port_info *pi; - int baud, bits, parity, flow; - - pr_debug("mpsc_console_setup[%d]: options: %s\n", co->index, options); - - if (co->index >= MPSC_NUM_CTLRS) - co->index = 0; - - pi = &mpsc_ports[co->index]; - - baud = pi->default_baud; - bits = pi->default_bits; - parity = pi->default_parity; - flow = pi->default_flow; - - if (!pi->port.ops) - return -ENODEV; - - spin_lock_init(&pi->port.lock); /* Temporary fix--copied from 8250.c */ - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&pi->port, co, baud, parity, bits, flow); -} - -static struct console mpsc_console = { - .name = MPSC_DEV_NAME, - .write = mpsc_console_write, - .device = uart_console_device, - .setup = mpsc_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &mpsc_reg, -}; - -static int __init mpsc_late_console_init(void) -{ - pr_debug("mpsc_late_console_init: Enter\n"); - - if (!(mpsc_console.flags & CON_ENABLED)) - register_console(&mpsc_console); - return 0; -} - -late_initcall(mpsc_late_console_init); - -#define MPSC_CONSOLE &mpsc_console -#else -#define MPSC_CONSOLE NULL -#endif -/* - ****************************************************************************** - * - * Dummy Platform Driver to extract & map shared register regions - * - ****************************************************************************** - */ -static void mpsc_resource_err(char *s) -{ - printk(KERN_WARNING "MPSC: Platform device resource error in %s\n", s); -} - -static int mpsc_shared_map_regs(struct platform_device *pd) -{ - struct resource *r; - - if ((r = platform_get_resource(pd, IORESOURCE_MEM, - MPSC_ROUTING_BASE_ORDER)) - && request_mem_region(r->start, - MPSC_ROUTING_REG_BLOCK_SIZE, - "mpsc_routing_regs")) { - mpsc_shared_regs.mpsc_routing_base = ioremap(r->start, - MPSC_ROUTING_REG_BLOCK_SIZE); - mpsc_shared_regs.mpsc_routing_base_p = r->start; - } else { - mpsc_resource_err("MPSC routing base"); - return -ENOMEM; - } - - if ((r = platform_get_resource(pd, IORESOURCE_MEM, - MPSC_SDMA_INTR_BASE_ORDER)) - && request_mem_region(r->start, - MPSC_SDMA_INTR_REG_BLOCK_SIZE, - "sdma_intr_regs")) { - mpsc_shared_regs.sdma_intr_base = ioremap(r->start, - MPSC_SDMA_INTR_REG_BLOCK_SIZE); - mpsc_shared_regs.sdma_intr_base_p = r->start; - } else { - iounmap(mpsc_shared_regs.mpsc_routing_base); - release_mem_region(mpsc_shared_regs.mpsc_routing_base_p, - MPSC_ROUTING_REG_BLOCK_SIZE); - mpsc_resource_err("SDMA intr base"); - return -ENOMEM; - } - - return 0; -} - -static void mpsc_shared_unmap_regs(void) -{ - if (mpsc_shared_regs.mpsc_routing_base) { - iounmap(mpsc_shared_regs.mpsc_routing_base); - release_mem_region(mpsc_shared_regs.mpsc_routing_base_p, - MPSC_ROUTING_REG_BLOCK_SIZE); - } - if (mpsc_shared_regs.sdma_intr_base) { - iounmap(mpsc_shared_regs.sdma_intr_base); - release_mem_region(mpsc_shared_regs.sdma_intr_base_p, - MPSC_SDMA_INTR_REG_BLOCK_SIZE); - } - - mpsc_shared_regs.mpsc_routing_base = NULL; - mpsc_shared_regs.sdma_intr_base = NULL; - - mpsc_shared_regs.mpsc_routing_base_p = 0; - mpsc_shared_regs.sdma_intr_base_p = 0; -} - -static int mpsc_shared_drv_probe(struct platform_device *dev) -{ - struct mpsc_shared_pdata *pdata; - int rc; - - if (dev->id != 0) - return -ENODEV; - - rc = mpsc_shared_map_regs(dev); - if (rc) - return rc; - - pdata = dev_get_platdata(&dev->dev); - - mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val; - mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val; - mpsc_shared_regs.MPSC_TCRR_m= pdata->tcrr_val; - mpsc_shared_regs.SDMA_INTR_CAUSE_m = pdata->intr_cause_val; - mpsc_shared_regs.SDMA_INTR_MASK_m = pdata->intr_mask_val; - - return 0; -} - -static int mpsc_shared_drv_remove(struct platform_device *dev) -{ - if (dev->id != 0) - return -ENODEV; - - mpsc_shared_unmap_regs(); - mpsc_shared_regs.MPSC_MRR_m = 0; - mpsc_shared_regs.MPSC_RCRR_m = 0; - mpsc_shared_regs.MPSC_TCRR_m = 0; - mpsc_shared_regs.SDMA_INTR_CAUSE_m = 0; - mpsc_shared_regs.SDMA_INTR_MASK_m = 0; - - return 0; -} - -static struct platform_driver mpsc_shared_driver = { - .probe = mpsc_shared_drv_probe, - .remove = mpsc_shared_drv_remove, - .driver = { - .name = MPSC_SHARED_NAME, - }, -}; - -/* - ****************************************************************************** - * - * Driver Interface Routines - * - ****************************************************************************** - */ -static struct uart_driver mpsc_reg = { - .owner = THIS_MODULE, - .driver_name = MPSC_DRIVER_NAME, - .dev_name = MPSC_DEV_NAME, - .major = MPSC_MAJOR, - .minor = MPSC_MINOR_START, - .nr = MPSC_NUM_CTLRS, - .cons = MPSC_CONSOLE, -}; - -static int mpsc_drv_map_regs(struct mpsc_port_info *pi, - struct platform_device *pd) -{ - struct resource *r; - - if ((r = platform_get_resource(pd, IORESOURCE_MEM, MPSC_BASE_ORDER)) - && request_mem_region(r->start, MPSC_REG_BLOCK_SIZE, - "mpsc_regs")) { - pi->mpsc_base = ioremap(r->start, MPSC_REG_BLOCK_SIZE); - pi->mpsc_base_p = r->start; - } else { - mpsc_resource_err("MPSC base"); - goto err; - } - - if ((r = platform_get_resource(pd, IORESOURCE_MEM, - MPSC_SDMA_BASE_ORDER)) - && request_mem_region(r->start, - MPSC_SDMA_REG_BLOCK_SIZE, "sdma_regs")) { - pi->sdma_base = ioremap(r->start,MPSC_SDMA_REG_BLOCK_SIZE); - pi->sdma_base_p = r->start; - } else { - mpsc_resource_err("SDMA base"); - goto err; - } - - if ((r = platform_get_resource(pd,IORESOURCE_MEM,MPSC_BRG_BASE_ORDER)) - && request_mem_region(r->start, - MPSC_BRG_REG_BLOCK_SIZE, "brg_regs")) { - pi->brg_base = ioremap(r->start, MPSC_BRG_REG_BLOCK_SIZE); - pi->brg_base_p = r->start; - } else { - mpsc_resource_err("BRG base"); - goto err; - } - return 0; - -err: - if (pi->sdma_base) { - iounmap(pi->sdma_base); - pi->sdma_base = NULL; - } - if (pi->mpsc_base) { - iounmap(pi->mpsc_base); - pi->mpsc_base = NULL; - } - return -ENOMEM; -} - -static void mpsc_drv_unmap_regs(struct mpsc_port_info *pi) -{ - if (pi->mpsc_base) { - iounmap(pi->mpsc_base); - release_mem_region(pi->mpsc_base_p, MPSC_REG_BLOCK_SIZE); - } - if (pi->sdma_base) { - iounmap(pi->sdma_base); - release_mem_region(pi->sdma_base_p, MPSC_SDMA_REG_BLOCK_SIZE); - } - if (pi->brg_base) { - iounmap(pi->brg_base); - release_mem_region(pi->brg_base_p, MPSC_BRG_REG_BLOCK_SIZE); - } - - pi->mpsc_base = NULL; - pi->sdma_base = NULL; - pi->brg_base = NULL; - - pi->mpsc_base_p = 0; - pi->sdma_base_p = 0; - pi->brg_base_p = 0; -} - -static void mpsc_drv_get_platform_data(struct mpsc_port_info *pi, - struct platform_device *pd, int num) -{ - struct mpsc_pdata *pdata; - - pdata = dev_get_platdata(&pd->dev); - - pi->port.uartclk = pdata->brg_clk_freq; - pi->port.iotype = UPIO_MEM; - pi->port.line = num; - pi->port.type = PORT_MPSC; - pi->port.fifosize = MPSC_TXBE_SIZE; - pi->port.membase = pi->mpsc_base; - pi->port.mapbase = (ulong)pi->mpsc_base; - pi->port.ops = &mpsc_pops; - - pi->mirror_regs = pdata->mirror_regs; - pi->cache_mgmt = pdata->cache_mgmt; - pi->brg_can_tune = pdata->brg_can_tune; - pi->brg_clk_src = pdata->brg_clk_src; - pi->mpsc_max_idle = pdata->max_idle; - pi->default_baud = pdata->default_baud; - pi->default_bits = pdata->default_bits; - pi->default_parity = pdata->default_parity; - pi->default_flow = pdata->default_flow; - - /* Initial values of mirrored regs */ - pi->MPSC_CHR_1_m = pdata->chr_1_val; - pi->MPSC_CHR_2_m = pdata->chr_2_val; - pi->MPSC_CHR_10_m = pdata->chr_10_val; - pi->MPSC_MPCR_m = pdata->mpcr_val; - pi->BRG_BCR_m = pdata->bcr_val; - - pi->shared_regs = &mpsc_shared_regs; - - pi->port.irq = platform_get_irq(pd, 0); -} - -static int mpsc_drv_probe(struct platform_device *dev) -{ - struct mpsc_port_info *pi; - int rc; - - dev_dbg(&dev->dev, "mpsc_drv_probe: Adding MPSC %d\n", dev->id); - - if (dev->id >= MPSC_NUM_CTLRS) - return -ENODEV; - - pi = &mpsc_ports[dev->id]; - - rc = mpsc_drv_map_regs(pi, dev); - if (rc) - return rc; - - mpsc_drv_get_platform_data(pi, dev, dev->id); - pi->port.dev = &dev->dev; - - rc = mpsc_make_ready(pi); - if (rc) - goto err_unmap; - - spin_lock_init(&pi->tx_lock); - rc = uart_add_one_port(&mpsc_reg, &pi->port); - if (rc) - goto err_relport; - - return 0; -err_relport: - mpsc_release_port(&pi->port); -err_unmap: - mpsc_drv_unmap_regs(pi); - return rc; -} - -static struct platform_driver mpsc_driver = { - .probe = mpsc_drv_probe, - .driver = { - .name = MPSC_CTLR_NAME, - .suppress_bind_attrs = true, - }, -}; - -static int __init mpsc_drv_init(void) -{ - int rc; - - printk(KERN_INFO "Serial: MPSC driver\n"); - - memset(mpsc_ports, 0, sizeof(mpsc_ports)); - memset(&mpsc_shared_regs, 0, sizeof(mpsc_shared_regs)); - - rc = uart_register_driver(&mpsc_reg); - if (rc) - return rc; - - rc = platform_driver_register(&mpsc_shared_driver); - if (rc) - goto err_unreg_uart; - - rc = platform_driver_register(&mpsc_driver); - if (rc) - goto err_unreg_plat; - - return 0; -err_unreg_plat: - platform_driver_unregister(&mpsc_shared_driver); -err_unreg_uart: - uart_unregister_driver(&mpsc_reg); - return rc; -} -device_initcall(mpsc_drv_init); - -/* -MODULE_AUTHOR("Mark A. Greer "); -MODULE_DESCRIPTION("Generic Marvell MPSC serial/UART driver"); -MODULE_LICENSE("GPL"); -*/ diff --git a/include/linux/mv643xx.h b/include/linux/mv643xx.h index 4471cf96ef69..47e5679b48e1 100644 --- a/include/linux/mv643xx.h +++ b/include/linux/mv643xx.h @@ -918,52 +918,6 @@ extern void mv64340_irq_init(unsigned int base); -/* MPSC Platform Device, Driver Data (Shared register regions) */ -#define MPSC_SHARED_NAME "mpsc_shared" - -#define MPSC_ROUTING_BASE_ORDER 0 -#define MPSC_SDMA_INTR_BASE_ORDER 1 - -#define MPSC_ROUTING_REG_BLOCK_SIZE 0x000c -#define MPSC_SDMA_INTR_REG_BLOCK_SIZE 0x0084 - -struct mpsc_shared_pdata { - u32 mrr_val; - u32 rcrr_val; - u32 tcrr_val; - u32 intr_cause_val; - u32 intr_mask_val; -}; - -/* MPSC Platform Device, Driver Data */ -#define MPSC_CTLR_NAME "mpsc" - -#define MPSC_BASE_ORDER 0 -#define MPSC_SDMA_BASE_ORDER 1 -#define MPSC_BRG_BASE_ORDER 2 - -#define MPSC_REG_BLOCK_SIZE 0x0038 -#define MPSC_SDMA_REG_BLOCK_SIZE 0x0c18 -#define MPSC_BRG_REG_BLOCK_SIZE 0x0008 - -struct mpsc_pdata { - u8 mirror_regs; - u8 cache_mgmt; - u8 max_idle; - int default_baud; - int default_bits; - int default_parity; - int default_flow; - u32 chr_1_val; - u32 chr_2_val; - u32 chr_10_val; - u32 mpcr_val; - u32 bcr_val; - u8 brg_can_tune; - u8 brg_clk_src; - u32 brg_clk_freq; -}; - /* Watchdog Platform Device, Driver Data */ #define MV64x60_WDT_NAME "mv64x60_wdt" diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 67c4aaaa2308..5642c05e0da0 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -129,7 +129,7 @@ /* Motorola i.MX SoC */ #define PORT_IMX 62 -/* Marvell MPSC */ +/* Marvell MPSC (obsolete unused) */ #define PORT_MPSC 63 /* TXX9 type number */ -- cgit v1.2.3-55-g7522 From 8493eab02608b0e82f67b892aa72882e510c31d0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 24 Jun 2019 14:35:39 +0200 Subject: serial: sh-sci: Fix TX DMA buffer flushing and workqueue races When uart_flush_buffer() is called, the .flush_buffer() callback zeroes the tx_dma_len field. This may race with the work queue function handling transmit DMA requests: 1. If the buffer is flushed before the first DMA API call, dmaengine_prep_slave_single() may be called with a zero length, causing the DMA request to never complete, leading to messages like: rcar-dmac e7300000.dma-controller: Channel Address Error happen and, with debug enabled: sh-sci e6e88000.serial: sci_dma_tx_work_fn: ffff800639b55000: 0...0, cookie 126 and DMA timeouts. 2. If the buffer is flushed after the first DMA API call, but before the second, dma_sync_single_for_device() may be called with a zero length, causing the transmit data not to be flushed to RAM, and leading to stale data being output. Fix this by: 1. Letting sci_dma_tx_work_fn() return immediately if the transmit buffer is empty, 2. Extending the critical section to cover all DMA preparational work, so tx_dma_len stays consistent for all of it, 3. Using local copies of circ_buf.head and circ_buf.tail, to make sure they match the actual operation above. Reported-by: Eugeniu Rosca Suggested-by: Yoshihiro Shimoda Signed-off-by: Geert Uytterhoeven Reviewed-by: Eugeniu Rosca Tested-by: Eugeniu Rosca Link: https://lore.kernel.org/r/20190624123540.20629-2-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index abc705716aa0..d4504daff992 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1398,6 +1398,7 @@ static void sci_dma_tx_work_fn(struct work_struct *work) struct circ_buf *xmit = &port->state->xmit; unsigned long flags; dma_addr_t buf; + int head, tail; /* * DMA is idle now. @@ -1407,16 +1408,23 @@ static void sci_dma_tx_work_fn(struct work_struct *work) * consistent xmit buffer state. */ spin_lock_irq(&port->lock); - buf = s->tx_dma_addr + (xmit->tail & (UART_XMIT_SIZE - 1)); + head = xmit->head; + tail = xmit->tail; + buf = s->tx_dma_addr + (tail & (UART_XMIT_SIZE - 1)); s->tx_dma_len = min_t(unsigned int, - CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), - CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); - spin_unlock_irq(&port->lock); + CIRC_CNT(head, tail, UART_XMIT_SIZE), + CIRC_CNT_TO_END(head, tail, UART_XMIT_SIZE)); + if (!s->tx_dma_len) { + /* Transmit buffer has been flushed */ + spin_unlock_irq(&port->lock); + return; + } desc = dmaengine_prep_slave_single(chan, buf, s->tx_dma_len, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { + spin_unlock_irq(&port->lock); dev_warn(port->dev, "Failed preparing Tx DMA descriptor\n"); goto switch_to_pio; } @@ -1424,18 +1432,18 @@ static void sci_dma_tx_work_fn(struct work_struct *work) dma_sync_single_for_device(chan->device->dev, buf, s->tx_dma_len, DMA_TO_DEVICE); - spin_lock_irq(&port->lock); desc->callback = sci_dma_tx_complete; desc->callback_param = s; - spin_unlock_irq(&port->lock); s->cookie_tx = dmaengine_submit(desc); if (dma_submit_error(s->cookie_tx)) { + spin_unlock_irq(&port->lock); dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n"); goto switch_to_pio; } + spin_unlock_irq(&port->lock); dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", - __func__, xmit->buf, xmit->tail, xmit->head, s->cookie_tx); + __func__, xmit->buf, tail, head, s->cookie_tx); dma_async_issue_pending(chan); return; -- cgit v1.2.3-55-g7522 From 775b7ffd7d6d5db320d99b0a485c51e04dfcf9f1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 24 Jun 2019 14:35:40 +0200 Subject: serial: sh-sci: Terminate TX DMA during buffer flushing While the .flush_buffer() callback clears sci_port.tx_dma_len since commit 1cf4a7efdc71cab8 ("serial: sh-sci: Fix race condition causing garbage during shutdown"), it does not terminate a transmit DMA operation that may be in progress. Fix this by terminating any pending DMA operations, and resetting the corresponding cookie. Signed-off-by: Geert Uytterhoeven Reviewed-by: Eugeniu Rosca Tested-by: Eugeniu Rosca Link: https://lore.kernel.org/r/20190624123540.20629-3-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index d4504daff992..d18c680aa64b 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1656,11 +1656,18 @@ static void sci_free_dma(struct uart_port *port) static void sci_flush_buffer(struct uart_port *port) { + struct sci_port *s = to_sci_port(port); + /* * In uart_flush_buffer(), the xmit circular buffer has just been - * cleared, so we have to reset tx_dma_len accordingly. + * cleared, so we have to reset tx_dma_len accordingly, and stop any + * pending transfers */ - to_sci_port(port)->tx_dma_len = 0; + s->tx_dma_len = 0; + if (s->chan_tx) { + dmaengine_terminate_async(s->chan_tx); + s->cookie_tx = -EINVAL; + } } #else /* !CONFIG_SERIAL_SH_SCI_DMA */ static inline void sci_request_dma(struct uart_port *port) -- cgit v1.2.3-55-g7522 From 41ffa48ea7b21330d2e7d87558bb710700da4609 Mon Sep 17 00:00:00 2001 From: Sergey Organov Date: Wed, 26 Jun 2019 17:11:28 +0300 Subject: serial: imx: set_termios(): factor-out 'ucr2' initial value Set common bits in a separate statement to make initialization explicit and not repeat the common part. Reviewed-by: Sascha Hauer Tested-by: Sascha Hauer Signed-off-by: Sergey Organov Reviewed-by: Uwe Kleine-Knig Link: https://lore.kernel.org/r/1561558293-7683-3-git-send-email-sorganov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 52374b37d272..df2186cac615 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1562,10 +1562,9 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, spin_lock_irqsave(&sport->port.lock, flags); + ucr2 = UCR2_SRST | UCR2_IRTS; if ((termios->c_cflag & CSIZE) == CS8) - ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS; - else - ucr2 = UCR2_SRST | UCR2_IRTS; + ucr2 |= UCR2_WS; if (termios->c_cflag & CRTSCTS) { if (sport->have_rtscts) { -- cgit v1.2.3-55-g7522 From ddf89e7503deb931403add648b67545ec196c3fe Mon Sep 17 00:00:00 2001 From: Sergey Organov Date: Wed, 26 Jun 2019 17:11:29 +0300 Subject: serial: imx: set_termios(): clarify RTS/CTS bits calculation Avoid repeating the same code for rs485 twice. Make it obvious we clear CRTSCTS bit in termios->c_cflag whenever sport->have_rtscts is false. Make it obvious we clear UCR2_IRTS whenever CRTSCTS is set. Reviewed-by: Sascha Hauer Tested-by: Sascha Hauer Signed-off-by: Sergey Organov Link: https://lore.kernel.org/r/1561558293-7683-4-git-send-email-sorganov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index df2186cac615..1cb891b4eaa0 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1566,35 +1566,25 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, if ((termios->c_cflag & CSIZE) == CS8) ucr2 |= UCR2_WS; - if (termios->c_cflag & CRTSCTS) { - if (sport->have_rtscts) { - ucr2 &= ~UCR2_IRTS; - - if (port->rs485.flags & SER_RS485_ENABLED) { - /* - * RTS is mandatory for rs485 operation, so keep - * it under manual control and keep transmitter - * disabled. - */ - if (port->rs485.flags & - SER_RS485_RTS_AFTER_SEND) - imx_uart_rts_active(sport, &ucr2); - else - imx_uart_rts_inactive(sport, &ucr2); - } else { - imx_uart_rts_auto(sport, &ucr2); - } - } else { - termios->c_cflag &= ~CRTSCTS; - } - } else if (port->rs485.flags & SER_RS485_ENABLED) { - /* disable transmitter */ + if (!sport->have_rtscts) + termios->c_cflag &= ~CRTSCTS; + + if (port->rs485.flags & SER_RS485_ENABLED) { + /* + * RTS is mandatory for rs485 operation, so keep + * it under manual control and keep transmitter + * disabled. + */ if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) imx_uart_rts_active(sport, &ucr2); else imx_uart_rts_inactive(sport, &ucr2); - } + } else if (termios->c_cflag & CRTSCTS) + imx_uart_rts_auto(sport, &ucr2); + + if (termios->c_cflag & CRTSCTS) + ucr2 &= ~UCR2_IRTS; if (termios->c_cflag & CSTOPB) ucr2 |= UCR2_STPB; -- cgit v1.2.3-55-g7522 From 011bd05d1f5d99eeb91e8a6f81f00654a2b8d3d1 Mon Sep 17 00:00:00 2001 From: Sergey Organov Date: Wed, 26 Jun 2019 17:11:30 +0300 Subject: serial: imx: set_termios(): preserve RTS state imx_set_termios() cleared RTS on every call, now fixed. Reviewed-by: Sascha Hauer Tested-by: Sascha Hauer Signed-off-by: Sergey Organov Link: https://lore.kernel.org/r/1561558293-7683-5-git-send-email-sorganov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 1cb891b4eaa0..57d6e6ba556e 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1562,7 +1562,14 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, spin_lock_irqsave(&sport->port.lock, flags); - ucr2 = UCR2_SRST | UCR2_IRTS; + /* + * Read current UCR2 and save it for future use, then clear all the bits + * except those we will or may need to preserve. + */ + old_ucr2 = imx_uart_readl(sport, UCR2); + ucr2 = old_ucr2 & (UCR2_TXEN | UCR2_RXEN | UCR2_ATEN | UCR2_CTS); + + ucr2 |= UCR2_SRST | UCR2_IRTS; if ((termios->c_cflag & CSIZE) == CS8) ucr2 |= UCR2_WS; @@ -1631,7 +1638,6 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, imx_uart_writel(sport, old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), UCR1); - old_ucr2 = imx_uart_readl(sport, UCR2); imx_uart_writel(sport, old_ucr2 & ~UCR2_ATEN, UCR2); while (!(imx_uart_readl(sport, USR2) & USR2_TXDC)) @@ -1639,7 +1645,6 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, /* then, disable everything */ imx_uart_writel(sport, old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN | UCR2_ATEN), UCR2); - old_ucr2 &= (UCR2_TXEN | UCR2_RXEN | UCR2_ATEN); /* custom-baudrate handling */ div = sport->port.uartclk / (baud * 16); @@ -1677,8 +1682,7 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, imx_uart_writel(sport, old_ucr1, UCR1); - /* set the parity, stop bits and data size */ - imx_uart_writel(sport, ucr2 | old_ucr2, UCR2); + imx_uart_writel(sport, ucr2, UCR2); if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) imx_uart_enable_ms(&sport->port); -- cgit v1.2.3-55-g7522 From 35a4ed0164e992c9c7b82eb1370081a292131904 Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Thu, 4 Jul 2019 21:40:07 +0800 Subject: tty: serial: fsl_lpuart: add imx8qxp support The lpuart of imx8ulp is basically the same as imx7ulp, but it has new feature support based on imx7ulp, like it can assert a DMA request on EOP(end-of-packet). imx8ulp lpuart use two clocks, one is ipg bus clock that is used to access registers, the other is baud clock that is used to transmit-receive data. Signed-off-by: Fugang Duan Link: https://lore.kernel.org/r/20190704134007.2316-1-fugang.duan@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 106 ++++++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 20 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 2f24d9796d20..92dad2b4ec36 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -234,9 +234,18 @@ static DEFINE_IDA(fsl_lpuart_ida); +enum lpuart_type { + VF610_LPUART, + LS1021A_LPUART, + IMX7ULP_LPUART, + IMX8QXP_LPUART, +}; + struct lpuart_port { struct uart_port port; - struct clk *clk; + enum lpuart_type devtype; + struct clk *ipg_clk; + struct clk *baud_clk; unsigned int txfifo_size; unsigned int rxfifo_size; @@ -261,19 +270,29 @@ struct lpuart_port { }; struct lpuart_soc_data { - char iotype; - u8 reg_off; + enum lpuart_type devtype; + char iotype; + u8 reg_off; }; static const struct lpuart_soc_data vf_data = { + .devtype = VF610_LPUART, .iotype = UPIO_MEM, }; static const struct lpuart_soc_data ls_data = { + .devtype = LS1021A_LPUART, .iotype = UPIO_MEM32BE, }; -static struct lpuart_soc_data imx_data = { +static struct lpuart_soc_data imx7ulp_data = { + .devtype = IMX7ULP_LPUART, + .iotype = UPIO_MEM32, + .reg_off = IMX_REG_OFF, +}; + +static struct lpuart_soc_data imx8qxp_data = { + .devtype = IMX8QXP_LPUART, .iotype = UPIO_MEM32, .reg_off = IMX_REG_OFF, }; @@ -281,7 +300,8 @@ static struct lpuart_soc_data imx_data = { static const struct of_device_id lpuart_dt_ids[] = { { .compatible = "fsl,vf610-lpuart", .data = &vf_data, }, { .compatible = "fsl,ls1021a-lpuart", .data = &ls_data, }, - { .compatible = "fsl,imx7ulp-lpuart", .data = &imx_data, }, + { .compatible = "fsl,imx7ulp-lpuart", .data = &imx7ulp_data, }, + { .compatible = "fsl,imx8qxp-lpuart", .data = &imx8qxp_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, lpuart_dt_ids); @@ -289,6 +309,11 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids); /* Forward declare this for the dma callbacks*/ static void lpuart_dma_tx_complete(void *arg); +static inline bool is_imx8qxp_lpuart(struct lpuart_port *sport) +{ + return sport->devtype == IMX8QXP_LPUART; +} + static inline u32 lpuart32_read(struct uart_port *port, u32 off) { switch (port->iotype) { @@ -314,6 +339,39 @@ static inline void lpuart32_write(struct uart_port *port, u32 val, } } +static int __lpuart_enable_clks(struct lpuart_port *sport, bool is_en) +{ + int ret = 0; + + if (is_en) { + ret = clk_prepare_enable(sport->ipg_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(sport->baud_clk); + if (ret) { + clk_disable_unprepare(sport->ipg_clk); + return ret; + } + } else { + clk_disable_unprepare(sport->baud_clk); + clk_disable_unprepare(sport->ipg_clk); + } + + return 0; +} + +static unsigned int lpuart_get_baud_clk_rate(struct lpuart_port *sport) +{ + if (is_imx8qxp_lpuart(sport)) + return clk_get_rate(sport->baud_clk); + + return clk_get_rate(sport->ipg_clk); +} + +#define lpuart_enable_clks(x) __lpuart_enable_clks(x, true) +#define lpuart_disable_clks(x) __lpuart_enable_clks(x, false) + static void lpuart_stop_tx(struct uart_port *port) { unsigned char temp; @@ -2069,7 +2127,7 @@ lpuart_console_get_options(struct lpuart_port *sport, int *baud, brfa = readb(sport->port.membase + UARTCR4); brfa &= UARTCR4_BRFA_MASK; - uartclk = clk_get_rate(sport->clk); + uartclk = lpuart_get_baud_clk_rate(sport); /* * baud = mod_clk/(16*(sbr[13]+(brfa)/32) */ @@ -2112,7 +2170,7 @@ lpuart32_console_get_options(struct lpuart_port *sport, int *baud, bd = lpuart32_read(&sport->port, UARTBAUD); bd &= UARTBAUD_SBR_MASK; sbr = bd; - uartclk = clk_get_rate(sport->clk); + uartclk = lpuart_get_baud_clk_rate(sport); /* * baud = mod_clk/(16*(sbr[13]+(brfa)/32) */ @@ -2286,6 +2344,7 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.mapbase = res->start; sport->port.dev = &pdev->dev; sport->port.type = PORT_LPUART; + sport->devtype = sdata->devtype; ret = platform_get_irq(pdev, 0); if (ret < 0) { dev_err(&pdev->dev, "cannot obtain irq\n"); @@ -2301,20 +2360,27 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.rs485_config = lpuart_config_rs485; - sport->clk = devm_clk_get(&pdev->dev, "ipg"); - if (IS_ERR(sport->clk)) { - ret = PTR_ERR(sport->clk); - dev_err(&pdev->dev, "failed to get uart clk: %d\n", ret); + sport->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(sport->ipg_clk)) { + ret = PTR_ERR(sport->ipg_clk); + dev_err(&pdev->dev, "failed to get uart ipg clk: %d\n", ret); return ret; } - ret = clk_prepare_enable(sport->clk); - if (ret) { - dev_err(&pdev->dev, "failed to enable uart clk: %d\n", ret); - return ret; + sport->baud_clk = NULL; + if (is_imx8qxp_lpuart(sport)) { + sport->baud_clk = devm_clk_get(&pdev->dev, "baud"); + if (IS_ERR(sport->baud_clk)) { + ret = PTR_ERR(sport->baud_clk); + dev_err(&pdev->dev, "failed to get uart baud clk: %d\n", ret); + return ret; + } } - sport->port.uartclk = clk_get_rate(sport->clk); + ret = lpuart_enable_clks(sport); + if (ret) + return ret; + sport->port.uartclk = lpuart_get_baud_clk_rate(sport); lpuart_ports[sport->port.line] = sport; @@ -2362,7 +2428,7 @@ static int lpuart_probe(struct platform_device *pdev) failed_attach_port: failed_irq_request: - clk_disable_unprepare(sport->clk); + lpuart_disable_clks(sport); return ret; } @@ -2374,7 +2440,7 @@ static int lpuart_remove(struct platform_device *pdev) ida_simple_remove(&fsl_lpuart_ida, sport->port.line); - clk_disable_unprepare(sport->clk); + lpuart_disable_clks(sport); if (sport->dma_tx_chan) dma_release_channel(sport->dma_tx_chan); @@ -2439,7 +2505,7 @@ static int lpuart_suspend(struct device *dev) } if (sport->port.suspended && !irq_wake) - clk_disable_unprepare(sport->clk); + lpuart_disable_clks(sport); return 0; } @@ -2451,7 +2517,7 @@ static int lpuart_resume(struct device *dev) unsigned long temp; if (sport->port.suspended && !irq_wake) - clk_prepare_enable(sport->clk); + lpuart_enable_clks(sport); if (lpuart_is_32(sport)) { lpuart32_setup_watermark(sport); -- cgit v1.2.3-55-g7522