[PATCH 05/16] serial: mvebu-uart: use a generic way to access the registers
Gregory CLEMENT
gregory.clement at free-electrons.com
Fri Oct 6 05:32:18 PDT 2017
Hi Miquel,
On ven., oct. 06 2017, Miquel Raynal <miquel.raynal at free-electrons.com> wrote:
> There are two UART ports on Armada3700. The second UART is based on the
> first one, plus additional features, but it has a different register
> layout (some bit fields are also moved inside the registers).
>
> Clearly separate register offsets and bit fields that differ between the
> standard and the extended IP. Access them in a generic way. Rename the
> defines with the "STD" prefix for future distinction with "EXT" defines.
> Point to these defines in the main driver data structure.
>
> The early console only uses the standard port (not extended).
>
> Suggested-by: Wilson Ding <dingwei at marvell.com>
> Signed-off-by: Miquel Raynal <miquel.raynal at free-electrons.com>
Reviewed-by: Gregory CLEMENT <gregory.clement at free-electrons.com>
Thanks,
Gregory
> ---
> drivers/tty/serial/mvebu-uart.c | 213 ++++++++++++++++++++++++++--------------
> 1 file changed, 140 insertions(+), 73 deletions(-)
>
> diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c
> index 25b11ede3a97..82438884af1e 100644
> --- a/drivers/tty/serial/mvebu-uart.c
> +++ b/drivers/tty/serial/mvebu-uart.c
> @@ -38,46 +38,32 @@
> #include <linux/tty_flip.h>
>
> /* Register Map */
> -#define UART_RBR 0x00
> -#define RBR_BRK_DET BIT(15)
> -#define RBR_FRM_ERR_DET BIT(14)
> -#define RBR_PAR_ERR_DET BIT(13)
> -#define RBR_OVR_ERR_DET BIT(12)
> +#define UART_STD_RBR 0x00
>
> -#define UART_TSH 0x04
> +#define UART_STD_TSH 0x04
>
> -#define UART_CTRL 0x08
> +#define UART_STD_CTRL1 0x08
> #define CTRL_SOFT_RST BIT(31)
> #define CTRL_TXFIFO_RST BIT(15)
> #define CTRL_RXFIFO_RST BIT(14)
> -#define CTRL_ST_MIRR_EN BIT(13)
> -#define CTRL_LPBK_EN BIT(12)
> #define CTRL_SND_BRK_SEQ BIT(11)
> -#define CTRL_PAR_EN BIT(10)
> -#define CTRL_TWO_STOP BIT(9)
> -#define CTRL_TX_HFL_INT BIT(8)
> -#define CTRL_RX_HFL_INT BIT(7)
> -#define CTRL_TX_EMP_INT BIT(6)
> -#define CTRL_TX_RDY_INT BIT(5)
> -#define CTRL_RX_RDY_INT BIT(4)
> #define CTRL_BRK_DET_INT BIT(3)
> #define CTRL_FRM_ERR_INT BIT(2)
> #define CTRL_PAR_ERR_INT BIT(1)
> #define CTRL_OVR_ERR_INT BIT(0)
> -#define CTRL_RX_INT (CTRL_RX_RDY_INT | CTRL_BRK_DET_INT |\
> - CTRL_FRM_ERR_INT | CTRL_PAR_ERR_INT | CTRL_OVR_ERR_INT)
> +#define CTRL_BRK_INT (CTRL_BRK_DET_INT | CTRL_FRM_ERR_INT | \
> + CTRL_PAR_ERR_INT | CTRL_OVR_ERR_INT)
>
> -#define UART_STAT 0x0c
> +#define UART_STD_CTRL2 UART_STD_CTRL1
> +#define CTRL_STD_TX_RDY_INT BIT(5)
> +#define CTRL_STD_RX_RDY_INT BIT(4)
> +
> +#define UART_STAT 0x0C
> #define STAT_TX_FIFO_EMP BIT(13)
> -#define STAT_RX_FIFO_EMP BIT(12)
> #define STAT_TX_FIFO_FUL BIT(11)
> -#define STAT_TX_FIFO_HFL BIT(10)
> -#define STAT_RX_TOGL BIT(9)
> -#define STAT_RX_FIFO_FUL BIT(8)
> -#define STAT_RX_FIFO_HFL BIT(7)
> #define STAT_TX_EMP BIT(6)
> -#define STAT_TX_RDY BIT(5)
> -#define STAT_RX_RDY BIT(4)
> +#define STAT_STD_TX_RDY BIT(5)
> +#define STAT_STD_RX_RDY BIT(4)
> #define STAT_BRK_DET BIT(3)
> #define STAT_FRM_ERR BIT(2)
> #define STAT_PAR_ERR BIT(1)
> @@ -92,13 +78,55 @@
> #define MVEBU_UART_TYPE "mvebu-uart"
> #define DRIVER_NAME "mvebu_serial"
>
> -static struct uart_port mvebu_uart_ports[MVEBU_NR_UARTS];
> +/* Register offsets, different depending on the UART */
> +struct uart_regs_layout {
> + unsigned int rbr;
> + unsigned int tsh;
> + unsigned int ctrl;
> + unsigned int intr;
> +};
> +
> +/* Diverging flags */
> +struct uart_flags {
> + unsigned int ctrl_tx_rdy_int;
> + unsigned int ctrl_rx_rdy_int;
> + unsigned int stat_tx_rdy;
> + unsigned int stat_rx_rdy;
> +};
> +
> +/* Driver data, a structure for each UART port */
> +struct mvebu_uart_driver_data {
> + bool is_ext;
> + struct uart_regs_layout regs;
> + struct uart_flags flags;
> +};
>
> -struct mvebu_uart_data {
> +/* MVEBU UART driver structure */
> +struct mvebu_uart {
> struct uart_port *port;
> - struct clk *clk;
> + struct clk *clk;
> + struct mvebu_uart_driver_data *data;
> };
>
> +static struct mvebu_uart *to_mvuart(struct uart_port *port)
> +{
> + return (struct mvebu_uart *)port->private_data;
> +}
> +
> +#define IS_EXTENDED(port) (to_mvuart(port)->data->is_ext)
> +
> +#define UART_RBR(port) (to_mvuart(port)->data->regs.rbr)
> +#define UART_TSH(port) (to_mvuart(port)->data->regs.tsh)
> +#define UART_CTRL(port) (to_mvuart(port)->data->regs.ctrl)
> +#define UART_INTR(port) (to_mvuart(port)->data->regs.intr)
> +
> +#define CTRL_TX_RDY_INT(port) (to_mvuart(port)->data->flags.ctrl_tx_rdy_int)
> +#define CTRL_RX_RDY_INT(port) (to_mvuart(port)->data->flags.ctrl_rx_rdy_int)
> +#define STAT_TX_RDY(port) (to_mvuart(port)->data->flags.stat_tx_rdy)
> +#define STAT_RX_RDY(port) (to_mvuart(port)->data->flags.stat_rx_rdy)
> +
> +static struct uart_port mvebu_uart_ports[MVEBU_NR_UARTS];
> +
> /* Core UART Driver Operations */
> static unsigned int mvebu_uart_tx_empty(struct uart_port *port)
> {
> @@ -128,26 +156,31 @@ static void mvebu_uart_set_mctrl(struct uart_port *port,
>
> static void mvebu_uart_stop_tx(struct uart_port *port)
> {
> - unsigned int ctl = readl(port->membase + UART_CTRL);
> + unsigned int ctl = readl(port->membase + UART_INTR(port));
>
> - ctl &= ~CTRL_TX_RDY_INT;
> - writel(ctl, port->membase + UART_CTRL);
> + ctl &= ~CTRL_TX_RDY_INT(port);
> + writel(ctl, port->membase + UART_INTR(port));
> }
>
> static void mvebu_uart_start_tx(struct uart_port *port)
> {
> - unsigned int ctl = readl(port->membase + UART_CTRL);
> + unsigned int ctl = readl(port->membase + UART_INTR(port));
>
> - ctl |= CTRL_TX_RDY_INT;
> - writel(ctl, port->membase + UART_CTRL);
> + ctl |= CTRL_TX_RDY_INT(port);
> + writel(ctl, port->membase + UART_INTR(port));
> }
>
> static void mvebu_uart_stop_rx(struct uart_port *port)
> {
> - unsigned int ctl = readl(port->membase + UART_CTRL);
> + unsigned int ctl;
>
> - ctl &= ~CTRL_RX_INT;
> - writel(ctl, port->membase + UART_CTRL);
> + ctl = readl(port->membase + UART_CTRL(port));
> + ctl &= ~CTRL_BRK_INT;
> + writel(ctl, port->membase + UART_CTRL(port));
> +
> + ctl = readl(port->membase + UART_INTR(port));
> + ctl &= ~CTRL_RX_RDY_INT(port);
> + writel(ctl, port->membase + UART_INTR(port));
> }
>
> static void mvebu_uart_break_ctl(struct uart_port *port, int brk)
> @@ -156,12 +189,12 @@ static void mvebu_uart_break_ctl(struct uart_port *port, int brk)
> unsigned long flags;
>
> spin_lock_irqsave(&port->lock, flags);
> - ctl = readl(port->membase + UART_CTRL);
> + ctl = readl(port->membase + UART_CTRL(port));
> if (brk == -1)
> ctl |= CTRL_SND_BRK_SEQ;
> else
> ctl &= ~CTRL_SND_BRK_SEQ;
> - writel(ctl, port->membase + UART_CTRL);
> + writel(ctl, port->membase + UART_CTRL(port));
> spin_unlock_irqrestore(&port->lock, flags);
> }
>
> @@ -172,8 +205,8 @@ static void mvebu_uart_rx_chars(struct uart_port *port, unsigned int status)
> char flag = 0;
>
> do {
> - if (status & STAT_RX_RDY) {
> - ch = readl(port->membase + UART_RBR);
> + if (status & STAT_RX_RDY(port)) {
> + ch = readl(port->membase + UART_RBR(port));
> ch &= 0xff;
> flag = TTY_NORMAL;
> port->icount.rx++;
> @@ -199,7 +232,7 @@ static void mvebu_uart_rx_chars(struct uart_port *port, unsigned int status)
> goto ignore_char;
>
> if (status & port->ignore_status_mask & STAT_PAR_ERR)
> - status &= ~STAT_RX_RDY;
> + status &= ~STAT_RX_RDY(port);
>
> status &= port->read_status_mask;
>
> @@ -208,7 +241,7 @@ static void mvebu_uart_rx_chars(struct uart_port *port, unsigned int status)
>
> status &= ~port->ignore_status_mask;
>
> - if (status & STAT_RX_RDY)
> + if (status & STAT_RX_RDY(port))
> tty_insert_flip_char(tport, ch, flag);
>
> if (status & STAT_BRK_DET)
> @@ -222,7 +255,7 @@ static void mvebu_uart_rx_chars(struct uart_port *port, unsigned int status)
>
> ignore_char:
> status = readl(port->membase + UART_STAT);
> - } while (status & (STAT_RX_RDY | STAT_BRK_DET));
> + } while (status & (STAT_RX_RDY(port) | STAT_BRK_DET));
>
> tty_flip_buffer_push(tport);
> }
> @@ -234,7 +267,7 @@ static void mvebu_uart_tx_chars(struct uart_port *port, unsigned int status)
> unsigned int st;
>
> if (port->x_char) {
> - writel(port->x_char, port->membase + UART_TSH);
> + writel(port->x_char, port->membase + UART_TSH(port));
> port->icount.tx++;
> port->x_char = 0;
> return;
> @@ -246,7 +279,7 @@ static void mvebu_uart_tx_chars(struct uart_port *port, unsigned int status)
> }
>
> for (count = 0; count < port->fifosize; count++) {
> - writel(xmit->buf[xmit->tail], port->membase + UART_TSH);
> + writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port));
> xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> port->icount.tx++;
>
> @@ -270,10 +303,11 @@ static irqreturn_t mvebu_uart_isr(int irq, void *dev_id)
> struct uart_port *port = (struct uart_port *)dev_id;
> unsigned int st = readl(port->membase + UART_STAT);
>
> - if (st & (STAT_RX_RDY | STAT_OVR_ERR | STAT_FRM_ERR | STAT_BRK_DET))
> + if (st & (STAT_RX_RDY(port) | STAT_OVR_ERR | STAT_FRM_ERR |
> + STAT_BRK_DET))
> mvebu_uart_rx_chars(port, st);
>
> - if (st & STAT_TX_RDY)
> + if (st & STAT_TX_RDY(port))
> mvebu_uart_tx_chars(port, st);
>
> return IRQ_HANDLED;
> @@ -281,12 +315,17 @@ static irqreturn_t mvebu_uart_isr(int irq, void *dev_id)
>
> static int mvebu_uart_startup(struct uart_port *port)
> {
> + unsigned int ctl;
> int ret;
>
> writel(CTRL_TXFIFO_RST | CTRL_RXFIFO_RST,
> - port->membase + UART_CTRL);
> + port->membase + UART_CTRL(port));
> udelay(1);
> - writel(CTRL_RX_INT, port->membase + UART_CTRL);
> + writel(CTRL_BRK_INT, port->membase + UART_CTRL(port));
> +
> + ctl = readl(port->membase + UART_INTR(port));
> + ctl |= CTRL_RX_RDY_INT(port);
> + writel(ctl, port->membase + UART_INTR(port));
>
> ret = request_irq(port->irq, mvebu_uart_isr, port->irqflags,
> DRIVER_NAME, port);
> @@ -300,7 +339,7 @@ static int mvebu_uart_startup(struct uart_port *port)
>
> static void mvebu_uart_shutdown(struct uart_port *port)
> {
> - writel(0, port->membase + UART_CTRL);
> + writel(0, port->membase + UART_INTR(port));
>
> free_irq(port->irq, port);
> }
> @@ -314,8 +353,8 @@ static void mvebu_uart_set_termios(struct uart_port *port,
>
> spin_lock_irqsave(&port->lock, flags);
>
> - port->read_status_mask = STAT_RX_RDY | STAT_OVR_ERR |
> - STAT_TX_RDY | STAT_TX_FIFO_FUL;
> + port->read_status_mask = STAT_RX_RDY(port) | STAT_OVR_ERR |
> + STAT_TX_RDY(port) | STAT_TX_FIFO_FUL;
>
> if (termios->c_iflag & INPCK)
> port->read_status_mask |= STAT_FRM_ERR | STAT_PAR_ERR;
> @@ -326,7 +365,7 @@ static void mvebu_uart_set_termios(struct uart_port *port,
> STAT_FRM_ERR | STAT_PAR_ERR | STAT_OVR_ERR;
>
> if ((termios->c_cflag & CREAD) == 0)
> - port->ignore_status_mask |= STAT_RX_RDY | STAT_BRK_ERR;
> + port->ignore_status_mask |= STAT_RX_RDY(port) | STAT_BRK_ERR;
>
> if (old)
> tty_termios_copy_hw(termios, old);
> @@ -357,10 +396,10 @@ static int mvebu_uart_get_poll_char(struct uart_port *port)
> {
> unsigned int st = readl(port->membase + UART_STAT);
>
> - if (!(st & STAT_RX_RDY))
> + if (!(st & STAT_RX_RDY(port)))
> return NO_POLL_CHAR;
>
> - return readl(port->membase + UART_RBR);
> + return readl(port->membase + UART_RBR(port));
> }
>
> static void mvebu_uart_put_poll_char(struct uart_port *port, unsigned char c)
> @@ -376,7 +415,7 @@ static void mvebu_uart_put_poll_char(struct uart_port *port, unsigned char c)
> udelay(1);
> }
>
> - writel(c, port->membase + UART_TSH);
> + writel(c, port->membase + UART_TSH(port));
> }
> #endif
>
> @@ -414,7 +453,8 @@ static void mvebu_uart_putc(struct uart_port *port, int c)
> break;
> }
>
> - writel(c, port->membase + UART_TSH);
> + /* At early stage, DT is not parsed yet, only use UART0 */
> + writel(c, port->membase + UART_STD_TSH);
>
> for (;;) {
> st = readl(port->membase + UART_STAT);
> @@ -459,7 +499,7 @@ static void wait_for_xmitr(struct uart_port *port)
> static void mvebu_uart_console_putchar(struct uart_port *port, int ch)
> {
> wait_for_xmitr(port);
> - writel(ch, port->membase + UART_TSH);
> + writel(ch, port->membase + UART_TSH(port));
> }
>
> static void mvebu_uart_console_write(struct console *co, const char *s,
> @@ -467,7 +507,7 @@ static void mvebu_uart_console_write(struct console *co, const char *s,
> {
> struct uart_port *port = &mvebu_uart_ports[co->index];
> unsigned long flags;
> - unsigned int ier;
> + unsigned int ier, intr, ctl;
> int locked = 1;
>
> if (oops_in_progress)
> @@ -475,16 +515,23 @@ static void mvebu_uart_console_write(struct console *co, const char *s,
> else
> spin_lock_irqsave(&port->lock, flags);
>
> - ier = readl(port->membase + UART_CTRL) &
> - (CTRL_RX_INT | CTRL_TX_RDY_INT);
> - writel(0, port->membase + UART_CTRL);
> + ier = readl(port->membase + UART_CTRL(port)) & CTRL_BRK_INT;
> + intr = readl(port->membase + UART_INTR(port)) &
> + (CTRL_RX_RDY_INT(port) | CTRL_TX_RDY_INT(port));
> + writel(0, port->membase + UART_CTRL(port));
> + writel(0, port->membase + UART_INTR(port));
>
> uart_console_write(port, s, count, mvebu_uart_console_putchar);
>
> wait_for_xmitr(port);
>
> if (ier)
> - writel(ier, port->membase + UART_CTRL);
> + writel(ier, port->membase + UART_CTRL(port));
> +
> + if (intr) {
> + ctl = intr | readl(port->membase + UART_INTR(port));
> + writel(ctl, port->membase + UART_INTR(port));
> + }
>
> if (locked)
> spin_unlock_irqrestore(&port->lock, flags);
> @@ -547,12 +594,16 @@ static struct uart_driver mvebu_uart_driver = {
> #endif
> };
>
> +static const struct of_device_id mvebu_uart_of_match[];
> +
> static int mvebu_uart_probe(struct platform_device *pdev)
> {
> struct resource *reg = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + const struct of_device_id *match = of_match_device(mvebu_uart_of_match,
> + &pdev->dev);
> struct uart_port *port;
> - struct mvebu_uart_data *data;
> + struct mvebu_uart *mvuart;
> int ret;
>
> if (!reg || !irq) {
> @@ -591,15 +642,16 @@ static int mvebu_uart_probe(struct platform_device *pdev)
> if (IS_ERR(port->membase))
> return -PTR_ERR(port->membase);
>
> - data = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_uart_data),
> - GFP_KERNEL);
> - if (!data)
> + mvuart = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_uart),
> + GFP_KERNEL);
> + if (!mvuart)
> return -ENOMEM;
>
> - data->port = port;
> + mvuart->data = (struct mvebu_uart_driver_data *)match->data;
> + mvuart->port = port;
>
> - port->private_data = data;
> - platform_set_drvdata(pdev, data);
> + port->private_data = mvuart;
> + platform_set_drvdata(pdev, mvuart);
>
> ret = uart_add_one_port(&mvebu_uart_driver, port);
> if (ret)
> @@ -607,9 +659,24 @@ static int mvebu_uart_probe(struct platform_device *pdev)
> return 0;
> }
>
> +static struct mvebu_uart_driver_data uart_std_driver_data = {
> + .is_ext = false,
> + .regs.rbr = UART_STD_RBR,
> + .regs.tsh = UART_STD_TSH,
> + .regs.ctrl = UART_STD_CTRL1,
> + .regs.intr = UART_STD_CTRL2,
> + .flags.ctrl_tx_rdy_int = CTRL_STD_TX_RDY_INT,
> + .flags.ctrl_rx_rdy_int = CTRL_STD_RX_RDY_INT,
> + .flags.stat_tx_rdy = STAT_STD_TX_RDY,
> + .flags.stat_rx_rdy = STAT_STD_RX_RDY,
> +};
> +
> /* Match table for of_platform binding */
> static const struct of_device_id mvebu_uart_of_match[] = {
> - { .compatible = "marvell,armada-3700-uart", },
> + {
> + .compatible = "marvell,armada-3700-uart",
> + .data = (void *)&uart_std_driver_data,
> + },
> {}
> };
>
> --
> 2.11.0
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
More information about the linux-arm-kernel
mailing list