[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [RFC PATCH 2/2] xen/arm: Add MESON UART driver for Amlogic S905 SoC



On 8/7/18 6:07 PM, Amit Singh Tomar wrote:

Hi,

> This patch adds driver for UART controller present on Amlogic S905
> SoC.
> https://dn.odroid.com/S905/DataSheet/S905_Public_Datasheet_V1.1.4.pdf
> 
> Signed-off-by: Amit Singh Tomar <amittomer25@xxxxxxxxx>
> ---
>  xen/drivers/char/Kconfig      |   8 ++
>  xen/drivers/char/Makefile     |   1 +
>  xen/drivers/char/meson-uart.c | 290
> ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 299
> insertions(+) create mode 100644 xen/drivers/char/meson-uart.c
> 
> diff --git a/xen/drivers/char/Kconfig b/xen/drivers/char/Kconfig
> index cc78ec3..b9fee4e 100644
> --- a/xen/drivers/char/Kconfig
> +++ b/xen/drivers/char/Kconfig
> @@ -20,6 +20,14 @@ config HAS_MVEBU
>         This selects the Marvell MVEBU UART. If you have a ARMADA
> 3700 based board, say Y.
>  
> +config HAS_MESON
> +    bool
> +    default y
> +    depends on ARM_64
> +    help
> +      This selects the Marvell MESON UART. If you have a Amlogic S905
> +      based board, say Y.

It seems this UART driver supports all Amlogic SoCs (905X, 805X, 912,
...), not just the S905. So please either remove the number or make it
clear that it's just an example.
And it's not a Marvell UART ;-)

> +
>  config HAS_PL011
>       bool
>       default y
> diff --git a/xen/drivers/char/Makefile b/xen/drivers/char/Makefile
> index b68c330..7c646d7 100644
> --- a/xen/drivers/char/Makefile
> +++ b/xen/drivers/char/Makefile
> @@ -3,6 +3,7 @@ obj-$(CONFIG_HAS_NS16550) += ns16550.o
>  obj-$(CONFIG_HAS_CADENCE_UART) += cadence-uart.o
>  obj-$(CONFIG_HAS_PL011) += pl011.o
>  obj-$(CONFIG_HAS_EXYNOS4210) += exynos4210-uart.o
> +obj-$(CONFIG_HAS_MESON) += meson-uart.o
>  obj-$(CONFIG_HAS_MVEBU) += mvebu-uart.o
>  obj-$(CONFIG_HAS_OMAP) += omap-uart.o
>  obj-$(CONFIG_HAS_SCIF) += scif-uart.o
> diff --git a/xen/drivers/char/meson-uart.c
> b/xen/drivers/char/meson-uart.c new file mode 100644
> index 0000000..8fe7e62
> --- /dev/null
> +++ b/xen/drivers/char/meson-uart.c
> @@ -0,0 +1,290 @@
> +/*
> + * xen/drivers/char/meson-uart.c
> + *
> + * Driver for Amlogic MESON UART
> + *
> + * Copyright (c) 2018, Amit Singh Tomar <amittomer25@xxxxxxxxx>.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms and conditions of the GNU General Public
> + * License, version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public
> + * License along with this program; If not, see
> <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <xen/irq.h>
> +#include <xen/serial.h>
> +#include <xen/vmap.h>
> +#include <asm/io.h>
> +
> +/* Register offsets */
> +#define UART_WFIFO                  0x00
> +#define UART_RFIFO                  0x04
> +#define UART_CONTROL                0x08
> +#define UART_STATUS                 0x0c
> +#define UART_MISC                   0x10
> +#define UART_REG5                   0x14
> +
> +/* UART_CONTROL bits */
> +#define UART_TX_EN                  BIT(12)
> +#define UART_RX_EN                  BIT(13)

You don't seem to use them in the code? This seems somewhat wrong, you
shouldn't rely on those bits being set by previous boot stages.

> +#define UART_TX_RST                 BIT(22)
> +#define UART_RX_RST                 BIT(23)
> +#define UART_CLEAR_ERR              BIT(24)
> +#define UART_RX_INT_EN              BIT(27)
> +#define UART_TX_INT_EN              BIT(28)
> +
> +/* UART_STATUS bits */
> +#define UART_PARITY_ERR             BIT(16)
> +#define UART_FRAME_ERR              BIT(17)
> +#define UART_TX_FIFO_WERR           BIT(18)

You don't use those, so I don't see a need to define them.

> +#define UART_RX_EMPTY               BIT(20)
> +#define UART_TX_FULL                BIT(21)
> +#define UART_TX_EMPTY               BIT(22)

Might be worth to add FIFO_ in those names.

> +#define UART_TX_CNT_MASK            GENMASK(14, 8)
> +
> +
> +#define UART_XMIT_IRQ_CNT_MASK      GENMASK(15, 8)
> +#define UART_RECV_IRQ_CNT_MASK      GENMASK(7, 0)
> +
> +#define TX_FIFO_SIZE                64
> +
> +static struct meson_s905_uart {
> +    unsigned int irq;
> +    void __iomem *regs;
> +    struct irqaction irqaction;
> +    struct vuart_info vuart;
> +} meson_s905_com = {0};
> +
> +#define meson_s905_read(uart, off)  readl((uart)->regs + off)
> +#define meson_s905_write(uart, off, val) writel(val, (uart->regs) + off)

I was wondering whether a clrsetbit helper would be more useful than
these very thin wrappers.

> +static void meson_s905_uart_interrupt(int irq, void *data,
> +        struct cpu_user_regs *regs)
> +{
> +    struct serial_port *port = data;
> +    struct meson_s905_uart *uart = port->uart;
> +    uint32_t st = meson_s905_read(uart, UART_STATUS);
> +
> +    if ( !(st & UART_RX_EMPTY) )
> +    {
> +        serial_rx_interrupt(port, regs);
> +    }
> +
> +    if ( !(st & UART_TX_FULL) )
> +    {
> +        if ( st & UART_TX_INT_EN )

No. This bit is in the control register, not the status register.
And do you actually need to read this from the hardware?

> +            serial_tx_interrupt(port, regs);
> +    }
> +
> +}
> +
> +static void __init meson_s905_uart_init_preirq(struct serial_port
> *port) +{
> +    struct meson_s905_uart *uart = port->uart;
> +    uint32_t reg;
> +
> +    reg = meson_s905_read(uart, UART_CONTROL);
> +    reg &= ~(UART_RX_RST | UART_TX_RST | UART_CLEAR_ERR);
> +    meson_s905_write(uart, UART_CONTROL, reg);
> +
> +    /* Disbale Rx/Tx interrupts */
> +    reg = meson_s905_read(uart, UART_CONTROL);
> +    reg &= ~(UART_RX_INT_EN | UART_TX_INT_EN);
> +    meson_s905_write(uart, UART_CONTROL, reg);

Why are those two separate sequences, the second just for disabling the
interrupts? They should be off from the beginning.
Besides: You should properly reset the UART, by first setting the RST
bits, then clearing them again (Julien: they are not self-resetting).
So I guess it's one MMIO read, and two writes.

> +}
> +
> +static void __init meson_s905_uart_init_postirq(struct serial_port
> *port) +{
> +    struct meson_s905_uart *uart = port->uart;
> +    uint32_t reg;
> +
> +    uart->irqaction.handler = meson_s905_uart_interrupt;
> +    uart->irqaction.name    = "meson_s905_uart";
> +    uart->irqaction.dev_id  = port;
> +
> +    if ( setup_irq(uart->irq, 0, &uart->irqaction) != 0 )
> +    {
> +        printk("Failed to allocated meson_s905_uart IRQ %d\n",
> uart->irq);
> +        return;
> +    }
> +
> +    /* Configure Rx/Tx interrupts based on bytes in FIFO */
> +    reg = meson_s905_read(uart, UART_MISC);
> +    reg = (UART_RECV_IRQ_CNT_MASK & 1) |
> +           (UART_XMIT_IRQ_CNT_MASK & ((TX_FIFO_SIZE / 2) << 8));

I think Julien mentioned that before: this looks wrong.
Even with using |= this is still failing, as you want to clear the bits
before ORing in some new values.

> +    meson_s905_write(uart, UART_MISC, reg);
> +
> +    /* Make sure Rx/Tx interrupts are enabled now */
> +    reg = meson_s905_read(uart, UART_CONTROL);
> +    reg |= (UART_RX_INT_EN | UART_TX_INT_EN);
> +    meson_s905_write(uart, UART_CONTROL, reg);
> +}
> +
> +static void meson_s905_uart_suspend(struct serial_port *port)
> +{
> +    BUG();
> +}
> +
> +static void meson_s905_uart_resume(struct serial_port *port)
> +{
> +    BUG();
> +}
> +
> +static void meson_s905_uart_putc(struct serial_port *port, char c)
> +{
> +    struct meson_s905_uart *uart = port->uart;
> +
> +    meson_s905_write(uart, UART_WFIFO, c);
> +}
> +
> +static int meson_s905_uart_getc(struct serial_port *port, char *c)
> +{
> +    struct meson_s905_uart *uart = port->uart;
> +
> +    if ( (meson_s905_read(uart, UART_STATUS) & UART_RX_EMPTY) )
> +        return 0;
> +
> +    *c =  meson_s905_read(uart, UART_RFIFO) & 0xff;
> +
> +    return 1;
> +}
> +
> +static int __init meson_s905_irq(struct serial_port *port)
> +{
> +    struct meson_s905_uart *uart = port->uart;
> +
> +    return uart->irq;
> +}
> +
> +static const struct vuart_info *meson_s905_vuart_info(struct
> serial_port *port) +{
> +    struct meson_s905_uart *uart = port->uart;
> +
> +    return &uart->vuart;
> +}
> +
> +static void meson_s905_uart_stop_tx(struct serial_port *port)
> +{
> +    struct meson_s905_uart *uart = port->uart;
> +    uint32_t reg;
> +
> +    reg = meson_s905_read(uart, UART_CONTROL);
> +    reg &= ~UART_TX_INT_EN;
> +    meson_s905_write(uart, UART_CONTROL, reg);
> +}
> +
> +static void meson_s905_uart_start_tx(struct serial_port *port)
> +{
> +    struct meson_s905_uart *uart = port->uart;
> +    uint32_t reg;
> +
> +    reg = meson_s905_read(uart, UART_CONTROL);
> +    reg |= UART_TX_INT_EN;
> +    meson_s905_write(uart, UART_CONTROL, reg);
> +}
> +
> +static int meson_s905_uart_tx_ready(struct serial_port *port)
> +{
> +    struct meson_s905_uart *uart = port->uart;
> +    uint32_t reg;
> +
> +    reg = meson_s905_read(uart, UART_STATUS);
> +
> +    if ( reg & UART_TX_EMPTY )
> +        return TX_FIFO_SIZE;
> +    if ( reg & UART_TX_FULL )
> +        return 0;
> +
> +    return (reg & UART_TX_CNT_MASK) >> 8;
> +}
> +
> +static struct uart_driver __read_mostly meson_uart_driver = {
> +    .init_preirq  = meson_s905_uart_init_preirq,
> +    .init_postirq = meson_s905_uart_init_postirq,
> +    .endboot      = NULL,
> +    .suspend      = meson_s905_uart_suspend,
> +    .resume       = meson_s905_uart_resume,
> +    .putc         = meson_s905_uart_putc,
> +    .getc         = meson_s905_uart_getc,
> +    .tx_ready    = meson_s905_uart_tx_ready,

w/s

> +    .stop_tx      = meson_s905_uart_stop_tx,
> +    .start_tx     = meson_s905_uart_start_tx,
> +    .irq          = meson_s905_irq,
> +    .vuart_info   = meson_s905_vuart_info,
> +};
> +
> +static int __init meson_uart_init(struct dt_device_node *dev, const
> void *data) +{
> +    const char *config = data;
> +    struct meson_s905_uart *uart;
> +    int res;
> +    u64 addr, size;
> +
> +    if ( strcmp(config, "") )
> +        printk("WARNING: UART configuration is not supported\n");
> +
> +    uart = &meson_s905_com;
> +
> +    res = dt_device_get_address(dev, 0, &addr, &size);
> +    if ( res )
> +    {
> +        printk("meson_s905: Unable to retrieve the base address of
> the UART\n");
> +        return res;
> +    }
> +
> +    res = platform_get_irq(dev, 0);
> +    if ( res < 0 )
> +    {
> +        printk("meson_s905: Unable to retrieve the IRQ\n");
> +        return -EINVAL;
> +    }
> +
> +    uart->irq  = res;
> +
> +    uart->regs = ioremap_nocache(addr, size);
> +    if ( !uart->regs )
> +    {
> +        printk("meson_s905: Unable to map the UART memory\n");

UART memory sounds weird. Either just UART or UART MMIO frame.

> +        return -ENOMEM;
> +    }
> +
> +    uart->vuart.base_addr = addr;
> +    uart->vuart.size = size;
> +    uart->vuart.data_off = UART_CONTROL;

This should be WFIFO.

> +    uart->vuart.status_off = UART_STATUS;
> +    uart->vuart.status = UART_RX_EMPTY | UART_TX_EMPTY;
> +
> +    /* Register with generic serial driver. */
> +    serial_register_uart(SERHND_DTUART, &meson_uart_driver, uart);
> +
> +    dt_device_set_used_by(dev, DOMID_XEN);
> +
> +    return 0;
> +}
> +
> +static const struct dt_device_match meson_dt_match[] __initconst =
> +{
> +    DT_MATCH_COMPATIBLE("amlogic,meson-uart"),

You should list all the UART compatible names the Linux driver lists.
The difference between the legacy binding and the "stable" ones seems to
be only the clock references, which we don't care about.
Their usage of compatible is a bit weird, but at least you need
"amlogic,meson-gx-uart" to cover the 64-bit parts.

Cheers,
Andre.

> +    { /* sentinel */ },
> +};
> +
> +DT_DEVICE_START(meson, "Amlogic-S905 UART", DEVICE_SERIAL)
> +    .dt_match = meson_dt_match,
> +    .init = meson_uart_init,
> +DT_DEVICE_END
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> +*/
> 


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.