See http://www.gossamer-threads.com/lists/xen/devel/297170 for some information on how to get this going. I''ve rebased and addressed the review comments. As before several of the patches are not to be applied because they can be done better using infrastructure from Julien''s "Allow Xen to boot with a raw Device Tree" patch. They are included for completeness. With this I can boot up until dom0 panics due to lack of a root filesystem. This is because upstream Linux currently lacks any useful storage drivers (SATA, USB host, MMC). Hopefully this will resolve itself soon! Despite this I think those patches which aren''t marked as being not for application can be applied as soon as people are happy with them. Hence I''ve dropped the RFC.
Common code uses this, it expects an uncached mapping. Signed-off-by: Ian Campbell <ian.campbell@citrix.com> Reviewed-by: Julien Grall <julien.grall@linaro.org> --- xen/arch/arm/mm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c index 69c157a..4521c8d 100644 --- a/xen/arch/arm/mm.c +++ b/xen/arch/arm/mm.c @@ -694,6 +694,11 @@ void *ioremap_attr(paddr_t pa, size_t len, unsigned int attributes) return (__vmap(&pfn, nr, 1, 1, attributes) + offs); } +void *ioremap(paddr_t pa, size_t len) +{ + return ioremap_attr(pa, len, PAGE_HYPERVISOR_NOCACHE); +} + static int create_xen_table(lpae_t *entry) { void *p; -- 1.7.10.4
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@citrix.com> Acked-by: Keir Frser <keir@xen.org> Cc: jbeulich@suse.com --- v2: Use __initdata Implement unknown register access sizes as write ignore, read all 1s. Coding style fixes Comment on reg_width meaning Fix ifdef placement io_size does not need 64-bits --- config/arm32.mk | 1 + xen/Rules.mk | 3 + xen/arch/x86/Rules.mk | 1 + xen/drivers/char/ns16550.c | 193 +++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 186 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..45da924 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,23 @@ 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. */ + u32 io_size; + int reg_shift; /* Bits to shift register offset by */ + int reg_width; /* Size of access to use, the registers + * themselves are still bytes */ 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 +70,51 @@ 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 ) + { + case 1: + return readb(addr); + case 4: + return readl(addr); + default: + return 0xff; + } } 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 ) + { + case 1: + writeb(c, addr); + break; + case 4: + writel(c, addr); + break; + default: + /* Ignored */ + break; + } } static int ns16550_ioport_invalid(struct ns16550 *uart) @@ -163,9 +205,10 @@ static int ns16550_getc(struct serial_port *port, char *pc) static void pci_serial_early_init(struct ns16550 *uart) { +#ifdef HAS_PCI if ( !uart->ps_bdf_enable || uart->io_base >= 0x10000 ) return; - + if ( uart->pb_bdf_enable ) pci_conf_write16(0, uart->pb_bdf[0], uart->pb_bdf[1], uart->pb_bdf[2], PCI_IO_BASE, @@ -177,6 +220,7 @@ static void pci_serial_early_init(struct ns16550 *uart) uart->io_base | PCI_BASE_ADDRESS_SPACE_IO); pci_conf_write16(0, uart->ps_bdf[0], uart->ps_bdf[1], uart->ps_bdf[2], PCI_COMMAND, PCI_COMMAND_IO); +#endif } static void ns16550_setup_preirq(struct ns16550 *uart) @@ -223,8 +267,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 +279,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 } @@ -284,15 +330,22 @@ static void __init ns16550_init_postirq(struct serial_port *port) 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 +354,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 +373,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 +417,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 +435,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 +461,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 +492,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 +534,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 +603,7 @@ pci_uart_config (struct ns16550 *uart, int skip_amt, int bar_idx) return 0; } +#endif #define PARSE_ERR(_f, _a...) \ do { \ @@ -564,6 +650,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 +664,7 @@ static void __init ns16550_parse_port_config( conf += 3; } else +#endif { uart->io_base = simple_strtoul(conf, &conf, 0); } @@ -585,6 +673,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 +690,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 +728,91 @@ 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; + u64 io_size; + + 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, &io_size); + if ( res ) + return res; + + uart->io_size = io_size; + + ASSERT(uart->io_size == io_size); /* Detect truncation */ + + 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[] __initconst +{ + "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 -- 1.7.10.4
This hardware has an additional feature which signals an error if you try to write LCR while the UART is busy. We need to clear this error during setup, otherwise LCR.DLAB doesn''t get set and we cannot read/write the divisor. This has been tested on the cubieboard2 Signed-off-by: Ian Campbell <ian.campbell@citrix.com> Acked-by: Keir Fraser <keir@xen.org> Cc: jbeulich@suse.com --- v2: Remove pointless cast. --- xen/drivers/char/ns16550.c | 14 ++++++++++++++ xen/include/xen/8250-uart.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c index 45da924..5892eb7 100644 --- a/xen/drivers/char/ns16550.c +++ b/xen/drivers/char/ns16550.c @@ -61,6 +61,7 @@ static struct ns16550 { struct timer resume_timer; unsigned int timeout_ms; bool_t intr_works; + bool_t dw_usr_bsy; #ifdef HAS_PCI /* PCI card parameters. */ unsigned int pb_bdf[3]; /* pci bridge BDF */ @@ -237,6 +238,16 @@ static void ns16550_setup_preirq(struct ns16550 *uart) /* No interrupts. */ ns_write_reg(uart, UART_IER, 0); + if ( uart->dw_usr_bsy && + (ns_read_reg(uart, UART_IIR) & UART_IIR_BSY) == UART_IIR_BSY ) + { + /* DesignWare 8250 detects if LCR is written while the UART is + * busy and raises a "busy detect" interrupt. Read the UART + * Status Register to clear this state. + */ + ns_read_reg(uart, UART_USR); + } + /* Line control and baud-rate generator. */ ns_write_reg(uart, UART_LCR, lcr | UART_LCR_DLAB); if ( uart->baud != BAUD_AUTO ) @@ -787,6 +798,8 @@ static int __init ns16550_uart_dt_init(struct dt_device_node *dev, /* The common bit of the driver mostly deals with irq not dt_irq. */ uart->irq = uart->dt_irq.irq; + uart->dw_usr_bsy = dt_device_is_compatible(dev, "snps,dw-apb-uart"); + uart->vuart.base_addr = uart->io_base; uart->vuart.size = uart->io_size; uart->vuart.data_off = UART_THR <<uart->reg_shift; @@ -804,6 +817,7 @@ static int __init ns16550_uart_dt_init(struct dt_device_node *dev, static const char const *ns16550_dt_compat[] __initconst { "ns16550", + "snps,dw-apb-uart", NULL }; diff --git a/xen/include/xen/8250-uart.h b/xen/include/xen/8250-uart.h index 8693d15..a682bae 100644 --- a/xen/include/xen/8250-uart.h +++ b/xen/include/xen/8250-uart.h @@ -32,6 +32,7 @@ #define UART_MCR 0x04 /* Modem control */ #define UART_LSR 0x05 /* line status */ #define UART_MSR 0x06 /* Modem status */ +#define UART_USR 0x1f /* Status register (DW) */ #define UART_DLL 0x00 /* divisor latch (ls) (DLAB=1) */ #define UART_DLM 0x01 /* divisor latch (ms) (DLAB=1) */ @@ -48,6 +49,7 @@ #define UART_IIR_RDA 0x04 /* - rx data recv''d */ #define UART_IIR_THR 0x02 /* - tx reg. empty */ #define UART_IIR_MSI 0x00 /* - MODEM status */ +#define UART_IIR_BSY 0x07 /* - busy detect (DW) */ /* FIFO Control Register */ #define UART_FCR_ENABLE 0x01 /* enable FIFO */ -- 1.7.10.4
NB: NOT TO BE APPLIED Julien has a patch to make this use a match list. This should be rebased onto that. Signed-off-by: Ian Campbell <ian.campbell@citrix.com> --- xen/arch/arm/gic.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index 7c24811..ab0f00d 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -360,6 +360,8 @@ void __init gic_init(void) node = dt_find_interrupt_controller("arm,cortex-a15-gic"); if ( !node ) + node = dt_find_interrupt_controller("arm,cortex-a7-gic"); + if ( !node ) panic("Unable to find compatible GIC in the device tree\n"); dt_device_set_used_by(node, DOMID_XEN); -- 1.7.10.4
Ian Campbell
2013-Sep-13 16:04 UTC
[PATCH 6/8] xen/arm: Basic support for sunxi/sun7i platform.
Specifically cubieboard2 Signed-off-by: Ian Campbell <ian.campbell@citrix.com> --- xen/arch/arm/platforms/Makefile | 1 + xen/arch/arm/platforms/sunxi.c | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 xen/arch/arm/platforms/sunxi.c diff --git a/xen/arch/arm/platforms/Makefile b/xen/arch/arm/platforms/Makefile index 4aa82e8..8c77ace 100644 --- a/xen/arch/arm/platforms/Makefile +++ b/xen/arch/arm/platforms/Makefile @@ -2,3 +2,4 @@ obj-y += vexpress.o obj-y += exynos5.o obj-y += midway.o obj-y += omap5.o +obj-y += sunxi.o diff --git a/xen/arch/arm/platforms/sunxi.c b/xen/arch/arm/platforms/sunxi.c new file mode 100644 index 0000000..ee0f39b --- /dev/null +++ b/xen/arch/arm/platforms/sunxi.c @@ -0,0 +1,42 @@ +/* + * xen/arch/arm/platforms/sunxi.c + * + * SUNXI (AllWinner A20/A31) specific settings + * + * Copyright (c) 2013 Citrix Systems. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#include <asm/p2m.h> +#include <xen/config.h> +#include <asm/platform.h> +#include <xen/mm.h> +#include <xen/device_tree.h> + +static const char const *sunxi_dt_compat[] __initdata +{ + "allwinner,sun7i-a20", + NULL +}; + +PLATFORM_START(sunxi, "Allwinner A20") + .compatible = sunxi_dt_compat, +PLATFORM_END + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ -- 1.7.10.4
These are in the same page as the UART which is used as the Xen console. We are not currently smart enough to avoid passing them through to the guest, accidentally giving the guest access to the Xen console UART. NOT TO BE APPLIED: Short term this should use Juliens forthcoming platform blacklist patch. Long term we need to be much cleverer about devices which share the same MMIO page... Signed-off-by: Ian Campbell <ian.campbell@citrix.com> --- xen/arch/arm/platforms/sunxi.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/xen/arch/arm/platforms/sunxi.c b/xen/arch/arm/platforms/sunxi.c index ee0f39b..4252580 100644 --- a/xen/arch/arm/platforms/sunxi.c +++ b/xen/arch/arm/platforms/sunxi.c @@ -22,6 +22,36 @@ #include <xen/mm.h> #include <xen/device_tree.h> +static int hide_by_name(const char *name) +{ + struct dt_device_node *n; + + n = dt_find_node_by_path(name); + if ( !n ) + { + printk("Unable to find %s to hide\n", name); + return -ENODEV; + } + dt_device_set_used_by(n, DOMID_XEN); + return 0; +} + +static int sunxi_init(void) +{ + int rc; + + if ( (rc = hide_by_name("/soc@01c00000/serial@01c28000")) < 0 ) + return rc; + if ( (rc = hide_by_name("/soc@01c00000/serial@01c28400")) < 0 ) + return rc; + if ( (rc = hide_by_name("/soc@01c00000/serial@01c28800")) < 0 ) + return rc; + if ( (rc = hide_by_name("/soc@01c00000/serial@01c28c00")) < 0 ) + return rc; + + return 0; +} + static const char const *sunxi_dt_compat[] __initdata { "allwinner,sun7i-a20", @@ -30,6 +60,7 @@ static const char const *sunxi_dt_compat[] __initdata PLATFORM_START(sunxi, "Allwinner A20") .compatible = sunxi_dt_compat, + .init = sunxi_init, PLATFORM_END /* -- 1.7.10.4
Not for really for application, might be useful to someone? Signed-off-by: Ian Campbell <ian.campbell@citrix.com> --- xen/arch/arm/arm32/head.S | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/xen/arch/arm/arm32/head.S b/xen/arch/arm/arm32/head.S index 79e95b6..06d53ad 100644 --- a/xen/arch/arm/arm32/head.S +++ b/xen/arch/arm/arm32/head.S @@ -366,6 +366,9 @@ paging: bne 1b launch: + adr r0, early_trap_vector + mcr CP32(r0, VBAR_EL2) + ldr r0, =init_stack /* Find the boot-time stack */ ldr sp, [r0] add sp, #STACK_SIZE /* (which grows down from the top). */ @@ -376,12 +379,24 @@ launch: beq start_xen /* and disappear into the land of C */ b start_secondary /* (to the appropriate entry point) */ +trap_early: + PRINT("\r\nEARLY TRAP\r\n") /* Fail-stop * r0: string explaining why */ fail: PRINT("- Boot failed -\r\n") 1: wfe b 1b + .align 5 +early_trap_vector: + .word 0 /* 0x00 - Reset */ + b trap_early /* 0x04 - Undefined Instruction */ + b trap_early /* 0x08 - Supervisor Call */ + b trap_early /* 0x0c - Prefetch Abort */ + b trap_early /* 0x10 - Data Abort */ + b trap_early /* 0x14 - Hypervisor */ + b trap_early /* 0x18 - IRQ */ + b trap_early /* 0x1c - FIQ */ #ifdef EARLY_PRINTK /* Bring up the UART. -- 1.7.10.4
>>> On 13.09.13 at 18:04, Ian Campbell <ian.campbell@citrix.com> 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@citrix.com> > Acked-by: Keir Frser <keir@xen.org>Reviewed-by: Jan Beulich <jbeulich@suse.com>> --- > v2: Use __initdata > Implement unknown register access sizes as write ignore, read all 1s. > Coding style fixes > Comment on reg_width meaning > Fix ifdef placement > io_size does not need 64-bits > --- > config/arm32.mk | 1 + > xen/Rules.mk | 3 + > xen/arch/x86/Rules.mk | 1 + > xen/drivers/char/ns16550.c | 193 +++++++++++++++++++++++++++++++++++++++++--- > 4 files changed, 186 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..45da924 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,23 @@ 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. */ > + u32 io_size; > + int reg_shift; /* Bits to shift register offset by */ > + int reg_width; /* Size of access to use, the registers > + * themselves are still bytes */ > 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 +70,51 @@ 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 ) > + { > + case 1: > + return readb(addr); > + case 4: > + return readl(addr); > + default: > + return 0xff; > + } > } > > 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 ) > + { > + case 1: > + writeb(c, addr); > + break; > + case 4: > + writel(c, addr); > + break; > + default: > + /* Ignored */ > + break; > + } > } > > static int ns16550_ioport_invalid(struct ns16550 *uart) > @@ -163,9 +205,10 @@ static int ns16550_getc(struct serial_port *port, char > *pc) > > static void pci_serial_early_init(struct ns16550 *uart) > { > +#ifdef HAS_PCI > if ( !uart->ps_bdf_enable || uart->io_base >= 0x10000 ) > return; > - > + > if ( uart->pb_bdf_enable ) > pci_conf_write16(0, uart->pb_bdf[0], uart->pb_bdf[1], uart->pb_bdf[2], > PCI_IO_BASE, > @@ -177,6 +220,7 @@ static void pci_serial_early_init(struct ns16550 *uart) > uart->io_base | PCI_BASE_ADDRESS_SPACE_IO); > pci_conf_write16(0, uart->ps_bdf[0], uart->ps_bdf[1], uart->ps_bdf[2], > PCI_COMMAND, PCI_COMMAND_IO); > +#endif > } > > static void ns16550_setup_preirq(struct ns16550 *uart) > @@ -223,8 +267,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 +279,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 > } > > @@ -284,15 +330,22 @@ static void __init ns16550_init_postirq(struct > serial_port *port) > 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 +354,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 +373,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 +417,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 +435,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 +461,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 +492,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 +534,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 +603,7 @@ pci_uart_config (struct ns16550 *uart, int skip_amt, int > bar_idx) > > return 0; > } > +#endif > > #define PARSE_ERR(_f, _a...) \ > do { \ > @@ -564,6 +650,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 +664,7 @@ static void __init ns16550_parse_port_config( > conf += 3; > } > else > +#endif > { > uart->io_base = simple_strtoul(conf, &conf, 0); > } > @@ -585,6 +673,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 +690,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 +728,91 @@ 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; > + u64 io_size; > + > + 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, &io_size); > + if ( res ) > + return res; > + > + uart->io_size = io_size; > + > + ASSERT(uart->io_size == io_size); /* Detect truncation */ > + > + 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[] __initconst > +{ > + "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 > -- > 1.7.10.4
Julien Grall
2013-Sep-13 16:43 UTC
Re: [PATCH RFC 0/8] xen/arm: initial cubieboard2 support.
On 09/13/2013 05:01 PM, Ian Campbell wrote:> See http://www.gossamer-threads.com/lists/xen/devel/297170 for some > information on how to get this going. > > I''ve rebased and addressed the review comments. > > As before several of the patches are not to be applied because they can > be done better using infrastructure from Julien''s "Allow Xen to boot > with a raw Device Tree" patch. They are included for completeness.I have a branch on my git tree with my latest device tree patch series (git://xenbits.xen.org/people/julieng/xen-unstable.git dt-edition-v4). You can at least rework the patch #6 and #8. For the latter, as discussed previously, in a near future, a solution based on memory trapping should be the best solution to handle correctly non page-aligned device.> With this I can boot up until dom0 panics due to lack of a root > filesystem. This is because upstream Linux currently lacks any useful > storage drivers (SATA, USB host, MMC). Hopefully this will resolve > itself soon! > > Despite this I think those patches which aren''t marked as being not for > application can be applied as soon as people are happy with them. Hence > I''ve dropped the RFC. >-- Julien Grall
Ian Campbell
2013-Sep-20 15:51 UTC
Re: [PATCH 2/8] xen/arm: replace io{read, write}{l, b} with {read, write}{l, b}
On Fri, 2013-09-13 at 17:33 +0100, Julien Grall wrote:> > read/write are used in common driver code (specifically ns16550) so instead of > > keeping our own variant around lets replace it with the more standard ones. > > > > At the same time resync with Linux making the "based on" comment in both setsof > s/setsof/sets of/ > > Can you precise the linux commit for the future?It''s v3.11, I''ve mentioned that in the updated version.> > -static inline void iowritel(const volatile void __iomem *addr, uint32_t val) > > +static inline void __raw_writeq(u64 val, volatile void __iomem *addr) > > { > > - dsb(); > > - asm volatile("str %w0, [%1]" : : "r" (val), "r" (addr)); > > + asm volatile("str %0, [%1]" : : "r" (val), "r" (addr)); > > The indentation seems wrong here and some other places.I deliberately retained the Linux style of indentation for these functions which came direct from Linux. Makes a visual check that they are the same much easier. Ian.
Apparently Analagous Threads
- [PATCH v3 0/7] support for cubieboard2 / sunxi processors
- [PATCH v8 8/5] Add UART support and arch timer initialization for OMAP5
- [PATCH RFC 5/8] ns16550: MMIO adjustments
- [PATCH 6/8] ns16550: PCI initialization adjustments
- Re: [Xen-changelog] [xen-3.4-testing] x86: Generalise BUGFRAME_dump mechanism to allow polled UART irq to