|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v6 4/6] xen/arm: Add the new OMAP UART driver.
On 13 August 2013 03:35, Chen Baozi <baozich@xxxxxxxxx> wrote:
>
> On Aug 13, 2013, at 6:21 AM, Julien Grall <julien.grall@xxxxxxxxxx> wrote:
>
>> On 9 August 2013 03:20, Chen Baozi <baozich@xxxxxxxxx> wrote:
>>> TI OMAP UART introduces some features such as register access modes, which
>>> makes its configuration and interrupt handling differs from 8250 compatible
>>> UART. Thus, we seperate this driver from ns16550's implementation.
>>>
>>> Signed-off-by: Chen Baozi <baozich@xxxxxxxxx>
>>> ---
>>> config/arm32.mk | 1 +
>>> xen/drivers/char/Makefile | 1 +
>>> xen/drivers/char/omap-uart.c | 354
>>> +++++++++++++++++++++++++++++++++++++++++++
>>> xen/include/xen/8250-uart.h | 51 +++++++
>>> 4 files changed, 407 insertions(+)
>>> create mode 100644 xen/drivers/char/omap-uart.c
>>>
>>> diff --git a/config/arm32.mk b/config/arm32.mk
>>> index 8b9899e..ef31cff 100644
>>> --- a/config/arm32.mk
>>> +++ b/config/arm32.mk
>>> @@ -11,6 +11,7 @@ CFLAGS += -marm
>>>
>>> HAS_PL011 := y
>>> HAS_EXYNOS4210 := y
>>> +HAS_OMAP := y
>>>
>>> # Use only if calling $(LD) directly.
>>> LDFLAGS_DIRECT += -EL
>>> diff --git a/xen/drivers/char/Makefile b/xen/drivers/char/Makefile
>>> index 37543f0..911b788 100644
>>> --- a/xen/drivers/char/Makefile
>>> +++ b/xen/drivers/char/Makefile
>>> @@ -2,6 +2,7 @@ obj-y += console.o
>>> obj-$(HAS_NS16550) += ns16550.o
>>> obj-$(HAS_PL011) += pl011.o
>>> obj-$(HAS_EXYNOS4210) += exynos4210-uart.o
>>> +obj-$(HAS_OMAP) += omap-uart.o
>>> obj-$(HAS_EHCI) += ehci-dbgp.o
>>> obj-$(CONFIG_ARM) += dt-uart.o
>>> obj-y += serial.o
>>> diff --git a/xen/drivers/char/omap-uart.c b/xen/drivers/char/omap-uart.c
>>> new file mode 100644
>>> index 0000000..11cb74b
>>> --- /dev/null
>>> +++ b/xen/drivers/char/omap-uart.c
>>> @@ -0,0 +1,354 @@
>>> +/*
>>> + * omap-uart.c
>>> + * Based on drivers/char/ns16550.c
>>> + *
>>> + * Driver for OMAP-UART controller
>>> + *
>>> + * Copyright (C) 2013, Chen Baozi <baozich@xxxxxxxxx>
>>> + *
>>> + * Note: This driver is made separate from 16550-series UART driver as
>>> + * omap platform has some specific configurations
>>> + */
>>> +
>>> +#include <xen/config.h>
>>> +#include <xen/console.h>
>>> +#include <xen/serial.h>
>>> +#include <xen/init.h>
>>> +#include <xen/irq.h>
>>> +#include <asm/early_printk.h>
>>> +#include <xen/device_tree.h>
>>> +#include <asm/device.h>
>>> +#include <xen/errno.h>
>>> +#include <xen/mm.h>
>>> +#include <xen/vmap.h>
>>> +#include <xen/8250-uart.h>
>>> +
>>> +#define REG_SHIFT 2
>>> +
>>> +#define omap_read(uart, off) ioreadl((uart)->regs + (off<<REG_SHIFT))
>>> +#define omap_write(uart, off, val) iowritel((uart)->regs +
>>> (off<<REG_SHIFT), (val))
>>> +
>>> +static struct omap_uart {
>>> + u32 baud, clock_hz, data_bits, parity, stop_bits, fifo_size;
>>> + struct dt_irq irq;
>>> + void __iomem *regs;
>>
>> I have noticed that I also use void * on the other drivers, but it's a
>> gcc extension
>> (http://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Pointer-Arith.html).
>> It's better to use unsigned char *.
>>
>>> + struct irqaction irqaction;
>>> +} omap_com = {0};
>>> +
>>> +static void omap_uart_interrupt(int irq, void *data, struct cpu_user_regs
>>> *regs)
>>> +{
>>> + struct serial_port *port = data;
>>> + struct omap_uart *uart = port->uart;
>>> + u32 lsr;
>>> +
>>> + while ( !(omap_read(uart, UART_IIR) & UART_IIR_NOINT) )
>>> + {
>>> + lsr = omap_read(uart, UART_LSR) & 0xff;
>>> + if ( lsr & UART_LSR_THRE )
>>> + serial_tx_interrupt(port, regs);
>>> + if ( lsr & UART_LSR_DR )
>>> + serial_rx_interrupt(port, regs);
>>> +
>>> + if ( port->txbufc == port->txbufp )
>>> + omap_write(uart, UART_IER, UART_IER_ERDAI|UART_IER_ELSI);
>>> + };
>>> +}
>>> +
>>> +static void baud_protocol_setup(struct omap_uart *uart)
>>> +{
>>> + u32 dll, dlh, efr;
>>> + unsigned int divisor;
>>> +
>>> + divisor = uart->clock_hz / (uart->baud << 4);
>>> + dll = divisor & 0xff;
>>> + dlh = divisor >> 8;
>>> +
>>> + /*
>>> + * Switch to register configuration mode B to access the UART_OMAP_EFR
>>> + * register.
>>> + */
>>> + omap_write(uart, UART_LCR, UART_LCR_CONF_MODE_B);
>>> + /*
>>> + * Enable access to the UART_IER[7:4] bit field.
>>> + */
>>> + efr = omap_read(uart, UART_OMAP_EFR);
>>> + omap_write(uart, UART_OMAP_EFR, efr|UART_OMAP_EFR_ECB);
>>> + /*
>>> + * Switch to register operation mode to access the UART_IER register.
>>> + */
>>> + omap_write(uart, UART_LCR, 0);
>>> + /*
>>> + * Clear the UART_IER register (set the UART_IER[4] SLEEP_MODE bit
>>> + * to 0 to change the UART_DLL and UART_DLM register). Set the
->>> + * UART_IER register value to 0x0000.
>>> + */
>>> + omap_write(uart, UART_IER, 0);
>>> + /*
>>> + * Switch to register configuartion mode B to access the UART_DLL and
>>> + * UART_DLM registers.
>>> + */
>>> + omap_write(uart, UART_LCR, UART_LCR_CONF_MODE_B);
>>> + /*
>>> + * Load divisor value.
>>> + */
>>> + omap_write(uart, UART_DLL, dll);
>>> + omap_write(uart, UART_DLM, dlh);
>>> + /*
>>> + * Restore the UART_OMAP_EFR
>>> + */
>>> + omap_write(uart, UART_OMAP_EFR, efr);
>>> + /*
>>> + * Load the new protocol formatting (parity, stop-bit, character
>>> length)
>>> + * and switch to register operational mode.
>>> + */
>>> + omap_write(uart, UART_LCR, (uart->data_bits - 5) |
>>> + ((uart->stop_bits - 1) << 2) | uart->parity);
>>> +}
>>> +
>>> +static void fifo_setup(struct omap_uart *uart)
>>> +{
>>> + u32 lcr, efr, mcr;
>>> + /*
>>> + * Switch to register configuration mode B to access the UART_OMAP_EFR
>>> + * register.
>>> + */
>>> + lcr = omap_read(uart, UART_LCR);
>>> + omap_write(uart, UART_LCR, UART_LCR_CONF_MODE_B);
>>> + /*
>>> + * Enable register submode TCR_TLR to access the UART_OMAP_TLR
>>> register.
>>> + */
>>> + efr = omap_read(uart, UART_OMAP_EFR);
>>> + omap_write(uart, UART_OMAP_EFR, efr|UART_OMAP_EFR_ECB);
>>> + /*
>>> + * Switch to register configuration mode A to access the UART_MCR
>>> + * register.
>>> + */
>>> + omap_write(uart, UART_LCR, UART_LCR_CONF_MODE_A);
>>> + /*
>>> + * Enable register submode TCR_TLR to access the UART_OMAP_TLR register
>>> + */
>>> + mcr = omap_read(uart, UART_MCR);
>>> + omap_write(uart, UART_MCR, mcr|UART_MCR_TCRTLR);
>>> + /*
>>> + * Enable the FIFO; load the new FIFO trigger and the new DMA mode.
>>> + */
>>> + omap_write(uart, UART_FCR, UART_FCR_R_TRIG_01|
>>> + UART_FCR_T_TRIG_10|UART_FCR_ENABLE);
>>> + /*
>>> + * Switch to register configuration mode B to access the UART_EFR
>>> + * register.
>>> + */
>>> + omap_write(uart, UART_LCR, UART_LCR_CONF_MODE_B);
>>> + /*
>>> + * Load the new FIFO triggers and the new DMA mode bit.
>>> + */
>>> + omap_write(uart, UART_OMAP_SCR, OMAP_UART_SCR_RX_TRIG_GRANU1_MASK);
>>> + /*
>>> + * Restore the UART_OMAP_EFR[4] value.
>>> + */
>>> + omap_write(uart, UART_OMAP_EFR, efr);
>>> + /*
>>> + * Switch to register configuration mode A to access the UART_MCR
>>> + * register.
>>> + */
>>> + omap_write(uart, UART_LCR, UART_LCR_CONF_MODE_A);
>>> + /*
>>> + * Restore UART_MCR[6] value.
>>> + */
>>> + omap_write(uart, UART_MCR, mcr);
>>> + /*
>>> + * Restore UART_LCR value.
>>> + */
>>> + omap_write(uart, UART_LCR, lcr);
>>> +
>>> + uart->fifo_size = 64;
>>> +}
>>> +
>>> +static void __init omap_uart_init_preirq(struct serial_port *port)
>>> +{
>>> + struct omap_uart *uart = port->uart;
>>> +
>>> + /*
>>> + * Clear the FIFO buffers.
>>> + */
>>> + omap_write(uart, UART_FCR, UART_FCR_ENABLE);
>>> + omap_write(uart, UART_FCR,
>>> UART_FCR_ENABLE|UART_FCR_CLRX|UART_FCR_CLTX);
>>> + omap_write(uart, UART_FCR, 0);
>>> +
>>> + /*
>>> + * The TRM says the mode should be disabled while UART_DLL and UART_DHL
>>> + * are being changed so we disable before setup, then enable.
>>> + */
>>> + omap_write(uart, UART_OMAP_MDR1, UART_OMAP_MDR1_DISABLE);
>>> +
>>> + /* Baud rate & protocol format setup */
>>> + baud_protocol_setup(uart);
>>> +
>>> + /* FIFO setup */
>>> + fifo_setup(uart);
>>> +
>>> + /* No flow control */
>>> + omap_write(uart, UART_MCR, UART_MCR_DTR|UART_MCR_RTS);
>>> +
>>> + omap_write(uart, UART_OMAP_MDR1, UART_OMAP_MDR1_16X_MODE);
>>> +}
>>> +
>>> +static void __init omap_uart_init_postirq(struct serial_port *port)
>>> +{
>>> + struct omap_uart *uart = port->uart;
>>> +
>>> + uart->irqaction.handler = omap_uart_interrupt;
>>> + uart->irqaction.name = "omap_uart";
>>> + uart->irqaction.dev_id = port;
>>> +
>>> + if ( setup_dt_irq(&uart->irq, &uart->irqaction) != 0 )
>>> + {
>>> + dprintk(XENLOG_ERR, "Failed to allocated omap_uart IRQ %d\n",
>>> + uart->irq.irq);
>>> + return;
>>> + }
>>> +
>>> + /* Enable interrupts */
>>> + omap_write(uart, UART_IER,
>>> UART_IER_ERDAI|UART_IER_ETHREI|UART_IER_ELSI);
>>> +}
>>> +
>>> +static void omap_uart_suspend(struct serial_port *port)
>>> +{
>>> + BUG();
>>> +}
>>> +
>>> +static void omap_uart_resume(struct serial_port *port)
>>> +{
>>> + BUG();
>>> +}
>>> +
>>> +static unsigned int omap_uart_tx_ready(struct serial_port *port)
>>> +{
>>> + struct omap_uart *uart = port->uart;
>>> +
>>> + omap_write(uart, UART_IER,
>>> UART_IER_ERDAI|UART_IER_ETHREI|UART_IER_ELSI);
>>
>> Why do you need to enable unmask interrupt here? Xen is just asking if
>> the UART is ready to transmit another character.
>
> Well, it is because we need to mask THRE interrupt after tx completes and
> unmake it
> once it starts to tx. This is one of main differences between OMAP UART and
> common
> 8250, since the THRE interrupt won't be cleared by writing to THR or reading
> IIR
> in OMAP UART.
>
> In Linux, there are .start_tx and .stop_tx callbacks for UART driver. Since we
> don't have that mechanism on Xen, I added it here.
If you need to only enable THRE interrupt, please only enable THRE interrupt.
You can't assume that *_ERDAI, *_ELSI are already set.
I think something like that is better:
reg = omap_read(uart, UART_IER);
omap_write(uart, UART_IER, reg | UART_IER);
This comment is the same for every place you play with the interrupt mask.
>>> +static const char const *omap_uart_dt_compat[] __initdata =
>>
>> const char * const.
>
> Hmmm, I just followed exynos4210-uart.c and pl011.c here...
Actually, this is an "issue" in the other drivers. const char * has the
same meaning as char const *. So one of the const was useless.
Cheers,
--
Julien Grall
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |