From 909102db44faf40bae96d8f9d23de79e1ca0853a Mon Sep 17 00:00:00 2001 From: Barry Song Date: Wed, 7 Aug 2013 13:35:38 +0800 Subject: serial: sirf: add support for Marco chip the marco and coming new CSR multiple SoCs have SET/CLR pair for INTEN registers to avoid some read-modify-write. this patch adds support for this and make the driver support current up and coming mp SoCs. Signed-off-by: Barry Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 76 +++++++++++++++++++++++++++++++-------- drivers/tty/serial/sirfsoc_uart.h | 3 ++ 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 67a0d1b..26a3bc834 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -139,40 +139,66 @@ static void sirfsoc_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) static void sirfsoc_uart_stop_tx(struct uart_port *port) { + struct sirfsoc_uart_port *sirfport = to_sirfport(port); unsigned int regv; - regv = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_TX_INT_EN); + + if (!sirfport->is_marco) { + regv = rd_regl(port, SIRFUART_INT_EN); + wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_TX_INT_EN); + } else { + wr_regl(port, SIRFUART_INT_EN_CLR, SIRFUART_TX_INT_EN); + } } void sirfsoc_uart_start_tx(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); unsigned long regv; + sirfsoc_uart_pio_tx_chars(sirfport, 1); wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_START); - regv = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_TX_INT_EN); + + if (!sirfport->is_marco) { + regv = rd_regl(port, SIRFUART_INT_EN); + wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_TX_INT_EN); + } else { + wr_regl(port, SIRFUART_INT_EN, SIRFUART_TX_INT_EN); + } } static void sirfsoc_uart_stop_rx(struct uart_port *port) { + struct sirfsoc_uart_port *sirfport = to_sirfport(port); unsigned long regv; + wr_regl(port, SIRFUART_RX_FIFO_OP, 0); - regv = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_RX_IO_INT_EN); + + if (!sirfport->is_marco) { + regv = rd_regl(port, SIRFUART_INT_EN); + wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_RX_IO_INT_EN); + } else { + wr_regl(port, SIRFUART_INT_EN_CLR, SIRFUART_RX_IO_INT_EN); + } } static void sirfsoc_uart_disable_ms(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); unsigned long reg; + sirfport->ms_enabled = 0; if (!sirfport->hw_flow_ctrl) return; + reg = rd_regl(port, SIRFUART_AFC_CTRL); wr_regl(port, SIRFUART_AFC_CTRL, reg & ~0x3FF); - reg = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, reg & ~SIRFUART_CTS_INT_EN); + + if (!sirfport->is_marco) { + reg = rd_regl(port, SIRFUART_INT_EN); + wr_regl(port, SIRFUART_INT_EN, reg & ~SIRFUART_CTS_INT_EN); + } else { + wr_regl(port, SIRFUART_INT_EN_CLR, SIRFUART_CTS_INT_EN); + } } static void sirfsoc_uart_enable_ms(struct uart_port *port) @@ -180,13 +206,20 @@ static void sirfsoc_uart_enable_ms(struct uart_port *port) struct sirfsoc_uart_port *sirfport = to_sirfport(port); unsigned long reg; unsigned long flg; + if (!sirfport->hw_flow_ctrl) return; flg = SIRFUART_AFC_RX_EN | SIRFUART_AFC_TX_EN; reg = rd_regl(port, SIRFUART_AFC_CTRL); wr_regl(port, SIRFUART_AFC_CTRL, reg | flg); - reg = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, reg | SIRFUART_CTS_INT_EN); + + if (!sirfport->is_marco) { + reg = rd_regl(port, SIRFUART_INT_EN); + wr_regl(port, SIRFUART_INT_EN, reg | SIRFUART_CTS_INT_EN); + } else { + wr_regl(port, SIRFUART_INT_EN, SIRFUART_CTS_INT_EN); + } + uart_handle_cts_change(port, !(rd_regl(port, SIRFUART_AFC_CTRL) & SIRFUART_CTS_IN_STATUS)); sirfport->ms_enabled = 1; @@ -313,9 +346,16 @@ recv_char: static void sirfsoc_uart_start_rx(struct uart_port *port) { - unsigned long regv; - regv = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_RX_IO_INT_EN); + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + + if (!sirfport->is_marco) { + unsigned long regv; + regv = rd_regl(port, SIRFUART_INT_EN); + wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_RX_IO_INT_EN); + } else { + wr_regl(port, SIRFUART_INT_EN, SIRFUART_RX_IO_INT_EN); + } + wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET); wr_regl(port, SIRFUART_RX_FIFO_OP, 0); wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START); @@ -513,7 +553,12 @@ irq_err: static void sirfsoc_uart_shutdown(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - wr_regl(port, SIRFUART_INT_EN, 0); + + if (!sirfport->is_marco) + wr_regl(port, SIRFUART_INT_EN, 0); + else + wr_regl(port, SIRFUART_INT_EN_CLR, ~0UL); + free_irq(port->irq, sirfport); if (sirfport->ms_enabled) { sirfsoc_uart_disable_ms(port); @@ -652,6 +697,9 @@ int sirfsoc_uart_probe(struct platform_device *pdev) port->dev = &pdev->dev; port->private_data = sirfport; + if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-uart")) + sirfport->is_marco = true; + if (of_find_property(pdev->dev.of_node, "hw_flow_ctrl", NULL)) sirfport->hw_flow_ctrl = 1; diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index 85328ba..6216660 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -13,6 +13,7 @@ #define SIRFUART_DIVISOR 0x0050 #define SIRFUART_INT_EN 0x0054 #define SIRFUART_INT_STATUS 0x0058 +#define SIRFUART_INT_EN_CLR 0x0060 #define SIRFUART_TX_DMA_IO_CTRL 0x0100 #define SIRFUART_TX_DMA_IO_LEN 0x0104 #define SIRFUART_TX_FIFO_CTRL 0x0108 @@ -164,6 +165,8 @@ struct sirfsoc_uart_port { struct uart_port port; struct pinctrl *p; struct clk *clk; + /* for SiRFmarco, there are SET/CLR for UART_INT_EN */ + bool is_marco; }; /* Hardware Flow Control */ -- cgit v1.1