This patch adds RXBS register access support for zynqmp. To avoid the corner error conditions it will consider only RXBS[2:0] bits while checking the error conditions (Parity,Framing and BRAK). Signed-off-by: Nava kishore Manne <navam@xxxxxxxxxx> --- drivers/tty/serial/xilinx_uartps.c | 101 +++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 20 deletions(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 9ca1a4d..efd7f73 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -57,7 +57,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); #define CDNS_UART_IMR 0x10 /* Interrupt Mask */ #define CDNS_UART_ISR 0x14 /* Interrupt Status */ #define CDNS_UART_BAUDGEN 0x18 /* Baud Rate Generator */ -#define CDNS_UART_RXTOUT 0x1C /* RX Timeout */ +#define CDNS_UART_RXTOUT 0x1C /* RX Timeout */ #define CDNS_UART_RXWM 0x20 /* RX FIFO Trigger Level */ #define CDNS_UART_MODEMCR 0x24 /* Modem Control */ #define CDNS_UART_MODEMSR 0x28 /* Modem Status */ @@ -68,6 +68,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); #define CDNS_UART_IRRX_PWIDTH 0x3C /* IR Min Received Pulse Width */ #define CDNS_UART_IRTX_PWIDTH 0x40 /* IR Transmitted pulse Width */ #define CDNS_UART_TXWM 0x44 /* TX FIFO Trigger Level */ +#define CDNS_UART_RXBS 0x48 /* RX FIFO byte status register */ /* Control Register Bit Definitions */ #define CDNS_UART_CR_STOPBRK 0x00000100 /* Stop TX break */ @@ -79,6 +80,9 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); #define CDNS_UART_CR_TXRST 0x00000002 /* TX logic reset */ #define CDNS_UART_CR_RXRST 0x00000001 /* RX logic reset */ #define CDNS_UART_CR_RST_TO 0x00000040 /* Restart Timeout Counter */ +#define CDNS_UART_RXBS_PARITY 0x00000001 /* Parity error status */ +#define CDNS_UART_RXBS_FRAMING 0x00000002 /* Framing error status */ +#define CDNS_UART_RXBS_BRK 0x00000004 /* Overrun error status */ /* * Mode Register: @@ -131,8 +135,9 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); CDNS_UART_IXR_TOUT) /* Goes in read_status_mask for break detection as the HW doesn't do it*/ -#define CDNS_UART_IXR_BRK 0x80000000 +#define CDNS_UART_IXR_BRK 0x00002000 +#define CDNS_UART_RXBS_SUPPORT BIT(1) /* * Modem Control register: * The read/write Modem Control register controls the interface with the modem @@ -172,18 +177,29 @@ struct cdns_uart { struct clk *pclk; unsigned int baud; struct notifier_block clk_rate_change_nb; + u32 quirks; +}; +struct cdns_platform_data { + u32 quirks; }; #define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \ clk_rate_change_nb); static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus) { + struct cdns_uart *cdns_uart = port->private_data; + bool is_rxbs_support; + unsigned int rxbs_status; + unsigned int status_mask; + + is_rxbs_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT; + /* * There is no hardware break detection, so we interpret framing * error with all-zeros data as a break sequence. Most of the time, * there's another non-zero byte at the end of the sequence. */ - if (isrstatus & CDNS_UART_IXR_FRAMING) { + if (!is_rxbs_support && (isrstatus & CDNS_UART_IXR_FRAMING)) { while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY)) { if (!readl(port->membase + CDNS_UART_FIFO)) { @@ -200,6 +216,8 @@ static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus) isrstatus &= port->read_status_mask; isrstatus &= ~port->ignore_status_mask; + status_mask = port->read_status_mask; + status_mask &= ~port->ignore_status_mask; if (!(isrstatus & (CDNS_UART_IXR_TOUT | CDNS_UART_IXR_RXTRIG))) return; @@ -208,6 +226,9 @@ static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus) u32 data; char status = TTY_NORMAL; + if (is_rxbs_support) + rxbs_status = readl(port->membase + CDNS_UART_RXBS); + data = readl(port->membase + CDNS_UART_FIFO); /* Non-NULL byte after BREAK is garbage (99%) */ @@ -217,21 +238,41 @@ static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus) if (uart_handle_break(port)) continue; } + if (is_rxbs_support && (rxbs_status & CDNS_UART_RXBS_BRK)) { + port->icount.brk++; + status = TTY_BREAK; + if (uart_handle_break(port)) + continue; + } if (uart_handle_sysrq_char(port, data)) continue; port->icount.rx++; - if (isrstatus & CDNS_UART_IXR_PARITY) { - port->icount.parity++; - status = TTY_PARITY; - } else if (isrstatus & CDNS_UART_IXR_FRAMING) { - port->icount.frame++; - status = TTY_FRAME; - } else if (isrstatus & CDNS_UART_IXR_OVERRUN) { - port->icount.overrun++; + if (is_rxbs_support) { + if ((rxbs_status & CDNS_UART_RXBS_PARITY) + && (status_mask & CDNS_UART_IXR_PARITY)) { + port->icount.parity++; + status = TTY_PARITY; + } + if ((rxbs_status & CDNS_UART_RXBS_FRAMING) + && (status_mask & CDNS_UART_IXR_PARITY)) { + port->icount.frame++; + status = TTY_FRAME; + } + } else { + if (isrstatus & CDNS_UART_IXR_PARITY) { + port->icount.parity++; + status = TTY_PARITY; + } + if (isrstatus & CDNS_UART_IXR_FRAMING) { + port->icount.frame++; + status = TTY_FRAME; + } } + if (isrstatus & CDNS_UART_IXR_OVERRUN) + port->icount.overrun++; uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN, data, status); @@ -736,10 +777,14 @@ static void cdns_uart_set_termios(struct uart_port *port, */ static int cdns_uart_startup(struct uart_port *port) { + struct cdns_uart *cdns_uart = port->private_data; + bool is_brk_support; int ret; unsigned long flags; unsigned int status = 0; + is_brk_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT; + spin_lock_irqsave(&port->lock, flags); /* Disable the TX and RX */ @@ -794,7 +839,11 @@ static int cdns_uart_startup(struct uart_port *port) } /* Set the Interrupt Registers with desired interrupts */ - writel(CDNS_UART_RX_IRQS, port->membase + CDNS_UART_IER); + if (is_brk_support) + writel(CDNS_UART_RX_IRQS | CDNS_UART_IXR_BRK, + port->membase + CDNS_UART_IER); + else + writel(CDNS_UART_RX_IRQS, port->membase + CDNS_UART_IER); return 0; } @@ -1328,6 +1377,18 @@ static int cdns_uart_resume(struct device *device) static SIMPLE_DEV_PM_OPS(cdns_uart_dev_pm_ops, cdns_uart_suspend, cdns_uart_resume); +static const struct cdns_platform_data zynqmp_uart_def = { + .quirks = CDNS_UART_RXBS_SUPPORT, }; + +/* Match table for of_platform binding */ +static const struct of_device_id cdns_uart_of_match[] = { + { .compatible = "xlnx,xuartps", }, + { .compatible = "cdns,uart-r1p8", }, + { .compatible = "cdns,uart-r1p12", .data = &zynqmp_uart_def }, + {} +}; +MODULE_DEVICE_TABLE(of, cdns_uart_of_match); + /** * cdns_uart_probe - Platform driver probe * @pdev: Pointer to the platform device structure @@ -1340,12 +1401,20 @@ static int cdns_uart_probe(struct platform_device *pdev) struct uart_port *port; struct resource *res; struct cdns_uart *cdns_uart_data; + const struct of_device_id *match; cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data), GFP_KERNEL); if (!cdns_uart_data) return -ENOMEM; + match = of_match_node(cdns_uart_of_match, pdev->dev.of_node); + if (match && match->data) { + const struct cdns_platform_data *data = match->data; + + cdns_uart_data->quirks = data->quirks; + } + cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "pclk"); if (IS_ERR(cdns_uart_data->pclk)) { cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "aper_clk"); @@ -1471,14 +1540,6 @@ static int cdns_uart_remove(struct platform_device *pdev) return rc; } -/* Match table for of_platform binding */ -static const struct of_device_id cdns_uart_of_match[] = { - { .compatible = "xlnx,xuartps", }, - { .compatible = "cdns,uart-r1p8", }, - {} -}; -MODULE_DEVICE_TABLE(of, cdns_uart_of_match); - static struct platform_driver cdns_uart_platform_driver = { .probe = cdns_uart_probe, .remove = cdns_uart_remove, -- 2.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html