[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH RFC 3/8] ns16550: make usable on ARM
On 10/09/2013 07:18, "Ian Campbell" <ian.campbell@xxxxxxxxxx> wrote: > There are several aspects to this: > - Correctly conditionalise use of PCI > - Correctly conditionalise use of IO ports > - Add discovery via device tree > - Support different registers shift/stride and widths > - Add vuart hooks. > > Signed-off-by: Ian Campbell <ian.campbell@xxxxxxxxxx> > Cc: keir@xxxxxxx > Cc: jbeulich@xxxxxxxx Bit of an ugly ifdef mess but it is only a serial driver. Acked-by: Keir Fraser <keir@xxxxxxx> > --- > config/arm32.mk | 1 + > xen/Rules.mk | 3 + > xen/arch/x86/Rules.mk | 1 + > xen/drivers/char/ns16550.c | 183 > +++++++++++++++++++++++++++++++++++++++++--- > 4 files changed, 176 insertions(+), 12 deletions(-) > > diff --git a/config/arm32.mk b/config/arm32.mk > index 76e229d..aa79d22 100644 > --- a/config/arm32.mk > +++ b/config/arm32.mk > @@ -12,6 +12,7 @@ CFLAGS += -marm > HAS_PL011 := y > HAS_EXYNOS4210 := y > HAS_OMAP := y > +HAS_NS16550 := y > > # Use only if calling $(LD) directly. > LDFLAGS_DIRECT += -EL > diff --git a/xen/Rules.mk b/xen/Rules.mk > index 736882a..df1428f 100644 > --- a/xen/Rules.mk > +++ b/xen/Rules.mk > @@ -60,6 +60,9 @@ CFLAGS-$(lock_profile) += -DLOCK_PROFILE > CFLAGS-$(HAS_ACPI) += -DHAS_ACPI > CFLAGS-$(HAS_GDBSX) += -DHAS_GDBSX > CFLAGS-$(HAS_PASSTHROUGH) += -DHAS_PASSTHROUGH > +CFLAGS-$(HAS_DEVICE_TREE) += -DHAS_DEVICE_TREE > +CFLAGS-$(HAS_PCI) += -DHAS_PCI > +CFLAGS-$(HAS_IOPORTS) += -DHAS_IOPORTS > CFLAGS-$(frame_pointer) += -fno-omit-frame-pointer -DCONFIG_FRAME_POINTER > > ifneq ($(max_phys_cpus),) > diff --git a/xen/arch/x86/Rules.mk b/xen/arch/x86/Rules.mk > index eb11b5b..c93d2af 100644 > --- a/xen/arch/x86/Rules.mk > +++ b/xen/arch/x86/Rules.mk > @@ -1,6 +1,7 @@ > ######################################## > # x86-specific definitions > > +HAS_IOPORTS := y > HAS_ACPI := y > HAS_VGA := y > HAS_VIDEO := y > diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c > index e0f80f6..854a572 100644 > --- a/xen/drivers/char/ns16550.c > +++ b/xen/drivers/char/ns16550.c > @@ -13,14 +13,19 @@ > #include <xen/init.h> > #include <xen/irq.h> > #include <xen/sched.h> > -#include <xen/pci.h> > #include <xen/timer.h> > #include <xen/serial.h> > #include <xen/iocap.h> > +#ifdef HAS_PCI > #include <xen/pci.h> > #include <xen/pci_regs.h> > +#endif > #include <xen/8250-uart.h> > +#include <xen/vmap.h> > #include <asm/io.h> > +#ifdef HAS_DEVICE_TREE > +#include <asm/device.h> > +#endif > #ifdef CONFIG_X86 > #include <asm/fixmap.h> > #endif > @@ -40,15 +45,22 @@ string_param("com2", opt_com2); > > static struct ns16550 { > int baud, clock_hz, data_bits, parity, stop_bits, fifo_size, irq; > - unsigned long io_base; /* I/O port or memory-mapped I/O address. */ > + u64 io_base; /* I/O port or memory-mapped I/O address. */ > + u64 io_size; > + int reg_shift; /* Bits to shift register offset by */ > + int reg_width; /* Number of bytes in each register */ > char __iomem *remapped_io_base; /* Remapped virtual address of MMIO. */ > /* UART with IRQ line: interrupt-driven I/O. */ > struct irqaction irqaction; > +#ifdef CONFIG_ARM > + struct vuart_info vuart; > +#endif > /* UART with no IRQ line: periodically-polled I/O. */ > struct timer timer; > struct timer resume_timer; > unsigned int timeout_ms; > bool_t intr_works; > +#ifdef HAS_PCI > /* PCI card parameters. */ > unsigned int pb_bdf[3]; /* pci bridge BDF */ > unsigned int ps_bdf[3]; /* pci serial port BDF */ > @@ -57,22 +69,46 @@ static struct ns16550 { > u32 bar; > u16 cr; > u8 bar_idx; > +#endif > +#ifdef HAS_DEVICE_TREE > + struct dt_irq dt_irq; > +#endif > } ns16550_com[2] = { { 0 } }; > > static void ns16550_delayed_resume(void *data); > > static char ns_read_reg(struct ns16550 *uart, int reg) > { > + void __iomem *addr = uart->remapped_io_base + (reg<<uart->reg_shift); > +#ifdef HAS_IOPORTS > if ( uart->remapped_io_base == NULL ) > return inb(uart->io_base + reg); > - return readb(uart->remapped_io_base + reg); > +#endif > + switch (uart->reg_width) { > + default: /* assume single byte */ > + case 1: > + return readb(addr); > + case 4: > + return readl(addr); > + } > } > > static void ns_write_reg(struct ns16550 *uart, int reg, char c) > { > + void __iomem *addr = uart->remapped_io_base + (reg<<uart->reg_shift); > +#ifdef HAS_IOPORTS > if ( uart->remapped_io_base == NULL ) > return outb(c, uart->io_base + reg); > - writeb(c, uart->remapped_io_base + reg); > +#endif > + switch (uart->reg_width) { > + default: /* assume single byte */ > + case 1: > + writeb(c, addr); > + break; > + case 4: > + writel(c, addr); > + break; > + } > } > > static int ns16550_ioport_invalid(struct ns16550 *uart) > @@ -161,6 +197,7 @@ static int ns16550_getc(struct serial_port *port, char > *pc) > return 1; > } > > +#ifdef HAS_PCI > static void pci_serial_early_init(struct ns16550 *uart) > { > if ( !uart->ps_bdf_enable || uart->io_base >= 0x10000 ) > @@ -178,6 +215,9 @@ static void pci_serial_early_init(struct ns16550 *uart) > pci_conf_write16(0, uart->ps_bdf[0], uart->ps_bdf[1], uart->ps_bdf[2], > PCI_COMMAND, PCI_COMMAND_IO); > } > +#else > +static void pci_serial_early_init(struct ns16550 *uart) {} > +#endif > > static void ns16550_setup_preirq(struct ns16550 *uart) > { > @@ -223,8 +263,10 @@ static void __init ns16550_init_preirq(struct serial_port > *port) > { > struct ns16550 *uart = port->uart; > > +#ifdef HAS_IOPORTS > /* I/O ports are distinguished by their size (16 bits). */ > if ( uart->io_base >= 0x10000 ) > +#endif > { > #ifdef CONFIG_X86 > enum fixed_addresses idx = FIX_COM_BEGIN + (uart - ns16550_com); > @@ -233,7 +275,7 @@ static void __init ns16550_init_preirq(struct serial_port > *port) > uart->remapped_io_base = (void __iomem *)fix_to_virt(idx); > uart->remapped_io_base += uart->io_base & ~PAGE_MASK; > #else > - uart->remapped_io_base = (char *)ioremap(uart->io_base, 8); > + uart->remapped_io_base = (char *)ioremap(uart->io_base, > uart->io_size); > #endif > } > > @@ -278,21 +320,27 @@ static void __init ns16550_init_postirq(struct > serial_port *port) > bits = uart->data_bits + uart->stop_bits + !!uart->parity; > uart->timeout_ms = max_t( > unsigned int, 1, (bits * uart->fifo_size * 1000) / uart->baud); > - > if ( uart->irq > 0 ) > { > uart->irqaction.handler = ns16550_interrupt; > uart->irqaction.name = "ns16550"; > uart->irqaction.dev_id = port; > +#ifdef HAS_DEVICE_TREE > + if ( (rc = setup_dt_irq(&uart->dt_irq, &uart->irqaction)) != 0 ) > + printk("ERROR: Failed to allocate ns16550 DT IRQ.\n"); > +#else > if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 ) > printk("ERROR: Failed to allocate ns16550 IRQ %d\n", uart->irq); > +#endif > } > > ns16550_setup_postirq(uart); > > +#ifdef HAS_PCI > if ( uart->bar || uart->ps_bdf_enable ) > pci_hide_device(uart->ps_bdf[0], PCI_DEVFN(uart->ps_bdf[1], > uart->ps_bdf[2])); > +#endif > } > > static void ns16550_suspend(struct serial_port *port) > @@ -301,13 +349,16 @@ static void ns16550_suspend(struct serial_port *port) > > stop_timer(&uart->timer); > > +#ifdef HAS_PCI > if ( uart->bar ) > uart->cr = pci_conf_read16(0, uart->ps_bdf[0], uart->ps_bdf[1], > uart->ps_bdf[2], PCI_COMMAND); > +#endif > } > > static void _ns16550_resume(struct serial_port *port) > { > +#ifdef HAS_PCI > struct ns16550 *uart = port->uart; > > if ( uart->bar ) > @@ -317,6 +368,7 @@ static void _ns16550_resume(struct serial_port *port) > pci_conf_write16(0, uart->ps_bdf[0], uart->ps_bdf[1], uart->ps_bdf[2], > PCI_COMMAND, uart->cr); > } > +#endif > > ns16550_setup_preirq(port->uart); > ns16550_setup_postirq(port->uart); > @@ -360,19 +412,17 @@ static void ns16550_resume(struct serial_port *port) > _ns16550_resume(port); > } > > -#ifdef CONFIG_X86 > static void __init ns16550_endboot(struct serial_port *port) > { > +#ifdef HAS_IOPORTS > struct ns16550 *uart = port->uart; > > if ( uart->remapped_io_base ) > return; > if ( ioports_deny_access(dom0, uart->io_base, uart->io_base + 7) != 0 ) > BUG(); > -} > -#else > -#define ns16550_endboot NULL > #endif > +} > > static int __init ns16550_irq(struct serial_port *port) > { > @@ -380,6 +430,23 @@ static int __init ns16550_irq(struct serial_port *port) > return ((uart->irq > 0) ? uart->irq : -1); > } > > +#ifdef HAS_DEVICE_TREE > +static const struct dt_irq __init *ns16550_dt_irq(struct serial_port *port) > +{ > + struct ns16550 *uart = port->uart; > + return &uart->dt_irq; > +} > +#endif > + > +#ifdef CONFIG_ARM > +static const struct vuart_info *ns16550_vuart_info(struct serial_port *port) > +{ > + struct ns16550 *uart = port->uart; > + > + return &uart->vuart; > +} > +#endif > + > static struct uart_driver __read_mostly ns16550_driver = { > .init_preirq = ns16550_init_preirq, > .init_postirq = ns16550_init_postirq, > @@ -389,7 +456,13 @@ static struct uart_driver __read_mostly ns16550_driver = > { > .tx_ready = ns16550_tx_ready, > .putc = ns16550_putc, > .getc = ns16550_getc, > - .irq = ns16550_irq > + .irq = ns16550_irq, > +#ifdef HAS_DEVICE_TREE > + .dt_irq_get = ns16550_dt_irq, > +#endif > +#ifdef CONFIG_ARM > + .vuart_info = ns16550_vuart_info, > +#endif > }; > > static int __init parse_parity_char(int c) > @@ -414,15 +487,21 @@ static int __init check_existence(struct ns16550 *uart) > { > unsigned char status, scratch, scratch2, scratch3; > > +#ifdef HAS_IO_PORTS > /* > * We can't poke MMIO UARTs until they get I/O remapped later. Assume > that > * if we're getting MMIO UARTs, the arch code knows what it's doing. > */ > if ( uart->io_base >= 0x10000 ) > return 1; > +#else > + return 1; /* Everything is MMIO */ > +#endif > > +#ifdef HAS_PCI > pci_serial_early_init(uart); > - > +#endif > + > /* > * Do a simple existence test first; if we fail this, > * there's no point trying anything else. > @@ -450,6 +529,7 @@ static int __init check_existence(struct ns16550 *uart) > return (status == 0x90); > } > > +#ifdef HAS_PCI > static int > pci_uart_config (struct ns16550 *uart, int skip_amt, int bar_idx) > { > @@ -518,6 +598,7 @@ pci_uart_config (struct ns16550 *uart, int skip_amt, int > bar_idx) > > return 0; > } > +#endif > > #define PARSE_ERR(_f, _a...) \ > do { \ > @@ -564,6 +645,7 @@ static void __init ns16550_parse_port_config( > > if ( *conf == ',' && *++conf != ',' ) > { > +#ifdef HAS_PCI > if ( strncmp(conf, "pci", 3) == 0 ) > { > if ( pci_uart_config(uart, 1/* skip AMT */, uart - ns16550_com) ) > @@ -577,6 +659,7 @@ static void __init ns16550_parse_port_config( > conf += 3; > } > else > +#endif > { > uart->io_base = simple_strtoul(conf, &conf, 0); > } > @@ -585,6 +668,7 @@ static void __init ns16550_parse_port_config( > if ( *conf == ',' && *++conf != ',' ) > uart->irq = simple_strtol(conf, &conf, 10); > > +#ifdef HAS_PCI > if ( *conf == ',' && *++conf != ',' ) > { > conf = parse_pci(conf, NULL, &uart->ps_bdf[0], > @@ -601,6 +685,7 @@ static void __init ns16550_parse_port_config( > PARSE_ERR("Bad bridge PCI coordinates"); > uart->pb_bdf_enable = 1; > } > +#endif > > config_parsed: > /* Sanity checks. */ > @@ -638,12 +723,86 @@ void __init ns16550_init(int index, struct > ns16550_defaults *defaults) > uart->stop_bits = defaults->stop_bits; > uart->irq = defaults->irq; > uart->io_base = defaults->io_base; > + uart->io_size = 8; > + uart->reg_width = 1; > + uart->reg_shift = 0; > + > /* Default is no transmit FIFO. */ > uart->fifo_size = 1; > > ns16550_parse_port_config(uart, (index == 0) ? opt_com1 : opt_com2); > } > > +#ifdef HAS_DEVICE_TREE > +static int __init ns16550_uart_dt_init(struct dt_device_node *dev, > + const void *data) > +{ > + struct ns16550 *uart; > + int res; > + u32 reg_shift, reg_width; > + > + uart = &ns16550_com[0]; > + > + uart->baud = BAUD_AUTO; > + uart->clock_hz = UART_CLOCK_HZ; > + uart->data_bits = 8; > + uart->parity = UART_PARITY_NONE; > + uart->stop_bits = 1; > + /* Default is no transmit FIFO. */ > + uart->fifo_size = 1; > + > + res = dt_device_get_address(dev, 0, &uart->io_base, &uart->io_size); > + if ( res ) > + return res; > + > + res = dt_property_read_u32(dev, "reg-shift", ®_shift); > + if ( !res ) > + uart->reg_shift = 0; > + else > + uart->reg_shift = reg_shift; > + > + res = dt_property_read_u32(dev, "reg-io-width", ®_width); > + if ( !res ) > + uart->reg_width = 1; > + else > + uart->reg_width = reg_width; > + > + if ( uart->reg_width != 1 && uart->reg_width != 4 ) > + return -EINVAL; > + > + res = dt_device_get_irq(dev, 0, &uart->dt_irq); > + if ( res ) > + return res; > + > + /* The common bit of the driver mostly deals with irq not dt_irq. */ > + uart->irq = uart->dt_irq.irq; > + > + uart->vuart.base_addr = uart->io_base; > + uart->vuart.size = uart->io_size; > + uart->vuart.data_off = UART_THR <<uart->reg_shift; > + uart->vuart.status_off = UART_LSR<<uart->reg_shift; > + uart->vuart.status = UART_LSR_THRE|UART_LSR_TEMT; > + > + /* Register with generic serial driver. */ > + serial_register_uart(uart - ns16550_com, &ns16550_driver, uart); > + > + dt_device_set_used_by(dev, DOMID_XEN); > + > + return 0; > +} > + > +static const char const *ns16550_dt_compat[] __initdata = > +{ > + "ns16550", > + NULL > +}; > + > +DT_DEVICE_START(ns16550, "NS16550 UART", DEVICE_SERIAL) > + .compatible = ns16550_dt_compat, > + .init = ns16550_uart_dt_init, > +DT_DEVICE_END > + > +#endif /* HAS_DEVICE_TREE */ > /* > * Local variables: > * mode: C _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |