[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 Mon, Aug 12, 2013 at 11:21:22PM +0100, Julien Grall 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. > > > + return omap_read(uart, UART_LSR) & UART_LSR_THRE ? uart->fifo_size : 0; > > +} > > + > > +static void omap_uart_putc(struct serial_port *port, char c) > > +{ > > + struct omap_uart *uart = port->uart; > > + > > + omap_write(uart, UART_THR, (uint32_t)(unsigned char)c); > > +} > > + > > +static int omap_uart_getc(struct serial_port *port, char *pc) > > +{ > > + struct omap_uart *uart = port->uart; > > + > > + if ( !(omap_read(uart, UART_LSR) & UART_LSR_DR) ) > > + return 0; > > + > > + *pc = omap_read(uart, UART_RBR) & 0xff; > > + return 1; > > +} > > + > > +static int __init omap_uart_irq(struct serial_port *port) > > +{ > > + struct omap_uart *uart = port->uart; > > + > > + return ((uart->irq.irq > 0) ? uart->irq.irq : -1); > > +} > > + > > +static const struct dt_irq __init *omap_uart_dt_irq(struct serial_port > > *port) > > +{ > > + struct omap_uart *uart = port->uart; > > + > > + return &uart->irq; > > +} > > + > > +static struct uart_driver __read_mostly omap_uart_driver = { > > + .init_preirq = omap_uart_init_preirq, > > + .init_postirq = omap_uart_init_postirq, > > + .endboot = NULL, > > + .suspend = omap_uart_suspend, > > + .resume = omap_uart_resume, > > + .tx_ready = omap_uart_tx_ready, > > + .putc = omap_uart_putc, > > + .getc = omap_uart_getc, > > + .irq = omap_uart_irq, > > + .dt_irq_get = omap_uart_dt_irq, > Can you implement vuart_info callback? It's used to partially emulate UART > for Linux early printk. > > For an example, you can look at xen/drivers/char/exynos4210.c I rebased my tree to the latest master branch to get the vuart_info. However, after rebasing to the master, I cannot build xen successfully. It would always failed building with the message: arm-linux-gnueabihf-gcc -O1 -fno-omit-frame-pointer -marm -g -fno-strict-aliasing -std=gnu99 -Wall -Wstrict-prototypes -Wdeclaration-after-statement -Wno-unused-but-set-variable -Wno-unused-local-typedefs -fno-builtin -fno-common -Wredundant-decls -iwithprefix include -Werror -Wno-pointer-arith -pipe -I/home/cbz/src/xen/xen/include -fno-stack-protector -fno-exceptions -Wnested-externs -msoft-float -mcpu=cortex-a15 -DGCC_HAS_VISIBILITY_ATTRIBUTE -fno-optimize-sibling-calls -DEARLY_PRINTK -DEARLY_PRINTK_INC=\"debug-8250.inc\" -DEARLY_PRINTK_BAUD= -DEARLY_UART_BASE_ADDRESS=0x48020000 -DEARLY_UART_REG_SHIFT=2 -g -D__XEN__ -include /home/cbz/src/xen/xen/include/xen/config.h -DVERBOSE -fno-omit-frame-pointer -DCONFIG_FRAME_POINTER -MMD -MF .cpu.o.d -c cpu.c -o cpu.o make[4]: *** No rule to make target `vpl011.h', needed by `domain.o'. Stop. make[4]: Leaving directory `/home/cbz/src/xen/xen/arch/arm' make[3]: *** [/home/cbz/src/xen/xen/arch/arm/built_in.o] Error 2 make[3]: Leaving directory `/home/cbz/src/xen/xen/arch/arm' make[2]: *** [/home/cbz/src/xen/xen/xen] Error 2 make[2]: Leaving directory `/home/cbz/src/xen/xen' make[1]: *** [install] Error 2 make[1]: Leaving directory `/home/cbz/src/xen/xen' make: *** [install-xen] Error 2 > > > +}; > > + > > +static int __init omap_uart_init(struct dt_device_node *dev, > > + const void *data) > > +{ > > + const char *config = data; > > + struct omap_uart *uart; > > + u32 clkspec; > > + int res; > > + u64 addr, size; > > + > > + if ( strcmp(config, "") ) > > + early_printk("WARNING: UART configuration is not supported\n"); > > + > > + uart = &omap_com; > > + > > + res = dt_property_read_u32(dev, "clock-frequency", &clkspec); > > + if ( !res ) > > + { > > + early_printk("omap-uart: Unable to retrieve the clock > > frequency\n"); > > + return -EINVAL; > > + } > > + > > + uart->clock_hz = clkspec; > > + uart->baud = 115200; > > + uart->data_bits = 8; > > + uart->parity = UART_PARITY_NONE; > > + uart->stop_bits = 1; > > + res = dt_device_get_address(dev, 0, &addr, &size); > > + if ( res ) > > + { > > + early_printk("omap-uart: Unable to retrieve the base" > > + " address of the UART\n"); > > + return res; > > + } > > + > > + uart->regs = ioremap_attr(addr, size, PAGE_HYPERVISOR_NOCACHE); > > + if ( !uart->regs ) > > + { > > + early_printk("omap-uart: Unable to map the UART memory\n"); > > + return -ENOMEM; > > + } > > + > > + res = dt_device_get_irq(dev, 0, &uart->irq); > > + if ( res ) > > + { > > + early_printk("omap-uart: Unable to retrieve the IRQ\n"); > > + return res; > > + } > > + > > + /* Register with generic serial driver */ > > + serial_register_uart(SERHND_DTUART, &omap_uart_driver, uart); > > + > > + dt_device_set_used_by(dev, DOMID_XEN); > > + > > + return 0; > > +} > > + > > +static const char const *omap_uart_dt_compat[] __initdata = > > const char * const. > > > +{ > > + "ti,omap4-uart", > > + NULL > > +}; > > + > > +DT_DEVICE_START(omap_uart, "OMAP UART", DEVICE_SERIAL) > > + .compatible = omap_uart_dt_compat, > > + .init = omap_uart_init, > > +DT_DEVICE_END > > + > > +/* > > + * Local variables: > > + * mode: C > > + * c-file-style: "BSD" > > + * c-basic-offset: 4 > > + * indent-tabs-mode: nil > > + * End: > > + */ > > diff --git a/xen/include/xen/8250-uart.h b/xen/include/xen/8250-uart.h > > index 7287364..8693d15 100644 > > --- a/xen/include/xen/8250-uart.h > > +++ b/xen/include/xen/8250-uart.h > > @@ -59,14 +59,45 @@ > > #define UART_FCR_TRG8 0x80 /* Rx FIFO trig lev 8 */ > > #define UART_FCR_TRG14 0xc0 /* Rx FIFO trig lev 14 */ > > > > +/* > > + * Note: The FIFO trigger levels are chip specific: > > + * RX:76 = 00 01 10 11 TX:54 = 00 01 10 11 > > + * PC16550D: 1 4 8 14 xx xx xx xx > > + * TI16C550A: 1 4 8 14 xx xx xx xx > > + * TI16C550C: 1 4 8 14 xx xx xx xx > > + * ST16C550: 1 4 8 14 xx xx xx xx > > + * ST16C650: 8 16 24 28 16 8 24 30 PORT_16650V2 > > + * NS16C552: 1 4 8 14 xx xx xx xx > > + * ST16C654: 8 16 56 60 8 16 32 56 PORT_16654 > > + * TI16C750: 1 16 32 56 xx xx xx xx PORT_16750 > > + * TI16C752: 8 16 56 60 8 16 32 56 > > + * Tegra: 1 4 8 14 16 8 4 1 PORT_TEGRA > > + */ > > +#define UART_FCR_R_TRIG_00 0x00 > > +#define UART_FCR_R_TRIG_01 0x40 > > +#define UART_FCR_R_TRIG_10 0x80 > > +#define UART_FCR_R_TRIG_11 0xc0 > > +#define UART_FCR_T_TRIG_00 0x00 > > +#define UART_FCR_T_TRIG_01 0x10 > > +#define UART_FCR_T_TRIG_10 0x20 > > +#define UART_FCR_T_TRIG_11 0x30 > > + > > /* Line Control Register */ > > #define UART_LCR_DLAB 0x80 /* Divisor Latch Access */ > > > > +/* > > + * Access to some registers depends on register access / configuration > > + * mode. > > + */ > > +#define UART_LCR_CONF_MODE_A UART_LCR_DLAB /* Configuration mode A */ > > +#define UART_LCR_CONF_MODE_B 0xBF /* Configuration mode B */ > > + > > /* Modem Control Register */ > > #define UART_MCR_DTR 0x01 /* Data Terminal Ready */ > > #define UART_MCR_RTS 0x02 /* Request to Send */ > > #define UART_MCR_OUT2 0x08 /* OUT2: interrupt mask */ > > #define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ > > +#define UART_MCR_TCRTLR 0x40 /* Access TCR/TLR (TI16C752, EFR[4]=1) */ > > > > /* Line Status Register */ > > #define UART_LSR_DR 0x01 /* Data ready */ > > @@ -92,6 +123,26 @@ > > #define RESUME_DELAY MILLISECS(10) > > #define RESUME_RETRIES 100 > > > > +/* Enhanced feature register */ > > +#define UART_OMAP_EFR 0x02 > > + > > +#define UART_OMAP_EFR_ECB 0x10 /* Enhanced control bit */ > > + > > +/* Mode definition register 1 */ > > +#define UART_OMAP_MDR1 0x08 > > + > > +/* > > + * These are the definitions for the MDR1 register > > + */ > > +#define UART_OMAP_MDR1_16X_MODE 0x00 /* UART 16x mode */ > > +#define UART_OMAP_MDR1_DISABLE 0x07 /* Disable (default state) */ > > + > > +/* Supplementary control register */ > > +#define UART_OMAP_SCR 0x10 > > + > > +/* SCR register bitmasks */ > > +#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7) > > + > > #endif /* __XEN_8250_UART_H__ */ > > > > /* > > -- > Julien Grall _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |