Hi, This is the third version of the patch series. It''s rebased on the lastest master, with 3 others patches. Major changes in v3: - Introduce ioremap_attr, ioreadl, iowritel - Typoes, hard tab, java doc... - Make generic UART based on DT architecture agnostic - Remove hardcoded IRQs in the virtual timer - Rework device tree printk Major changes in v2: - Add platform_poweroff and platform_quirks - Rework early assembly printk to use macro instead of function - Use defines where it''s possible in exynos UART code - Missing Signed-off-by from Anthony exynos UART code - Introduce request_dt_irq and setup_dt_irq. request_irq and setup_irq are deprecated for ARM. This part is divided in small patches (one to introduce new function and one to remove the old function) to avoid compilation breakage. For all changes see in each patch. Xen can boot on both Arndale Board and Versatile Express without recompilation. But there is still some hardcoded part, mainly in assembly. Things to do: - Move secondary CPUs bring up code in platform specific - Move out of Xen the switch between secure mode and hypervisor mode - Rework dom0 device tree creation - Use everywhere the new device tree API - Add a support for SYS MMU For the moment the ARM specific known bugs are: - Bash can sometimes crash with: segfault, memory corruption. It''s seems it''s due to a lack of VPF save/restore in Xen. - Assert in early code, ie before console is setup, let Xen spinned without an error message. If you want to try this patch series you can clone: git clone -b arm-v3 git://xenbits.xen.org/people/julieng/xen-unstable.git The command line to compile Xen is the same as before. You just need to specify on Xen command line, via the device tree, which UART will be used for the console. Feel free to test this branch. The wiki page is up-to-date for the arndale board: http://wiki.xen.org/wiki/Xen_ARM_with_Virtualization_Extensions/Arndale Cheers, Anthony PERARD (2): xen/arm: Add exynos 4210 UART support xen/arm: Add Exynos 4210 UART support for early printk Julien Grall (39): xen/arm: lr must be included in range [0-nr_lr( xen/arm: Don''t allow dom0 to access to vpl011 UART0 memory range xen/arm: Remove duplicated GICD_ICPIDR2 definition xen/arm: Bump early printk internal buffer to 512 xen/arm: Fix early_panic when EARLY_PRINTK is disabled xen/arm: Load dtb after dom0 kernel xen/arm: Switch to SYS_STATE_boot right after console setup xen/arm: Export early_vprintk xen/arm: Extend create_xen_entries prototype to take mapping attribute xen/mm: Align virtual address on PAGE_SIZE in iounmap xen/arm: Introduce ioremap_attr xen/arm: Add helpers ioreadl/iowritel xen/arm: Remove early_ioremap xen/arm: Create a hierarchical device tree xen/arm: Add helpers to use the device tree xen/arm: Add helpers to retrieve an address from the device tree xen/arm: Add helpers to retrieve an interrupt description from the device tree xen/arm: Introduce gic_route_dt_irq xen/arm: Introduce gic_irq_xlate xen/arm: Introduce setup_dt_irq xen/arm: Introduce request_dt_irq xen/arm: Use hierarchical device tree to retrieve GIC information xen/arm: Retrieve timer interrupts from the device tree xen/arm: Don''t hardcode VGIC informations xen/arm: Don''t hardcode virtual timer IRQs xen/arm: Introduce a generic way to use a device from the device tree xen/arm: New callback in uart_driver to get device tree interrupt structure xen/arm: Add generic UART to get the device in the device tree xen/arm: Use device tree API in pl011 UART driver xen/arm: Use the device tree to map the address range and IRQ to dom0 xen/arm: Allow Xen to run on multiple platform without recompilation xen/arm: WORKAROUND 1:1 memory mapping for dom0 xen/arm: Add versatile express platform xen/arm: Remove request_irq xen/arm: Remove setup_irq xen/arm: Don''t use pl011 UART by default for early printk xen/arm: Add platform specific code for the exynos5 xen/arm: WORKAROUND Support kick cpus and switch to hypervisor for the exynos5 xen/arm64: Remove hardcoded value for gic in assembly code config/arm32.mk | 1 + docs/misc/arm/early-printk.txt | 15 + xen/arch/arm/Makefile | 4 +- xen/arch/arm/Rules.mk | 21 + xen/arch/arm/arm32/Makefile | 4 +- xen/arch/arm/arm32/debug-exynos4210.inc | 77 ++ xen/arch/arm/arm32/debug-pl011.inc | 58 ++ xen/arch/arm/arm32/debug.S | 33 + xen/arch/arm/arm32/head.S | 84 +- xen/arch/arm/arm32/mode_switch.S | 75 +- xen/arch/arm/arm64/Makefile | 2 + xen/arch/arm/arm64/debug-pl011.inc | 59 ++ xen/arch/arm/arm64/debug.S | 33 + xen/arch/arm/arm64/head.S | 69 +- xen/arch/arm/arm64/mode_switch.S | 7 +- xen/arch/arm/device.c | 74 ++ xen/arch/arm/domain_build.c | 194 ++++- xen/arch/arm/early_printk.c | 21 +- xen/arch/arm/gic.c | 160 +++- xen/arch/arm/irq.c | 8 +- xen/arch/arm/mm.c | 57 +- xen/arch/arm/platform.c | 137 +++ xen/arch/arm/platforms/Makefile | 1 + xen/arch/arm/platforms/exynos5.c | 110 +++ xen/arch/arm/platforms/vexpress.c | 43 + xen/arch/arm/setup.c | 16 +- xen/arch/arm/shutdown.c | 16 +- xen/arch/arm/time.c | 68 +- xen/arch/arm/vgic.c | 20 +- xen/arch/arm/vpl011.c | 4 +- xen/arch/arm/vtimer.c | 13 +- xen/arch/arm/xen.lds.S | 14 + xen/common/device_tree.c | 1372 ++++++++++++++++++++++++++++-- xen/drivers/char/Makefile | 2 + xen/drivers/char/dt-uart.c | 69 ++ xen/drivers/char/exynos4210-uart.c | 359 ++++++++ xen/drivers/char/pl011.c | 84 +- xen/drivers/char/serial.c | 16 + xen/drivers/video/arm_hdlcd.c | 3 +- xen/include/asm-arm/config.h | 15 +- xen/include/asm-arm/device.h | 52 ++ xen/include/asm-arm/domain.h | 5 +- xen/include/asm-arm/early_printk.h | 8 +- xen/include/asm-arm/exynos4210-uart.h | 111 +++ xen/include/asm-arm/gic.h | 16 +- xen/include/asm-arm/irq.h | 8 + xen/include/asm-arm/mm.h | 14 +- xen/include/asm-arm/page.h | 2 + xen/include/asm-arm/platform.h | 61 ++ xen/include/asm-arm/platforms/exynos5.h | 39 + xen/include/asm-arm/platforms/vexpress.h | 14 + xen/include/asm-arm/time.h | 15 + xen/include/xen/device_tree.h | 404 ++++++++- xen/include/xen/serial.h | 11 +- xen/include/xen/vmap.h | 4 +- 55 files changed, 3804 insertions(+), 378 deletions(-) create mode 100644 docs/misc/arm/early-printk.txt create mode 100644 xen/arch/arm/arm32/debug-exynos4210.inc create mode 100644 xen/arch/arm/arm32/debug-pl011.inc create mode 100644 xen/arch/arm/arm32/debug.S create mode 100644 xen/arch/arm/arm64/debug-pl011.inc create mode 100644 xen/arch/arm/arm64/debug.S create mode 100644 xen/arch/arm/device.c create mode 100644 xen/arch/arm/platform.c create mode 100644 xen/arch/arm/platforms/exynos5.c create mode 100644 xen/drivers/char/dt-uart.c create mode 100644 xen/drivers/char/exynos4210-uart.c create mode 100644 xen/include/asm-arm/device.h create mode 100644 xen/include/asm-arm/exynos4210-uart.h create mode 100644 xen/include/asm-arm/platform.h create mode 100644 xen/include/asm-arm/platforms/exynos5.h -- 1.7.10.4
Julien Grall
2013-May-10 02:17 UTC
[PATCH V3 01/41] xen/arm: lr must be included in range [0-nr_lr(
Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> Changes in v2: - Typo in the commit message --- xen/arch/arm/gic.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index feb7b29..eeb526e 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -512,7 +512,9 @@ static inline void gic_set_lr(int lr, unsigned int virtual_irq, { int maintenance_int = GICH_LR_MAINTENANCE_IRQ; - BUG_ON(lr > nr_lrs); + BUG_ON(lr >= nr_lrs); + BUG_ON(lr < 0); + BUG_ON(state & ~(GICH_LR_STATE_MASK<<GICH_LR_STATE_SHIFT)); GICH[GICH_LR + lr] = state | maintenance_int | -- 1.7.10.4
Julien Grall
2013-May-10 02:17 UTC
[PATCH V3 02/41] xen/arm: Don''t allow dom0 to access to vpl011 UART0 memory range
As vpl011 UART is not initialized for dom 0, when the domain tries to access to this range, a segfault will occur in Xen. The right behaviour should be a data abort for the guest. Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> --- xen/arch/arm/vpl011.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xen/arch/arm/vpl011.c b/xen/arch/arm/vpl011.c index 9472d0a..13ba623 100644 --- a/xen/arch/arm/vpl011.c +++ b/xen/arch/arm/vpl011.c @@ -85,7 +85,9 @@ static void uart0_print_char(char c) static int uart0_mmio_check(struct vcpu *v, paddr_t addr) { - return addr >= UART0_START && addr < UART0_END; + struct domain *d = v->domain; + + return d->domain_id != 0 && addr >= UART0_START && addr < UART0_END; } static int uart0_mmio_read(struct vcpu *v, mmio_info_t *info) -- 1.7.10.4
Julien Grall
2013-May-10 02:17 UTC
[PATCH V3 03/41] xen/arm: Remove duplicated GICD_ICPIDR2 definition
Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> --- xen/include/asm-arm/gic.h | 1 - 1 file changed, 1 deletion(-) diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h index 84ebc83..3efb2b5 100644 --- a/xen/include/asm-arm/gic.h +++ b/xen/include/asm-arm/gic.h @@ -43,7 +43,6 @@ #define GICD_ICFGRN (0xCFC/4) #define GICD_NSACR (0xE00/4) #define GICD_NSACRN (0xEFC/4) -#define GICD_ICPIDR2 (0xFE8/4) #define GICD_SGIR (0xF00/4) #define GICD_CPENDSGIR (0xF10/4) #define GICD_CPENDSGIRN (0xF1C/4) -- 1.7.10.4
Julien Grall
2013-May-10 02:17 UTC
[PATCH V3 04/41] xen/arm: Bump early printk internal buffer to 512
When debug is enabled in device tree code, some lines are bigger than 80 characters. Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> Changes in v2: - Move buffer to a static variable --- xen/arch/arm/early_printk.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xen/arch/arm/early_printk.c b/xen/arch/arm/early_printk.c index bdf4c0e..0f99a43 100644 --- a/xen/arch/arm/early_printk.c +++ b/xen/arch/arm/early_printk.c @@ -29,6 +29,9 @@ void __init early_putch(char c) *r = c; } +/* Early printk buffer */ +static char __initdata buf[512]; + static void __init early_puts(const char *s) { while (*s != ''\0'') { @@ -41,8 +44,6 @@ static void __init early_puts(const char *s) static void __init early_vprintk(const char *fmt, va_list args) { - char buf[80]; - vsnprintf(buf, sizeof(buf), fmt, args); early_puts(buf); } -- 1.7.10.4
Julien Grall
2013-May-10 02:17 UTC
[PATCH V3 05/41] xen/arm: Fix early_panic when EARLY_PRINTK is disabled
Even if EARLY_PRINTK is not enabled, early_panic must never return. Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> --- xen/include/asm-arm/early_printk.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xen/include/asm-arm/early_printk.h b/xen/include/asm-arm/early_printk.h index a770d4a..a0297a7 100644 --- a/xen/include/asm-arm/early_printk.h +++ b/xen/include/asm-arm/early_printk.h @@ -20,7 +20,7 @@ void early_panic(const char *fmt, ...) __attribute__((noreturn)); #else static inline void early_printk(const char *fmt, ...) {} -static inline void early_panic(const char *fmt, ...) {} +static inline void __attribute__((noreturn)) early_panic(const char *fmt, ...) {while(1);} #endif -- 1.7.10.4
On some setup, the first linux page table is at 0x40004000. Xen will load dom0 device tree at 0x4000100. In case of the device tree is big, linux will corrupt the device tree. Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> --- xen/arch/arm/domain_build.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c index 8748272..6581492 100644 --- a/xen/arch/arm/domain_build.c +++ b/xen/arch/arm/domain_build.c @@ -407,8 +407,9 @@ int construct_dom0(struct domain *d) /* The following loads use the domain''s p2m */ p2m_load_VTTBR(d); - dtb_load(&kinfo); + kinfo.dtb_paddr = kinfo.zimage.load_addr + kinfo.zimage.len; kernel_load(&kinfo); + dtb_load(&kinfo); discard_initial_modules(); -- 1.7.10.4
Julien Grall
2013-May-10 02:17 UTC
[PATCH V3 07/41] xen/arm: Switch to SYS_STATE_boot right after console setup
Signed-off-by: Julien Grall <julien.grall@linaro.org> --- xen/arch/arm/setup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index 59646d6..29447ef 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -435,6 +435,8 @@ void __init start_xen(unsigned long boot_phys_offset, console_init_preirq(); #endif + system_state = SYS_STATE_boot; + processor_id(); init_xen_time(); -- 1.7.10.4
Signed-off-by: Julien Grall <julien.grall@linaro.org> --- xen/arch/arm/early_printk.c | 2 +- xen/include/asm-arm/early_printk.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/xen/arch/arm/early_printk.c b/xen/arch/arm/early_printk.c index 0f99a43..65fa912 100644 --- a/xen/arch/arm/early_printk.c +++ b/xen/arch/arm/early_printk.c @@ -42,7 +42,7 @@ static void __init early_puts(const char *s) } } -static void __init early_vprintk(const char *fmt, va_list args) +void __init early_vprintk(const char *fmt, va_list args) { vsnprintf(buf, sizeof(buf), fmt, args); early_puts(buf); diff --git a/xen/include/asm-arm/early_printk.h b/xen/include/asm-arm/early_printk.h index a0297a7..7083199 100644 --- a/xen/include/asm-arm/early_printk.h +++ b/xen/include/asm-arm/early_printk.h @@ -11,14 +11,18 @@ #define __ARM_EARLY_PRINTK_H__ #include <xen/config.h> +#include <xen/init.h> +#include <xen/stdarg.h> #ifdef EARLY_UART_ADDRESS +void __init early_vprintk(const char *fmt, va_list args); void early_printk(const char *fmt, ...); void early_panic(const char *fmt, ...) __attribute__((noreturn)); #else +static inline void __init early_vprintk(const char *fmt, va_list args) {} static inline void early_printk(const char *fmt, ...) {} static inline void __attribute__((noreturn)) early_panic(const char *fmt, ...) {while(1);} -- 1.7.10.4
Julien Grall
2013-May-10 02:17 UTC
[PATCH V3 09/41] xen/arm: Extend create_xen_entries prototype to take mapping attribute
Signed-off-by: Julien Grall <julien.grall@linaro.org> --- xen/arch/arm/mm.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c index 2836cbb..96297d3 100644 --- a/xen/arch/arm/mm.c +++ b/xen/arch/arm/mm.c @@ -631,7 +631,8 @@ enum xenmap_operation { static int create_xen_entries(enum xenmap_operation op, unsigned long virt, unsigned long mfn, - unsigned long nr_mfns) + unsigned long nr_mfns, + unsigned int flags) { int rc; unsigned long addr = virt, addr_end = addr + nr_mfns * PAGE_SIZE; @@ -664,6 +665,7 @@ static int create_xen_entries(enum xenmap_operation op, } pte = mfn_to_xen_entry(mfn); pte.pt.table = 1; + pte.pt.ai = flags; write_pte(&third[third_table_offset(addr)], pte); break; case REMOVE: @@ -693,12 +695,11 @@ int map_pages_to_xen(unsigned long virt, unsigned long nr_mfns, unsigned int flags) { - ASSERT(flags == PAGE_HYPERVISOR); - return create_xen_entries(INSERT, virt, mfn, nr_mfns); + return create_xen_entries(INSERT, virt, mfn, nr_mfns, flags); } void destroy_xen_mappings(unsigned long v, unsigned long e) { - create_xen_entries(REMOVE, v, 0, (e - v) >> PAGE_SHIFT); + create_xen_entries(REMOVE, v, 0, (e - v) >> PAGE_SHIFT, 0); } enum mg { mg_clear, mg_ro, mg_rw, mg_rx }; -- 1.7.10.4
Julien Grall
2013-May-10 02:17 UTC
[PATCH V3 10/41] xen/mm: Align virtual address on PAGE_SIZE in iounmap
ioremap function can unlikely return an unaligned virtual address if the physical address itself is unaligned on a page size. Signed-off-by: Julien Grall <julien.grall@linaro.org> CC: JBeulich@suse.com CC: keir@xen.org --- xen/include/xen/vmap.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xen/include/xen/vmap.h b/xen/include/xen/vmap.h index 88e5d99..c713a43 100644 --- a/xen/include/xen/vmap.h +++ b/xen/include/xen/vmap.h @@ -15,7 +15,9 @@ void __iomem *ioremap(paddr_t, size_t); static inline void iounmap(void __iomem *va) { - vunmap((void __force *)va); + vaddr_t addr = (vaddr_t)(void __force *)va; + + vunmap((void *)(addr & PAGE_MASK)); } void vm_init(void); -- 1.7.10.4
Map physical range in virtual memory with a specific mapping attribute. Also add new mapping attributes for ARM: PAGE_HYPERVISOR_NOCACHE and PAGE_HYPERVISOR_WC. Signed-off-by: Julien Grall <julien.grall@linaro.org> --- xen/arch/arm/mm.c | 14 ++++++++++++++ xen/arch/arm/setup.c | 4 ++-- xen/include/asm-arm/mm.h | 2 ++ xen/include/asm-arm/page.h | 2 ++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c index 96297d3..10d2869 100644 --- a/xen/arch/arm/mm.c +++ b/xen/arch/arm/mm.c @@ -38,6 +38,7 @@ #include <xen/sched.h> #include <xen/vmap.h> #include <xsm/xsm.h> +#include <xen/pfn.h> struct domain *dom_xen, *dom_io, *dom_cow; @@ -608,6 +609,19 @@ void *__init arch_vmap_virt_end(void) return (void *)early_vmap_start; } +/* + * This function should only be used to remap device address ranges + * TODO: add a check to verify this assumption + */ +void *ioremap_attr(paddr_t pa, size_t len, unsigned int attributes) +{ + unsigned long pfn = PFN_DOWN(pa); + unsigned int offs = pa & (PAGE_SIZE - 1); + unsigned int nr = PFN_UP(offs + len); + + return (__vmap(&pfn, nr, 1, 1, attributes) + offs); +} + static int create_xen_table(lpae_t *entry) { void *p; diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index 29447ef..a667db4 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -429,6 +429,8 @@ void __init start_xen(unsigned long boot_phys_offset, setup_pagetables(boot_phys_offset, get_xen_paddr()); setup_mm(fdt_paddr, fdt_size); + vm_init(); + #ifdef EARLY_UART_ADDRESS /* TODO Need to get device tree or command line for UART address */ pl011_init(0, FIXMAP_ADDR(FIXMAP_CONSOLE)); @@ -483,8 +485,6 @@ void __init start_xen(unsigned long boot_phys_offset, console_init_postirq(); - vm_init(); - do_presmp_initcalls(); for_each_present_cpu ( i ) diff --git a/xen/include/asm-arm/mm.h b/xen/include/asm-arm/mm.h index 26c271e..4ac8fab 100644 --- a/xen/include/asm-arm/mm.h +++ b/xen/include/asm-arm/mm.h @@ -155,6 +155,8 @@ extern void set_fixmap(unsigned map, unsigned long mfn, unsigned attributes); extern void clear_fixmap(unsigned map); /* map a 2MB aligned physical range in virtual memory. */ void* early_ioremap(paddr_t start, size_t len, unsigned attributes); +/* map a physical range in virtual memory */ +void *ioremap_attr(paddr_t start, size_t len, unsigned attributes); #define mfn_valid(mfn) ({ \ unsigned long __m_f_n = (mfn); \ diff --git a/xen/include/asm-arm/page.h b/xen/include/asm-arm/page.h index fd6946e..13fbd78 100644 --- a/xen/include/asm-arm/page.h +++ b/xen/include/asm-arm/page.h @@ -59,6 +59,8 @@ #define DEV_CACHED WRITEBACK #define PAGE_HYPERVISOR (MATTR_MEM) +#define PAGE_HYPERVISOR_NOCACHE (DEV_SHARED) +#define PAGE_HYPERVISOR_WC (DEV_WC) #define MAP_SMALL_PAGES PAGE_HYPERVISOR /* -- 1.7.10.4
Signed-off-by: Julien Grall <julien.grall@linaro.org> --- xen/include/asm-arm/mm.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/xen/include/asm-arm/mm.h b/xen/include/asm-arm/mm.h index 4ac8fab..b5ea2d1 100644 --- a/xen/include/asm-arm/mm.h +++ b/xen/include/asm-arm/mm.h @@ -156,7 +156,17 @@ extern void clear_fixmap(unsigned map); /* map a 2MB aligned physical range in virtual memory. */ void* early_ioremap(paddr_t start, size_t len, unsigned attributes); /* map a physical range in virtual memory */ -void *ioremap_attr(paddr_t start, size_t len, unsigned attributes); +void __iomem *ioremap_attr(paddr_t start, size_t len, unsigned attributes); + +static inline uint32_t ioreadl(const volatile void __iomem *addr) +{ + return (*(volatile uint32_t *)addr); +} + +static inline void iowritel(const volatile void __iomem *addr, uint32_t val) +{ + *(volatile uint32_t *)addr = val; +} #define mfn_valid(mfn) ({ \ unsigned long __m_f_n = (mfn); \ -- 1.7.10.4
In the commit "xen/arm: Introduce ioremap_attr", vamp initialization has been moved earlier in the boot process. All the calls to early_ioremap can be replaced by ioremap_attr. Signed-off-by: Julien Grall <julien.grall@linaro.org> --- xen/arch/arm/mm.c | 38 +------------------------------------- xen/drivers/video/arm_hdlcd.c | 3 ++- xen/include/asm-arm/mm.h | 2 -- 3 files changed, 3 insertions(+), 40 deletions(-) diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c index 10d2869..d8d0309 100644 --- a/xen/arch/arm/mm.c +++ b/xen/arch/arm/mm.c @@ -568,45 +568,9 @@ void __init setup_frametable_mappings(paddr_t ps, paddr_t pe) frametable_virt_end = FRAMETABLE_VIRT_START + (nr_pages * sizeof(struct page_info)); } -/* Map the physical memory range start - start + len into virtual - * memory and return the virtual address of the mapping. - * start has to be 2MB aligned. - * len has to be < VMAP_VIRT_END - VMAP_VIRT_START. - */ -static __initdata unsigned long early_vmap_start = VMAP_VIRT_END; -void* __init early_ioremap(paddr_t start, size_t len, unsigned attributes) -{ - paddr_t end = start + len; - unsigned long map_start; - - len = (len + SECOND_SIZE - 1) & ~SECOND_MASK; - early_vmap_start -= len; - - ASSERT(!(start & (~SECOND_MASK))); - ASSERT(!(early_vmap_start & (~SECOND_MASK))); - - /* The range we need to map is too big */ - if ( early_vmap_start >= VMAP_VIRT_START ) - return NULL; - - map_start = early_vmap_start; - while ( start < end ) - { - lpae_t e = mfn_to_xen_entry(start >> PAGE_SHIFT); - e.pt.ai = attributes; - write_pte(xen_second + second_table_offset(map_start), e); - - start += SECOND_SIZE; - map_start += SECOND_SIZE; - } - flush_xen_data_tlb_range_va(early_vmap_start, len); - - return (void*)early_vmap_start; -} - void *__init arch_vmap_virt_end(void) { - return (void *)early_vmap_start; + return (void *)VMAP_VIRT_END; } /* diff --git a/xen/drivers/video/arm_hdlcd.c b/xen/drivers/video/arm_hdlcd.c index d0ec13d..ecf2eba 100644 --- a/xen/drivers/video/arm_hdlcd.c +++ b/xen/drivers/video/arm_hdlcd.c @@ -211,7 +211,8 @@ void __init video_init(void) printk(KERN_INFO "Initializing HDLCD driver\n"); - lfb = early_ioremap(framebuffer_start, framebuffer_size, DEV_WC); + lfb = ioremap_attr(framebuffer_start, framebuffer_size, + PAGE_HYPERVISOR_WC); if ( !lfb ) { printk(KERN_ERR "Couldn''t map the framebuffer\n"); diff --git a/xen/include/asm-arm/mm.h b/xen/include/asm-arm/mm.h index b5ea2d1..6b993bc 100644 --- a/xen/include/asm-arm/mm.h +++ b/xen/include/asm-arm/mm.h @@ -153,8 +153,6 @@ extern void setup_frametable_mappings(paddr_t ps, paddr_t pe); extern void set_fixmap(unsigned map, unsigned long mfn, unsigned attributes); /* Remove a mapping from a fixmap entry */ extern void clear_fixmap(unsigned map); -/* map a 2MB aligned physical range in virtual memory. */ -void* early_ioremap(paddr_t start, size_t len, unsigned attributes); /* map a physical range in virtual memory */ void __iomem *ioremap_attr(paddr_t start, size_t len, unsigned attributes); -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 14/41] xen/arm: Create a hierarchical device tree
Add function to parse the device tree and create a hierarchical tree. This code is based on drivers/of/base.c in linux source. Signed-off-by: Julien Grall <julien.grall@linaro.org> Changes in v3: - Remove dt_switch_to_printk - Use system_state to choose between early_printk and printk - Remove DT_USE_BY_XEN, instead use DOMID_XEN Changes in v2: - s/dom0$/device/ in comment "By default dom0 owns the dom0" - Use DOMID_XEN instead of DOMID_INVALID for DT_USED_BY_XEN --- xen/arch/arm/setup.c | 1 + xen/common/device_tree.c | 463 ++++++++++++++++++++++++++++++++++++++++- xen/include/xen/device_tree.h | 89 ++++++++ 3 files changed, 548 insertions(+), 5 deletions(-) diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index a667db4..81bc956 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -430,6 +430,7 @@ void __init start_xen(unsigned long boot_phys_offset, setup_mm(fdt_paddr, fdt_size); vm_init(); + dt_unflatten_host_device_tree(); #ifdef EARLY_UART_ADDRESS /* TODO Need to get device tree or command line for UART address */ diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c index 7997f41..6d55a02 100644 --- a/xen/common/device_tree.c +++ b/xen/common/device_tree.c @@ -2,6 +2,8 @@ * Device Tree * * Copyright (C) 2012 Citrix Systems, Inc. + * Copyright 2009 Benjamin Herrenschmidt, IBM Corp + * benh@kernel.crashing.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -19,14 +21,64 @@ #include <xen/stdarg.h> #include <xen/string.h> #include <xen/cpumask.h> +#include <xen/ctype.h> +#include <xen/lib.h> #include <asm/early_printk.h> struct dt_early_info __initdata early_info; void *device_tree_flattened; +/* Host device tree */ +struct dt_device_node *dt_host; + +/** + * struct dt_alias_prop - Alias property in ''aliases'' node + * @link: List node to link the structure in aliases_lookup list + * @alias: Alias property name + * @np: Pointer to device_node that the alias stands for + * @id: Index value from end of alias name + * @stem: Alias string without the index + * + * The structure represents one alias property of ''aliases'' node as + * an entry in aliases_lookup list. + */ +struct dt_alias_prop { + struct list_head link; + const char *alias; + struct dt_device_node *np; + int id; + char stem[0]; +}; + +static LIST_HEAD(aliases_lookup); /* Some device tree functions may be called both before and after the console is initialized. */ -static void (*dt_printk)(const char *fmt, ...) = early_printk; +static void dt_printk(const char *fmt, ...) +{ + static char buf[512]; + va_list args; + + va_start(args, fmt); + + if ( system_state == SYS_STATE_early_boot ) + early_vprintk(fmt, args); + else + { + vsnprintf(buf, sizeof(buf), fmt, args); + printk(buf); + } + va_end(args); +} + +#define ALIGN(x, a) ((x + (a) - 1) & ~((a) - 1)); + +// #define DEBUG_DT + +#ifdef DEBUG_DT +# define dt_dprintk(fmt, args...) dt_printk(XENLOG_DEBUG fmt, ##args) +#else +# define dt_dprintk(fmt, args...) do {} while ( 0 ) +#endif bool_t device_tree_node_matches(const void *fdt, int node, const char *match) { @@ -263,7 +315,7 @@ static int dump_node(const void *fdt, int node, const char *name, int depth, if ( name[0] == ''\0'' ) name = "/"; - printk("%s%s:\n", prefix, name); + dt_printk("%s%s:\n", prefix, name); for ( prop = fdt_first_property_offset(fdt, node); prop >= 0; @@ -273,7 +325,7 @@ static int dump_node(const void *fdt, int node, const char *name, int depth, p = fdt_get_property_by_offset(fdt, prop, NULL); - printk("%s %s\n", prefix, fdt_string(fdt, fdt32_to_cpu(p->nameoff))); + dt_printk("%s %s\n", prefix, fdt_string(fdt, fdt32_to_cpu(p->nameoff))); } return 0; @@ -488,11 +540,412 @@ size_t __init device_tree_early_init(const void *fdt) device_tree_for_each_node((void *)fdt, early_scan_node, NULL); early_print_info(); - dt_printk = printk; - return fdt_totalsize(fdt); } +static void __init *unflatten_dt_alloc(unsigned long *mem, unsigned long size, + unsigned long align) +{ + void *res; + + *mem = ALIGN(*mem, align); + res = (void *)*mem; + *mem += size; + + return res; +} + +/* Find a property with a given name for a given node and return it. */ +static const struct dt_property * +dt_find_property(const struct dt_device_node *np, + const char *name, + u32 *lenp) +{ + const struct dt_property *pp; + + if ( !np ) + return NULL; + + for ( pp = np->properties; pp; pp = pp->next ) + { + if ( strcmp(pp->name, name) == 0 ) + { + if ( lenp ) + *lenp = pp->length; + break; + } + } + + return pp; +} + +const void *dt_get_property(const struct dt_device_node *np, + const char *name, u32 *lenp) +{ + const struct dt_property *pp = dt_find_property(np, name, lenp); + + return pp ? pp->value : NULL; +} + +struct dt_device_node *dt_find_node_by_path(const char *path) +{ + struct dt_device_node *np; + + for_each_device_node(dt_host, np) + if ( np->full_name && (dt_node_cmp(np->full_name, path) == 0) ) + break; + + return np; +} + +/** + * unflatten_dt_node - Alloc and populate a device_node from the flat tree + * @fdt: The parent device tree blob + * @mem: Memory chunk to use for allocating device nodes and properties + * @p: pointer to node in flat tree + * @dad: Parent struct device_node + * @allnextpp: pointer to ->allnext from last allocated device_node + * @fpsize: Size of the node path up at the current depth. + */ +static unsigned long __init unflatten_dt_node(const void *fdt, + unsigned long mem, + unsigned long *p, + struct dt_device_node *dad, + struct dt_device_node ***allnextpp, + unsigned long fpsize) +{ + struct dt_device_node *np; + struct dt_property *pp, **prev_pp = NULL; + char *pathp; + u32 tag; + unsigned int l, allocl; + int has_name = 0; + int new_format = 0; + + tag = be32_to_cpup((__be32 *)(*p)); + if ( tag != FDT_BEGIN_NODE ) + { + dt_printk(XENLOG_WARNING "Weird tag at start of node: %x\n", tag); + return mem; + } + *p += 4; + pathp = (char *)*p; + l = allocl = strlen(pathp) + 1; + *p = ALIGN(*p + l, 4); + + /* version 0x10 has a more compact unit name here instead of the full + * path. we accumulate the full path size using "fpsize", we''ll rebuild + * it later. We detect this because the first character of the name is + * not ''/''. + */ + if ( (*pathp) != ''/'' ) + { + new_format = 1; + if ( fpsize == 0 ) + { + /* root node: special case. fpsize accounts for path + * plus terminating zero. root node only has ''/'', so + * fpsize should be 2, but we want to avoid the first + * level nodes to have two ''/'' so we use fpsize 1 here + */ + fpsize = 1; + allocl = 2; + } + else + { + /* account for ''/'' and path size minus terminal 0 + * already in ''l'' + */ + fpsize += l; + allocl = fpsize; + } + } + + np = unflatten_dt_alloc(&mem, sizeof(struct dt_device_node) + allocl, + __alignof__(struct dt_device_node)); + if ( allnextpp ) + { + memset(np, 0, sizeof(*np)); + np->full_name = ((char *)np) + sizeof(struct dt_device_node); + /* By default dom0 owns the device */ + np->used_by = 0; + if ( new_format ) + { + char *fn = np->full_name; + /* rebuild full path for new format */ + if ( dad && dad->parent ) + { + strlcpy(fn, dad->full_name, allocl); +#ifdef DEBUG_DT + if ( (strlen(fn) + l + 1) != allocl ) + { + dt_dprintk("%s: p: %d, l: %d, a: %d\n", + pathp, (int)strlen(fn), + l, allocl); + } +#endif + fn += strlen(fn); + } + *(fn++) = ''/''; + memcpy(fn, pathp, l); + } + else + memcpy(np->full_name, pathp, l); + prev_pp = &np->properties; + **allnextpp = np; + *allnextpp = &np->allnext; + if ( dad != NULL ) + { + np->parent = dad; + /* we temporarily use the next field as `last_child''*/ + if ( dad->next == NULL ) + dad->child = np; + else + dad->next->sibling = np; + dad->next = np; + } + } + /* process properties */ + while ( 1 ) + { + u32 sz, noff; + const char *pname; + + tag = be32_to_cpup((__be32 *)(*p)); + if ( tag == FDT_NOP ) + { + *p += 4; + continue; + } + if ( tag != FDT_PROP ) + break; + *p += 4; + sz = be32_to_cpup((__be32 *)(*p)); + noff = be32_to_cpup((__be32 *)((*p) + 4)); + *p += 8; + if ( fdt_version(fdt) < 0x10 ) + *p = ALIGN(*p, sz >= 8 ? 8 : 4); + + pname = fdt_string(fdt, noff); + if ( pname == NULL ) + { + dt_dprintk("Can''t find property name in list!\n"); + break; + } + if ( strcmp(pname, "name") == 0 ) + has_name = 1; + l = strlen(pname) + 1; + pp = unflatten_dt_alloc(&mem, sizeof(struct dt_property), + __alignof__(struct dt_property)); + if ( allnextpp ) + { + /* We accept flattened tree phandles either in + * ePAPR-style "phandle" properties, or the + * legacy "linux,phandle" properties. If both + * appear and have different values, things + * will get weird. Don''t do that. */ + if ( (strcmp(pname, "phandle") == 0) || + (strcmp(pname, "linux,phandle") == 0) ) + { + if ( np->phandle == 0 ) + np->phandle = be32_to_cpup((__be32*)*p); + } + /* And we process the "ibm,phandle" property + * used in pSeries dynamic device tree + * stuff */ + if ( strcmp(pname, "ibm,phandle") == 0 ) + np->phandle = be32_to_cpup((__be32 *)*p); + pp->name = pname; + pp->length = sz; + pp->value = (void *)*p; + *prev_pp = pp; + prev_pp = &pp->next; + } + *p = ALIGN((*p) + sz, 4); + } + /* with version 0x10 we may not have the name property, recreate + * it here from the unit name if absent + */ + if ( !has_name ) + { + char *p1 = pathp, *ps = pathp, *pa = NULL; + int sz; + + while ( *p1 ) + { + if ( (*p1) == ''@'' ) + pa = p1; + if ( (*p1) == ''/'' ) + ps = p1 + 1; + p1++; + } + if ( pa < ps ) + pa = p1; + sz = (pa - ps) + 1; + pp = unflatten_dt_alloc(&mem, sizeof(struct dt_property) + sz, + __alignof__(struct dt_property)); + if ( allnextpp ) + { + pp->name = "name"; + pp->length = sz; + pp->value = pp + 1; + *prev_pp = pp; + prev_pp = &pp->next; + memcpy(pp->value, ps, sz - 1); + ((char *)pp->value)[sz - 1] = 0; + dt_dprintk("fixed up name for %s -> %s\n", pathp, + (char *)pp->value); + } + } + if ( allnextpp ) + { + *prev_pp = NULL; + np->name = dt_get_property(np, "name", NULL); + np->type = dt_get_property(np, "device_type", NULL); + + if ( !np->name ) + np->name = "<NULL>"; + if ( !np->type ) + np->type = "<NULL>"; + } + while ( tag == FDT_BEGIN_NODE || tag == FDT_NOP ) + { + if ( tag == FDT_NOP ) + *p += 4; + else + mem = unflatten_dt_node(fdt, mem, p, np, allnextpp, fpsize); + tag = be32_to_cpup((__be32 *)(*p)); + } + if ( tag != FDT_END_NODE ) + { + dt_printk(XENLOG_WARNING "Weird tag at end of node: %x\n", tag); + return mem; + } + + *p += 4; + return mem; +} + +/** + * __unflatten_device_tree - create tree of device_nodes from flat blob + * + * unflattens a device-tree, creating the + * tree of struct device_node. It also fills the "name" and "type" + * pointers of the nodes so the normal device-tree walking functions + * can be used. + * @fdt: The fdt to expand + * @mynodes: The device_node tree created by the call + */ +static void __init __unflatten_device_tree(const void *fdt, + struct dt_device_node **mynodes) +{ + unsigned long start, mem, size; + struct dt_device_node **allnextp = mynodes; + + dt_dprintk(" -> unflatten_device_tree()\n"); + + dt_dprintk("Unflattening device tree:\n"); + dt_dprintk("magic: %#08x\n", fdt_magic(fdt)); + dt_dprintk("size: %#08x\n", fdt_totalsize(fdt)); + dt_dprintk("version: %#08x\n", fdt_version(fdt)); + + /* First pass, scan for size */ + start = ((unsigned long)fdt) + fdt_off_dt_struct(fdt); + size = unflatten_dt_node(fdt, 0, &start, NULL, NULL, 0); + size = (size | 3) + 1; + + dt_dprintk(" size is %#lx allocating...\n", size); + + /* Allocate memory for the expanded device tree */ + mem = (unsigned long)_xmalloc (size + 4, __alignof__(struct dt_device_node)); + + ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef); + + dt_dprintk(" unflattening %lx...\n", mem); + + /* Second pass, do actual unflattening */ + start = ((unsigned long)fdt) + fdt_off_dt_struct(fdt); + unflatten_dt_node(fdt, mem, &start, NULL, &allnextp, 0); + if ( be32_to_cpup((__be32 *)start) != FDT_END ) + dt_printk(XENLOG_WARNING "Weird tag at end of tree: %08x\n", + *((u32 *)start)); + if ( be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeef ) + dt_printk(XENLOG_WARNING "End of tree marker overwritten: %08x\n", + be32_to_cpu(((__be32 *)mem)[size / 4])); + *allnextp = NULL; + + dt_dprintk(" <- unflatten_device_tree()\n"); +} + +static void dt_alias_add(struct dt_alias_prop *ap, + struct dt_device_node *np, + int id, const char *stem, int stem_len) +{ + ap->np = np; + ap->id = id; + strlcpy(ap->stem, stem, stem_len + 1); + list_add_tail(&ap->link, &aliases_lookup); + dt_dprintk("adding DT alias:%s: stem=%s id=%d node=%s\n", + ap->alias, ap->stem, ap->id, dt_node_full_name(np)); +} + +/** + * dt_alias_scan - Scan all properties of ''aliases'' node + * + * The function scans all the properties of ''aliases'' node and populate + * the the global lookup table with the properties. It returns the + * number of alias_prop found, or error code in error case. + */ +static void __init dt_alias_scan(void) +{ + const struct dt_property *pp; + const struct dt_device_node *aliases; + + aliases = dt_find_node_by_path("/aliases"); + if ( !aliases ) + return; + + for_each_property_of_node( aliases, pp ) + { + const char *start = pp->name; + const char *end = start + strlen(start); + struct dt_device_node *np; + struct dt_alias_prop *ap; + int id, len; + + /* Skip those we do not want to proceed */ + if ( !strcmp(pp->name, "name") || + !strcmp(pp->name, "phandle") || + !strcmp(pp->name, "linux,phandle") ) + continue; + + np = dt_find_node_by_path(pp->value); + if ( !np ) + continue; + + /* walk the alias backwards to extract the id and work out + * the ''stem'' string */ + while ( isdigit(*(end-1)) && end > start ) + end--; + len = end - start; + + id = simple_strtoll(end, NULL, 10); + + /* Allocate an alias_prop with enough space for the stem */ + ap = _xmalloc(sizeof(*ap) + len + 1, 4); + if ( !ap ) + continue; + ap->alias = start; + dt_alias_add(ap, np, id, start, len); + } +} + +void __init dt_unflatten_host_device_tree(void) +{ + __unflatten_device_tree(device_tree_flattened, &dt_host); + dt_alias_scan(); +} + /* * Local variables: * mode: C diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h index 19bda98..015b808 100644 --- a/xen/include/xen/device_tree.h +++ b/xen/include/xen/device_tree.h @@ -10,6 +10,10 @@ #ifndef __XEN_DEVICE_TREE_H__ #define __XEN_DEVICE_TREE_H__ +#include <asm/byteorder.h> +#include <public/xen.h> +#include <xen/init.h> +#include <xen/string.h> #include <xen/types.h> #define DEVICE_TREE_MAX_DEPTH 16 @@ -52,6 +56,49 @@ struct dt_early_info { struct dt_module_info modules; }; +typedef u32 dt_phandle; + +/** + * dt_property - describe a property for a device + * @name: name of the property + * @length: size of the value + * @value: pointer to data contained in the property + * @next: pointer to the next property of a specific node + */ +struct dt_property { + const char *name; + u32 length; + void *value; + struct dt_property *next; +}; + +/** + * dt_device_node - describe a node in the device tree + * @name: name of the node + * @type: type of the node (ie: memory, cpu, ...) + * @full_name: full name, it''s composed of all the ascendant name separate by / + * @used_by: who owns the node? (ie: xen, dom0...) + * @properties: list of properties for the node + * @child: pointer to the first child + * @sibling: pointer to the next sibling + * @allnext: pointer to the next in list of all nodes + */ +struct dt_device_node { + const char *name; + const char *type; + dt_phandle phandle; + char *full_name; + domid_t used_by; /* By default it''s used by dom0 */ + + struct dt_property *properties; + struct dt_device_node *parent; + struct dt_device_node *child; + struct dt_device_node *sibling; + struct dt_device_node *next; /* TODO: Remove it. Only use to know the last children */ + struct dt_device_node *allnext; + +}; + typedef int (*device_tree_node_func)(const void *fdt, int node, const char *name, int depth, u32 address_cells, u32 size_cells, @@ -77,4 +124,46 @@ int device_tree_for_each_node(const void *fdt, const char *device_tree_bootargs(const void *fdt); void device_tree_dump(const void *fdt); +/** + * dt_unflatten_host_device_tree - Unflatten the host device tree + * + * Create a hierarchical device tree for the host DTB to be able + * to retrieve parents. + */ +void __init dt_unflatten_host_device_tree(void); + +/** + * Host device tree + * DO NOT modify it! + */ +extern struct dt_device_node *dt_host; + +#define dt_node_cmp(s1, s2) strcmp((s1), (s2)) +#define dt_compat_cmp(s1, s2, l) strnicmp((s1), (s2), l) + +#define for_each_property_of_node(dn, pp) \ + for ( pp = dn->properties; pp != NULL; pp = pp->next ) + +#define for_each_device_node(dt, dn) \ + for ( dn = dt; dn != NULL; dn = dn->allnext ) + +static inline const char *dt_node_full_name(const struct dt_device_node *np) +{ + return (np && np->full_name) ? np->full_name : "<no-node>"; +} + +/** + * Find a property with a given name for a given node + * and return the value. + */ +const void *dt_get_property(const struct dt_device_node *np, + const char *name, u32 *lenp); + +/** + * dt_find_node_by_path - Find a node matching a full DT path + * @path: The full path to match + * + * Returns a node pointer. + */ +struct dt_device_node *dt_find_node_by_path(const char *path); #endif -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 15/41] xen/arm: Add helpers to use the device tree
Signed-off-by: Julien Grall <julien.grall@linaro.org> Changes in v3: - Allow to use "old" functions only in init code Changes in v2: - Use dt_node_cmp and dt_compat_cmp in early device tree code --- xen/common/device_tree.c | 162 ++++++++++++++++++++++++++++++++++++----- xen/include/xen/device_tree.h | 155 +++++++++++++++++++++++++++++++++++---- 2 files changed, 283 insertions(+), 34 deletions(-) diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c index 6d55a02..c731105 100644 --- a/xen/common/device_tree.c +++ b/xen/common/device_tree.c @@ -80,7 +80,8 @@ static void dt_printk(const char *fmt, ...) # define dt_dprintk(fmt, args...) do {} while ( 0 ) #endif -bool_t device_tree_node_matches(const void *fdt, int node, const char *match) +bool_t __init device_tree_node_matches(const void *fdt, int node, + const char *match) { const char *name; size_t match_len; @@ -94,7 +95,8 @@ bool_t device_tree_node_matches(const void *fdt, int node, const char *match) && (name[match_len] == ''@'' || name[match_len] == ''\0''); } -bool_t device_tree_type_matches(const void *fdt, int node, const char *match) +bool_t __init device_tree_type_matches(const void *fdt, int node, + const char *match) { const void *prop; @@ -102,20 +104,24 @@ bool_t device_tree_type_matches(const void *fdt, int node, const char *match) if ( prop == NULL ) return 0; - return !strcmp(prop, match); + return !dt_node_cmp(prop, match); } -bool_t device_tree_node_compatible(const void *fdt, int node, const char *match) +bool_t __init device_tree_node_compatible(const void *fdt, int node, + const char *match) { int len, l; + int mlen; const void *prop; + mlen = strlen(match); + prop = fdt_getprop(fdt, node, "compatible", &len); if ( prop == NULL ) return 0; while ( len > 0 ) { - if ( !strcmp(prop, match) ) + if ( !dt_compat_cmp(prop, match, mlen) ) return 1; l = strlen(prop) + 1; prop += l; @@ -125,7 +131,7 @@ bool_t device_tree_node_compatible(const void *fdt, int node, const char *match) return 0; } -static int device_tree_nr_reg_ranges(const struct fdt_property *prop, +static __init int device_tree_nr_reg_ranges(const struct fdt_property *prop, u32 address_cells, u32 size_cells) { u32 reg_cells = address_cells + size_cells; @@ -146,14 +152,14 @@ static void __init get_val(const u32 **cell, u32 cells, u64 *val) } } -void device_tree_get_reg(const u32 **cell, u32 address_cells, u32 size_cells, - u64 *start, u64 *size) +void __init device_tree_get_reg(const u32 **cell, u32 address_cells, + u32 size_cells, u64 *start, u64 *size) { get_val(cell, address_cells, start); get_val(cell, size_cells, size); } -static void set_val(u32 **cell, u32 cells, u64 val) +static void __init set_val(u32 **cell, u32 cells, u64 val) { u32 c = cells; @@ -165,15 +171,15 @@ static void set_val(u32 **cell, u32 cells, u64 val) (*cell) += cells; } -void device_tree_set_reg(u32 **cell, u32 address_cells, u32 size_cells, - u64 start, u64 size) +void __init device_tree_set_reg(u32 **cell, u32 address_cells, u32 size_cells, + u64 start, u64 size) { set_val(cell, address_cells, start); set_val(cell, size_cells, size); } -u32 device_tree_get_u32(const void *fdt, int node, const char *prop_name, - u32 dflt) +u32 __init device_tree_get_u32(const void *fdt, int node, const char *prop_name, + u32 dflt) { const struct fdt_property *prop; @@ -195,8 +201,8 @@ u32 device_tree_get_u32(const void *fdt, int node, const char *prop_name, * Returns 0 if all nodes were iterated over successfully. If @func * returns a value different from 0, that value is returned immediately. */ -int device_tree_for_each_node(const void *fdt, - device_tree_node_func func, void *data) +int __init device_tree_for_each_node(const void *fdt, + device_tree_node_func func, void *data) { int node; int depth; @@ -262,8 +268,8 @@ static int _find_compatible_node(const void *fdt, return 0; } -int find_compatible_node(const char *compatible, int *node, int *depth, - u32 *address_cells, u32 *size_cells) +int __init find_compatible_node(const char *compatible, int *node, int *depth, + u32 *address_cells, u32 *size_cells) { int ret; struct find_compat c; @@ -335,7 +341,7 @@ static int dump_node(const void *fdt, int node, const char *name, int depth, * device_tree_dump - print a text representation of a device tree * @fdt: flat device tree to print */ -void device_tree_dump(const void *fdt) +void __init device_tree_dump(const void *fdt) { device_tree_for_each_node(fdt, dump_node, NULL); } @@ -587,6 +593,54 @@ const void *dt_get_property(const struct dt_device_node *np, return pp ? pp->value : NULL; } +bool_t dt_device_is_compatible(const struct dt_device_node *device, + const char *compat) +{ + const char* cp; + u32 cplen, l; + + cp = dt_get_property(device, "compatible", &cplen); + if ( cp == NULL ) + return 0; + while ( cplen > 0 ) + { + if ( dt_compat_cmp(cp, compat, strlen(compat)) == 0 ) + return 1; + l = strlen(cp) + 1; + cp += l; + cplen -= l; + } + + return 0; +} + +bool_t dt_machine_is_compatible(const char *compat) +{ + const struct dt_device_node *root; + bool_t rc = 0; + + root = dt_find_node_by_path("/"); + if ( root ) + { + rc = dt_device_is_compatible(root, compat); + } + return rc; +} + +struct dt_device_node *dt_find_node_by_name(struct dt_device_node *from, + const char *name) +{ + struct dt_device_node *np; + struct dt_device_node *dt; + + dt = from ? from->allnext : dt_host; + for_each_device_node(dt, np) + if ( np->name && (dt_node_cmp(np->name, name) == 0) ) + break; + + return np; +} + struct dt_device_node *dt_find_node_by_path(const char *path) { struct dt_device_node *np; @@ -598,6 +652,78 @@ struct dt_device_node *dt_find_node_by_path(const char *path) return np; } +struct dt_device_node *dt_find_node_by_alias(const char *alias) +{ + const struct dt_alias_prop *app; + + list_for_each_entry( app, &aliases_lookup, link ) + { + if ( !strcmp(app->alias, alias) ) + return app->np; + } + + return NULL; +} + +const struct dt_device_node *dt_get_parent(const struct dt_device_node *node) +{ + if ( !node ) + return NULL; + + return node->parent; +} + +struct dt_device_node * +dt_find_compatible_node(struct dt_device_node *from, + const char *type, + const char *compatible) +{ + struct dt_device_node *np; + struct dt_device_node *dt; + + dt = from ? from->allnext : dt_host; + for_each_device_node(dt, np) + { + if ( type + && !(np->type && (dt_node_cmp(np->type, type) == 0)) ) + continue; + if ( dt_device_is_compatible(np, compatible) ) + break; + } + + return np; +} + +int dt_n_addr_cells(const struct dt_device_node *np) +{ + const __be32 *ip; + + do { + if ( np->parent ) + np = np->parent; + ip = dt_get_property(np, "#address-cells", NULL); + if ( ip ) + return be32_to_cpup(ip); + } while ( np->parent ); + /* No #address-cells property for the root node */ + return DT_ROOT_NODE_ADDR_CELLS_DEFAULT; +} + +int dt_n_size_cells(const struct dt_device_node *np) +{ + const __be32 *ip; + + do { + if ( np->parent ) + np = np->parent; + ip = dt_get_property(np, "#size-cells", NULL); + if ( ip ) + return be32_to_cpup(ip); + } while ( np->parent ); + /* No #address-cells property for the root node */ + return DT_ROOT_NODE_SIZE_CELLS_DEFAULT; +} + /** * unflatten_dt_node - Alloc and populate a device_node from the flat tree * @fdt: The parent device tree blob diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h index 015b808..7a6adc7 100644 --- a/xen/include/xen/device_tree.h +++ b/xen/include/xen/device_tree.h @@ -107,22 +107,25 @@ typedef int (*device_tree_node_func)(const void *fdt, extern struct dt_early_info early_info; extern void *device_tree_flattened; -size_t device_tree_early_init(const void *fdt); - -void device_tree_get_reg(const u32 **cell, u32 address_cells, u32 size_cells, - u64 *start, u64 *size); -void device_tree_set_reg(u32 **cell, u32 address_cells, u32 size_cells, - u64 start, u64 size); -u32 device_tree_get_u32(const void *fdt, int node, const char *prop_name, - u32 dflt); -bool_t device_tree_node_matches(const void *fdt, int node, const char *match); -bool_t device_tree_node_compatible(const void *fdt, int node, const char *match); -int find_compatible_node(const char *compatible, int *node, int *depth, - u32 *address_cells, u32 *size_cells); -int device_tree_for_each_node(const void *fdt, - device_tree_node_func func, void *data); -const char *device_tree_bootargs(const void *fdt); -void device_tree_dump(const void *fdt); +size_t __init device_tree_early_init(const void *fdt); + +void __init device_tree_get_reg(const u32 **cell, u32 address_cells, + u32 size_cells, + u64 *start, u64 *size); +void __init device_tree_set_reg(u32 **cell, u32 address_cells, u32 size_cells, + u64 start, u64 size); +u32 __init device_tree_get_u32(const void *fdt, int node, + const char *prop_name, u32 dflt); +bool_t __init device_tree_node_matches(const void *fdt, int node, + const char *match); +bool_t __init device_tree_node_compatible(const void *fdt, int node, + const char *match); +int __init find_compatible_node(const char *compatible, int *node, int *depth, + u32 *address_cells, u32 *size_cells); +int __init device_tree_for_each_node(const void *fdt, + device_tree_node_func func, void *data); +const char __init *device_tree_bootargs(const void *fdt); +void __init device_tree_dump(const void *fdt); /** * dt_unflatten_host_device_tree - Unflatten the host device tree @@ -141,17 +144,72 @@ extern struct dt_device_node *dt_host; #define dt_node_cmp(s1, s2) strcmp((s1), (s2)) #define dt_compat_cmp(s1, s2, l) strnicmp((s1), (s2), l) +/* Default #address and #size cells */ +#define DT_ROOT_NODE_ADDR_CELLS_DEFAULT 1 +#define DT_ROOT_NODE_SIZE_CELLS_DEFAULT 1 + #define for_each_property_of_node(dn, pp) \ for ( pp = dn->properties; pp != NULL; pp = pp->next ) #define for_each_device_node(dt, dn) \ for ( dn = dt; dn != NULL; dn = dn->allnext ) +/* Helper to read a big number; size is in cells (not bytes) */ +static inline u64 dt_read_number(const __be32 *cell, int size) +{ + u64 r = 0; + + while ( size-- ) + r = (r << 32) | be32_to_cpu(*(cell++)); + return r; +} + static inline const char *dt_node_full_name(const struct dt_device_node *np) { return (np && np->full_name) ? np->full_name : "<no-node>"; } +static inline const char *dt_node_name(const struct dt_device_node *np) +{ + return (np && np->name) ? np->name : "<no-node>"; +} + +static inline bool_t +dt_device_type_is_equal(const struct dt_device_node *device, + const char *type) +{ + return !dt_node_cmp(device->type, type); +} + +static inline void dt_device_set_used_by(struct dt_device_node *device, + domid_t used_by) +{ + /* TODO: children must inherit to the used_by thing */ + device->used_by = used_by; +} + +static inline domid_t dt_device_used_by(const struct dt_device_node *device) +{ + return device->used_by; +} + +/** + * dt_find_compatible_node - Find a node based on type and one of the + * tokens in its "compatible" property + * @from: The node to start searching from or NULL, the node + * you pass will not be searched, only the next one + * will; typically, you pass what the previous call + * returned. + * @type: The type string to match "device_type" or NULL to ignore + * @compatible: The string to match to one of the tokens in the device + * "compatible" list. + * + * Returns a node pointer. + */ +struct dt_device_node *dt_find_compatible_node(struct dt_device_node *from, + const char *type, + const char *compatible); + /** * Find a property with a given name for a given node * and return the value. @@ -160,10 +218,75 @@ const void *dt_get_property(const struct dt_device_node *np, const char *name, u32 *lenp); /** + * Checks if the given "compat" string matches one of the strings in + * the device''s "compatible" property + */ +bool_t dt_device_is_compatible(const struct dt_device_node *device, + const char *compat); + +/** + * dt_machine_is_compatible - Test root of device tree for a given compatible value + * @compat: compatible string to look for in root node''s compatible property. + * + * Returns true if the root node has the given value in its + * compatible property. + */ +bool_t dt_machine_is_compatible(const char *compat); + +/** + * dt_find_node_by_name - Find a node by its "name" property + * @from: The node to start searching from or NULL, the node + * you pass will not be searched, only the next one + * will; typically, you pass what the previous call + * returned. of_node_put() will be called on it + * @name: The name string to match against + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct dt_device_node *dt_find_node_by_name(struct dt_device_node *node, + const char *name); + +/** + * df_find_node_by_alias - Find a node matching an alias + * @alias: The alias to match + * + * Returns a node pointer. + */ +struct dt_device_node *dt_find_node_by_alias(const char *alias); + +/** * dt_find_node_by_path - Find a node matching a full DT path * @path: The full path to match * * Returns a node pointer. */ struct dt_device_node *dt_find_node_by_path(const char *path); + +/** + * dt_get_parent - Get a node''s parent if any + * @node: Node to get parent + * + * Returns a node pointer. + */ +const struct dt_device_node *dt_get_parent(const struct dt_device_node *node); + +/** + * dt_n_size_cells - Helper to retrieve the number of cell for the size + * @np: node to get the value + * + * This function retrieves for a give device-tree node the number of + * cell for the size field. + */ +int dt_n_size_cells(const struct dt_device_node *np); + +/** + * dt_n_addr_cells - Helper to retrieve the number of cell for the address + * @np: node to get the value + * + * This function retrieves for a give device-tree node the number of + * cell for the address field. + */ +int dt_n_addr_cells(const struct dt_device_node *np); + #endif -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 16/41] xen/arm: Add helpers to retrieve an address from the device tree
Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> Changes in v3: - Typoes - Remove hard tabs --- xen/common/device_tree.c | 343 +++++++++++++++++++++++++++++++++++++++++ xen/include/xen/device_tree.h | 22 +++ 2 files changed, 365 insertions(+) diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c index c731105..27328c8 100644 --- a/xen/common/device_tree.c +++ b/xen/common/device_tree.c @@ -76,10 +76,38 @@ static void dt_printk(const char *fmt, ...) #ifdef DEBUG_DT # define dt_dprintk(fmt, args...) dt_printk(XENLOG_DEBUG fmt, ##args) +static void dt_dump_addr(const char *s, const __be32 *addr, int na) +{ + dt_dprintk("%s", s); + while ( na-- ) + dt_dprintk(" %08x", be32_to_cpu(*(addr++))); + dt_dprintk("\n"); +} #else # define dt_dprintk(fmt, args...) do {} while ( 0 ) +static void dt_dump_addr(const char *s, const __be32 *addr, int na) { } #endif +#define DT_BAD_ADDR ((u64)-1) + +/* Max address size we deal with */ +#define DT_MAX_ADDR_CELLS 4 +#define DT_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= DT_MAX_ADDR_CELLS) +#define DT_CHECK_COUNTS(na, ns) (DT_CHECK_ADDR_COUNT(na) && (ns) > 0) + +/* Callbacks for bus specific translators */ +struct dt_bus +{ + const char *name; + const char *addresses; + int (*match)(const struct dt_device_node *parent); + void (*count_cells)(const struct dt_device_node *child, + int *addrc, int *sizec); + u64 (*map)(__be32 *addr, const __be32 *range, int na, int ns, int pna); + int (*translate)(__be32 *addr, u64 offset, int na); + unsigned int (*get_flags)(const __be32 *addr); +}; + bool_t __init device_tree_node_matches(const void *fdt, int node, const char *match) { @@ -724,6 +752,321 @@ int dt_n_size_cells(const struct dt_device_node *np) return DT_ROOT_NODE_SIZE_CELLS_DEFAULT; } +/* + * Default translator (generic bus) + */ +static void dt_bus_default_count_cells(const struct dt_device_node *dev, + int *addrc, int *sizec) +{ + if ( addrc ) + *addrc = dt_n_addr_cells(dev); + if ( sizec ) + *sizec = dt_n_size_cells(dev); +} + +static u64 dt_bus_default_map(__be32 *addr, const __be32 *range, + int na, int ns, int pna) +{ + u64 cp, s, da; + + cp = dt_read_number(range, na); + s = dt_read_number(range + na + pna, ns); + da = dt_read_number(addr, na); + + dt_dprintk("DT: default map, cp=%llx, s=%llx, da=%llx\n", + (unsigned long long)cp, (unsigned long long)s, + (unsigned long long)da); + + /* + * If the number of address cells is larger than 2 we assume the + * mapping doesn''t specify a physical address. Rather, the address + * specifies an identifier that must match exactly. + */ + if ( na > 2 && memcmp(range, addr, na * 4) != 0 ) + return DT_BAD_ADDR; + + if ( da < cp || da >= (cp + s) ) + return DT_BAD_ADDR; + return da - cp; +} + +static int dt_bus_default_translate(__be32 *addr, u64 offset, int na) +{ + u64 a = dt_read_number(addr, na); + + memset(addr, 0, na * 4); + a += offset; + if ( na > 1 ) + addr[na - 2] = cpu_to_be32(a >> 32); + addr[na - 1] = cpu_to_be32(a & 0xffffffffu); + + return 0; +} +static unsigned int dt_bus_default_get_flags(const __be32 *addr) +{ + /* TODO: Return the type of memory (device, ...) for caching + * attribute during mapping */ + return 0; +} + +/* + * Array of bus specific translators + */ +static const struct dt_bus dt_busses[] +{ + /* Default */ + { + .name = "default", + .addresses = "reg", + .match = NULL, + .count_cells = dt_bus_default_count_cells, + .map = dt_bus_default_map, + .translate = dt_bus_default_translate, + .get_flags = dt_bus_default_get_flags, + }, +}; + +static const struct dt_bus *dt_match_bus(const struct dt_device_node *np) +{ + int i; + + for ( i = 0; i < ARRAY_SIZE(dt_busses); i++ ) + if ( !dt_busses[i].match || dt_busses[i].match(np) ) + return &dt_busses[i]; + BUG(); + + return NULL; +} + +static const __be32 *dt_get_address(const struct dt_device_node *dev, + int index, u64 *size, + unsigned int *flags) +{ + const __be32 *prop; + u32 psize; + const struct dt_device_node *parent; + const struct dt_bus *bus; + int onesize, i, na, ns; + + /* Get parent & match bus type */ + parent = dt_get_parent(dev); + if ( parent == NULL ) + return NULL; + bus = dt_match_bus(parent); + bus->count_cells(dev, &na, &ns); + + if ( !DT_CHECK_ADDR_COUNT(na) ) + return NULL; + + /* Get "reg" or "assigned-addresses" property */ + prop = dt_get_property(dev, bus->addresses, &psize); + if ( prop == NULL ) + return NULL; + psize /= 4; + + onesize = na + ns; + for ( i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++ ) + { + if ( i == index ) + { + if ( size ) + *size = dt_read_number(prop + na, ns); + if ( flags ) + *flags = bus->get_flags(prop); + return prop; + } + } + return NULL; +} + +static int dt_translate_one(const struct dt_device_node *parent, + const struct dt_bus *bus, + const struct dt_bus *pbus, + __be32 *addr, int na, int ns, + int pna, const char *rprop) +{ + const __be32 *ranges; + unsigned int rlen; + int rone; + u64 offset = DT_BAD_ADDR; + + ranges = dt_get_property(parent, rprop, &rlen); + if ( ranges == NULL ) + { + dt_printk(XENLOG_ERR "DT: no ranges; cannot translate\n"); + return 1; + } + if ( ranges == NULL || rlen == 0 ) + { + offset = dt_read_number(addr, na); + memset(addr, 0, pna * 4); + dt_dprintk("DT: empty ranges; 1:1 translation\n"); + goto finish; + } + + dt_dprintk("DT: walking ranges...\n"); + + /* Now walk through the ranges */ + rlen /= 4; + rone = na + pna + ns; + for ( ; rlen >= rone; rlen -= rone, ranges += rone ) + { + offset = bus->map(addr, ranges, na, ns, pna); + if ( offset != DT_BAD_ADDR ) + break; + } + if ( offset == DT_BAD_ADDR ) + { + dt_dprintk("DT: not found !\n"); + return 1; + } + memcpy(addr, ranges + na, 4 * pna); + +finish: + dt_dump_addr("DT: parent translation for:", addr, pna); + dt_dprintk("DT: with offset: %llx\n", (unsigned long long)offset); + + /* Translate it into parent bus space */ + return pbus->translate(addr, offset, pna); +} + +/* + * Translate an address from the device-tree into a CPU physical address, + * this walks up the tree and applies the various bus mappings on the + * way. + * + * Note: We consider that crossing any level with #size-cells == 0 to mean + * that translation is impossible (that is we are not dealing with a value + * that can be mapped to a cpu physical address). This is not really specified + * that way, but this is traditionally the way IBM at least do things + */ +static u64 __dt_translate_address(const struct dt_device_node *dev, + const __be32 *in_addr, const char *rprop) +{ + const struct dt_device_node *parent = NULL; + const struct dt_bus *bus, *pbus; + __be32 addr[DT_MAX_ADDR_CELLS]; + int na, ns, pna, pns; + u64 result = DT_BAD_ADDR; + + dt_dprintk("DT: ** translation for device %s **\n", dev->full_name); + + /* Get parent & match bus type */ + parent = dt_get_parent(dev); + if ( parent == NULL ) + goto bail; + bus = dt_match_bus(parent); + + /* Count address cells & copy address locally */ + bus->count_cells(dev, &na, &ns); + if ( !DT_CHECK_COUNTS(na, ns) ) + { + dt_printk(XENLOG_ERR "dt_parse: Bad cell count for %s\n", + dev->full_name); + goto bail; + } + memcpy(addr, in_addr, na * 4); + + dt_dprintk("DT: bus is %s (na=%d, ns=%d) on %s\n", + bus->name, na, ns, parent->full_name); + dt_dump_addr("DT: translating address:", addr, na); + + /* Translate */ + for ( ;; ) + { + /* Switch to parent bus */ + dev = parent; + parent = dt_get_parent(dev); + + /* If root, we have finished */ + if ( parent == NULL ) + { + dt_dprintk("DT: reached root node\n"); + result = dt_read_number(addr, na); + break; + } + + /* Get new parent bus and counts */ + pbus = dt_match_bus(parent); + pbus->count_cells(dev, &pna, &pns); + if ( !DT_CHECK_COUNTS(pna, pns) ) + { + printk(XENLOG_ERR "dt_parse: Bad cell count for %s\n", + dev->full_name); + break; + } + + dt_dprintk("DT: parent bus is %s (na=%d, ns=%d) on %s\n", + pbus->name, pna, pns, parent->full_name); + + /* Apply bus translation */ + if ( dt_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop) ) + break; + + /* Complete the move up one level */ + na = pna; + ns = pns; + bus = pbus; + + dt_dump_addr("DT: one level translation:", addr, na); + } + +bail: + return result; +} + +/* dt_device_address - Translate device tree address and return it */ +int dt_device_get_address(const struct dt_device_node *dev, int index, + u64 *addr, u64 *size) +{ + const __be32 *addrp; + unsigned int flags; + + addrp = dt_get_address(dev, index, size, &flags); + if ( addrp == NULL ) + return -EINVAL; + + if ( !addr ) + return -EINVAL; + + *addr = __dt_translate_address(dev, addrp, "ranges"); + + if ( *addr == DT_BAD_ADDR ) + return -EINVAL; + + return 0; +} + +unsigned int dt_number_of_address(const struct dt_device_node *dev) +{ + const __be32 *prop; + u32 psize; + const struct dt_device_node *parent; + const struct dt_bus *bus; + int onesize, na, ns; + + /* Get parent & match bus type */ + parent = dt_get_parent(dev); + if ( parent == NULL ) + return 0; + + bus = dt_match_bus(parent); + bus->count_cells(dev, &na, &ns); + + if ( !DT_CHECK_COUNTS(na, ns) ) + return 0; + + /* Get "reg" or "assigned-addresses" property */ + prop = dt_get_property(dev, bus->addresses, &psize); + if ( prop == NULL ) + return 0; + + psize /= 4; + onesize = na + ns; + + return (psize / onesize); +} + /** * unflatten_dt_node - Alloc and populate a device_node from the flat tree * @fdt: The parent device tree blob diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h index 7a6adc7..cc3a070 100644 --- a/xen/include/xen/device_tree.h +++ b/xen/include/xen/device_tree.h @@ -272,6 +272,28 @@ struct dt_device_node *dt_find_node_by_path(const char *path); const struct dt_device_node *dt_get_parent(const struct dt_device_node *node); /** + * dt_device_get_address - Resolve an address for a device + * @device: the device whose address is to be resolved + * @index: index of the address to resolve + * @addr: address filled by this function + * @size: size filled by this function + * + * This function resolves an address, walking the tree, for a give + * device-tree node. It returns 0 on success. + */ +int dt_device_get_address(const struct dt_device_node *dev, int index, + u64 *addr, u64 *size); + +/** + * dt_number_of_address - Get the number of addresses for a device + * @device: the device whose number of address is to be retrieved + * + * Return the number of address for this device or 0 if there is no + * address or an error occurred. + */ +unsigned int dt_number_of_address(const struct dt_device_node *device); + +/** * dt_n_size_cells - Helper to retrieve the number of cell for the size * @np: node to get the value * -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 17/41] xen/arm: Add helpers to retrieve an interrupt description from the device tree
Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> Changes in v3: - Rename dt_irq_is_level_trigger to dt_irq_is_level_triggered - Remove hard tabs - Typoes Changes in v2: - Move interrupt type from xen/irq.h to xen/device_tree.h - Prefix all defines by DT_ --- xen/common/device_tree.c | 362 +++++++++++++++++++++++++++++++++++++++++ xen/include/xen/device_tree.h | 130 +++++++++++++++ 2 files changed, 492 insertions(+) diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c index 27328c8..f0f60ce 100644 --- a/xen/common/device_tree.c +++ b/xen/common/device_tree.c @@ -27,8 +27,11 @@ struct dt_early_info __initdata early_info; void *device_tree_flattened; +dt_irq_xlate_func dt_irq_xlate; /* Host device tree */ struct dt_device_node *dt_host; +/* Interrupt controller node*/ +const struct dt_device_node *dt_interrupt_controller; /** * struct dt_alias_prop - Alias property in ''aliases'' node @@ -1037,6 +1040,81 @@ int dt_device_get_address(const struct dt_device_node *dev, int index, return 0; } +/** + * dt_find_node_by_phandle - Find a node given a phandle + * @handle: phandle of the node to find + * + * Returns a node pointer. + */ +static const struct dt_device_node *dt_find_node_by_phandle(dt_phandle handle) +{ + const struct dt_device_node *np; + + for_each_device_node(dt_host, np) + if ( np->phandle == handle ) + break; + + return np; +} + +/** + * dt_irq_find_parent - Given a device node, find its interrupt parent node + * @child: pointer to device node + * + * Returns a pointer to the interrupt parent node, or NULL if the interrupt + * parent could not be determined. + */ +static const struct dt_device_node * +dt_irq_find_parent(const struct dt_device_node *child) +{ + const struct dt_device_node *p; + const __be32 *parp; + + do + { + parp = dt_get_property(child, "interrupt-parent", NULL); + if ( parp == NULL ) + p = dt_get_parent(child); + else + p = dt_find_node_by_phandle(be32_to_cpup(parp)); + child = p; + } while ( p && dt_get_property(p, "#interrupt-cells", NULL) == NULL ); + + return p; +} + +unsigned int dt_number_of_irq(const struct dt_device_node *device) +{ + const struct dt_device_node *p; + const __be32 *intspec, *tmp; + u32 intsize, intlen; + + dt_dprintk("dt_irq_number: dev=%s\n", device->full_name); + + /* Get the interrupts property */ + intspec = dt_get_property(device, "interrupts", &intlen); + if ( intspec == NULL ) + return 0; + intlen /= sizeof(*intspec); + + dt_dprintk(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen); + + /* Look for the interrupt parent. */ + p = dt_irq_find_parent(device); + if ( p == NULL ) + return 0; + + /* Get size of interrupt specifier */ + tmp = dt_get_property(p, "#interrupt-cells", NULL); + if ( tmp == NULL ) + return 0; + intsize = be32_to_cpu(*tmp); + + dt_dprintk(" intsize=%d intlen=%d\n", intsize, intlen); + + return (intlen / intsize); +} + unsigned int dt_number_of_address(const struct dt_device_node *dev) { const __be32 *prop; @@ -1068,6 +1146,274 @@ unsigned int dt_number_of_address(const struct dt_device_node *dev) } /** + * dt_irq_map_raw - Low level interrupt tree parsing + * @parent: the device interrupt parent + * @intspec: interrupt specifier ("interrupts" property of the device) + * @ointsize: size of the passed in interrupt specifier + * @addr: address specifier (start of "reg" property of the device) + * @oirq: structure dt_raw_irq filled by this function + * + * Returns 0 on success and a negative number on error + * + * This function is a low-level interrupt tree walking function. It + * can be used to do a partial walk with synthesized reg and interrupts + * properties, for example when resolving PCI interrupts when no device + * node exist for the parent. + */ +static int dt_irq_map_raw(const struct dt_device_node *parent, + const __be32 *intspec, u32 ointsize, + const __be32 *addr, + struct dt_raw_irq *oirq) +{ + const struct dt_device_node *ipar, *tnode, *old = NULL, *newpar = NULL; + const __be32 *tmp, *imap, *imask; + u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; + u32 imaplen; + int match, i; + + dt_dprintk("dt_irq_map_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n", + parent->full_name, be32_to_cpup(intspec), + be32_to_cpup(intspec + 1), ointsize); + + ipar = parent; + + /* First get the #interrupt-cells property of the current cursor + * that tells us how to interpret the passed-in intspec. If there + * is none, we are nice and just walk up the tree + */ + do { + tmp = dt_get_property(ipar, "#interrupt-cells", NULL); + if ( tmp != NULL ) + { + intsize = be32_to_cpu(*tmp); + break; + } + tnode = ipar; + ipar = dt_irq_find_parent(ipar); + } while ( ipar ); + if ( ipar == NULL ) + { + dt_dprintk(" -> no parent found !\n"); + goto fail; + } + + dt_dprintk("dt_irq_map_raw: ipar=%s, size=%d\n", ipar->full_name, intsize); + + if ( ointsize != intsize ) + return -EINVAL; + + /* Look for this #address-cells. We have to implement the old linux + * trick of looking for the parent here as some device-trees rely on it + */ + old = ipar; + do { + tmp = dt_get_property(old, "#address-cells", NULL); + tnode = dt_get_parent(old); + old = tnode; + } while ( old && tmp == NULL ); + + old = NULL; + addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp); + + dt_dprintk(" -> addrsize=%d\n", addrsize); + + /* Now start the actual "proper" walk of the interrupt tree */ + while ( ipar != NULL ) + { + /* Now check if cursor is an interrupt-controller and if it is + * then we are done + */ + if ( dt_get_property(ipar, "interrupt-controller", NULL) != NULL ) + { + dt_dprintk(" -> got it !\n"); + if ( intsize > DT_MAX_IRQ_SPEC ) + { + dt_dprintk(" -> intsize(%u) greater than DT_MAX_IRQ_SPEC(%u)\n", + intsize, DT_MAX_IRQ_SPEC); + goto fail; + } + for ( i = 0; i < intsize; i++ ) + oirq->specifier[i] = dt_read_number(intspec + i, 1); + oirq->size = intsize; + oirq->controller = ipar; + return 0; + } + + /* Now look for an interrupt-map */ + imap = dt_get_property(ipar, "interrupt-map", &imaplen); + /* No interrupt map, check for an interrupt parent */ + if ( imap == NULL ) + { + dt_dprintk(" -> no map, getting parent\n"); + newpar = dt_irq_find_parent(ipar); + goto skiplevel; + } + imaplen /= sizeof(u32); + + /* Look for a mask */ + imask = dt_get_property(ipar, "interrupt-map-mask", NULL); + + /* If we were passed no "reg" property and we attempt to parse + * an interrupt-map, then #address-cells must be 0. + * Fail if it''s not. + */ + if ( addr == NULL && addrsize != 0 ) + { + dt_dprintk(" -> no reg passed in when needed !\n"); + goto fail; + } + + /* Parse interrupt-map */ + match = 0; + while ( imaplen > (addrsize + intsize + 1) && !match ) + { + /* Compare specifiers */ + match = 1; + for ( i = 0; i < addrsize && match; ++i ) + { + __be32 mask = imask ? imask[i] : cpu_to_be32(0xffffffffu); + match = ((addr[i] ^ imap[i]) & mask) == 0; + } + for ( ; i < (addrsize + intsize) && match; ++i ) + { + __be32 mask = imask ? imask[i] : cpu_to_be32(0xffffffffu); + match = ((intspec[i-addrsize] ^ imap[i]) & mask) == 0; + } + imap += addrsize + intsize; + imaplen -= addrsize + intsize; + + dt_dprintk(" -> match=%d (imaplen=%d)\n", match, imaplen); + + /* Get the interrupt parent */ + newpar = dt_find_node_by_phandle(be32_to_cpup(imap)); + imap++; + --imaplen; + + /* Check if not found */ + if ( newpar == NULL ) + { + dt_dprintk(" -> imap parent not found !\n"); + goto fail; + } + + /* Get #interrupt-cells and #address-cells of new + * parent + */ + tmp = dt_get_property(newpar, "#interrupt-cells", NULL); + if ( tmp == NULL ) + { + dt_dprintk(" -> parent lacks #interrupt-cells!\n"); + goto fail; + } + newintsize = be32_to_cpu(*tmp); + tmp = dt_get_property(newpar, "#address-cells", NULL); + newaddrsize = (tmp == NULL) ? 0 : be32_to_cpu(*tmp); + + dt_dprintk(" -> newintsize=%d, newaddrsize=%d\n", + newintsize, newaddrsize); + + /* Check for malformed properties */ + if ( imaplen < (newaddrsize + newintsize) ) + goto fail; + + imap += newaddrsize + newintsize; + imaplen -= newaddrsize + newintsize; + + dt_dprintk(" -> imaplen=%d\n", imaplen); + } + if ( !match ) + goto fail; + + old = newpar; + addrsize = newaddrsize; + intsize = newintsize; + intspec = imap - intsize; + addr = intspec - addrsize; + + skiplevel: + /* Iterate again with new parent */ + dt_dprintk(" -> new parent: %s\n", dt_node_full_name(newpar)); + ipar = newpar; + newpar = NULL; + } +fail: + return -EINVAL; +} + +int dt_device_get_raw_irq(const struct dt_device_node *device, int index, + struct dt_raw_irq *out_irq) +{ + const struct dt_device_node *p; + const __be32 *intspec, *tmp, *addr; + u32 intsize, intlen; + int res = -EINVAL; + + dt_dprintk("dt_device_get_raw_irq: dev=%s, index=%d\n", + device->full_name, index); + + /* Get the interrupts property */ + intspec = dt_get_property(device, "interrupts", &intlen); + if ( intspec == NULL ) + return -EINVAL; + intlen /= sizeof(*intspec); + + dt_dprintk(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen); + + /* Get the reg property (if any) */ + addr = dt_get_property(device, "reg", NULL); + + /* Look for the interrupt parent. */ + p = dt_irq_find_parent(device); + if ( p == NULL ) + return -EINVAL; + + /* Get size of interrupt specifier */ + tmp = dt_get_property(p, "#interrupt-cells", NULL); + if ( tmp == NULL ) + goto out; + intsize = be32_to_cpu(*tmp); + + dt_dprintk(" intsize=%d intlen=%d\n", intsize, intlen); + + /* Check index */ + if ( (index + 1) * intsize > intlen ) + goto out; + + /* Get new specifier and map it */ + res = dt_irq_map_raw(p, intspec + index * intsize, intsize, + addr, out_irq); + if ( res ) + goto out; +out: + return res; +} + +int dt_irq_translate(const struct dt_raw_irq *raw, + struct dt_irq *out_irq) +{ + ASSERT(dt_irq_xlate != NULL); + + /* TODO: Retrieve the right irq_xlate. This is only work for the gic */ + + return dt_irq_xlate(raw->specifier, raw->size, + &out_irq->irq, &out_irq->type); +} + +int dt_device_get_irq(const struct dt_device_node *device, int index, + struct dt_irq *out_irq) +{ + struct dt_raw_irq raw; + int res; + + res = dt_device_get_raw_irq(device, index, &raw); + + if ( res ) + return res; + + return dt_irq_translate(&raw, out_irq); +} + +/** * unflatten_dt_node - Alloc and populate a device_node from the flat tree * @fdt: The parent device tree blob * @mem: Memory chunk to use for allocating device nodes and properties @@ -1409,6 +1755,22 @@ static void __init dt_alias_scan(void) } } +struct dt_device_node * __init dt_find_interrupt_controller(const char *compat) +{ + struct dt_device_node *np = NULL; + + while ( (np = dt_find_compatible_node(np, NULL, compat)) ) + { + if ( !dt_find_property(np, "interrupt-controller", NULL) ) + continue; + + if ( dt_get_parent(np) ) + break; + } + + return np; +} + void __init dt_unflatten_host_device_tree(void) { __unflatten_device_tree(device_tree_flattened, &dt_host); diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h index cc3a070..a7cfa94 100644 --- a/xen/include/xen/device_tree.h +++ b/xen/include/xen/device_tree.h @@ -99,6 +99,67 @@ struct dt_device_node { }; +/** + * IRQ line type. + * + * DT_IRQ_TYPE_NONE - default, unspecified type + * DT_IRQ_TYPE_EDGE_RISING - rising edge triggered + * DT_IRQ_TYPE_EDGE_FALLING - falling edge triggered + * DT_IRQ_TYPE_EDGE_BOTH - rising and falling edge triggered + * DT_IRQ_TYPE_LEVEL_HIGH - high level triggered + * DT_IRQ_TYPE_LEVEL_LOW - low level triggered + * DT_IRQ_TYPE_LEVEL_MASK - Mask to filter out the level bits + * DT_IRQ_TYPE_SENSE_MASK - Mask for all the above bits + */ +#define DT_IRQ_TYPE_NONE 0x00000000 +#define DT_IRQ_TYPE_EDGE_RISING 0x00000001 +#define DT_IRQ_TYPE_EDGE_FALLING 0x00000002 +#define DT_IRQ_TYPE_EDGE_BOTH \ + (DT_IRQ_TYPE_EDGE_FALLING | DT_IRQ_TYPE_EDGE_RISING) +#define DT_IRQ_TYPE_LEVEL_HIGH 0x00000004 +#define DT_IRQ_TYPE_LEVEL_LOW 0x00000008 +#define DT_IRQ_TYPE_LEVEL_MASK \ + (DT_IRQ_TYPE_LEVEL_LOW | DT_IRQ_TYPE_LEVEL_HIGH) +#define DT_IRQ_TYPE_SENSE_MASK 0x0000000f + +/** + * dt_irq - describe an IRQ in the device tree + * @irq: IRQ number + * @type: IRQ type (see DT_IRQ_TYPE_*) + * + * This structure is returned when an interrupt is mapped. + */ +struct dt_irq { + unsigned int irq; + unsigned int type; +}; + +/* If type == DT_IRQ_TYPE_NONE, assume we use level triggered */ +static inline bool_t dt_irq_is_level_triggered(const struct dt_irq *irq) +{ + unsigned int type = irq->type; + + return (type & DT_IRQ_TYPE_LEVEL_MASK) || (type == DT_IRQ_TYPE_NONE); +} + +/** + * dt_raw_irq - container for device_node/irq_specifier for an irq controller + * @controller: pointer to interrupt controller deivce tree node + * @size: size of interrupt specifier + * @specifier: array of cells @size long specifying the specific interrupt + * + * This structure is returned when an interrupt is mapped but not translated. + */ +#define DT_MAX_IRQ_SPEC 4 /* We handle specifiers of at most 4 cells */ +struct dt_raw_irq { + const struct dt_device_node *controller; + u32 size; + u32 specifier[DT_MAX_IRQ_SPEC]; +}; + +#define dt_irq(irq) ((irq)->irq) +#define dt_irq_flags(irq) ((irq)->flags) + typedef int (*device_tree_node_func)(const void *fdt, int node, const char *name, int depth, u32 address_cells, u32 size_cells, @@ -136,11 +197,40 @@ void __init device_tree_dump(const void *fdt); void __init dt_unflatten_host_device_tree(void); /** + * IRQ translation callback + * TODO: For the moment we assume that we only have ONE + * interrupt-controller. + */ +typedef int (*dt_irq_xlate_func)(const u32 *intspec, unsigned int intsize, + unsigned int *out_hwirq, + unsigned int *out_type); +extern dt_irq_xlate_func dt_irq_xlate; + +/** * Host device tree * DO NOT modify it! */ extern struct dt_device_node *dt_host; +/** + * Primary interrupt controller + * Exynos SOC has an interrupt combiner, interrupt has no physical + * meaning when it''s not connected to the primary controller. + * We will only map interrupt whose parent controller is + * dt_interrupt_controller. It should always be a GIC. + * TODO: Handle multiple GIC + */ +extern const struct dt_device_node *dt_interrupt_controller; + +/** + * Find the interrupt controller + * For the moment we handle only one interrupt controller: the first + * one without parent which is compatible with the string "compat". + * + * If found, return the interrupt controller device node. + */ +struct dt_device_node * __init dt_find_interrupt_controller(const char *compat); + #define dt_node_cmp(s1, s2) strcmp((s1), (s2)) #define dt_compat_cmp(s1, s2, l) strnicmp((s1), (s2), l) @@ -285,6 +375,15 @@ int dt_device_get_address(const struct dt_device_node *dev, int index, u64 *addr, u64 *size); /** + * dt_number_of_irq - Get the number of IRQ for a device + * @device: the device whose number of interrupt is to be retrieved + * + * Return the number of irq for this device or 0 if there is no + * interrupt or an error occurred. + */ +unsigned int dt_number_of_irq(const struct dt_device_node *device); + +/** * dt_number_of_address - Get the number of addresses for a device * @device: the device whose number of address is to be retrieved * @@ -294,6 +393,37 @@ int dt_device_get_address(const struct dt_device_node *dev, int index, unsigned int dt_number_of_address(const struct dt_device_node *device); /** + * dt_device_get_irq - Resolve an interrupt for a device + * @device: the device whose interrupt is to be resolved + * @index: index of the interrupt to resolve + * @out_irq: structure dt_irq filled by this function + * + * This function resolves an interrupt, walking the tree, for a given + * device-tree node. It''s the high level pendant to dt_device_get_raw_irq(). + */ +int dt_device_get_irq(const struct dt_device_node *device, int index, + struct dt_irq *irq); + +/** + * dt_device_get_raw_irq - Resolve an interrupt for a device without translation + * @device: the device whose interrupt is to be resolved + * @index: index of the interrupt to resolve + * @out_irq: structure dt_raw_irq filled by this function + * + * This function resolves an interrupt for a device, no translation is + * made. dt_irq_translate can be called after. + */ +int dt_device_get_raw_irq(const struct dt_device_node *device, int index, + struct dt_raw_irq *irq); + +/** + * dt_irq_translate - Translate an irq + * @raw: IRQ to translate (raw format) + * @out_irq: structure dt_irq filled by this function + */ +int dt_irq_translate(const struct dt_raw_irq *raw, struct dt_irq *out_irq); + +/** * dt_n_size_cells - Helper to retrieve the number of cell for the size * @np: node to get the value * -- 1.7.10.4
This function routes an IRQ to a specific cpu. The IRQ is retrieved via the device tree. Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> Changes in v3: - Rename dt_irq_is_level_trigger to dt_irq_is_level_triggered Changes in v2: - Use dt_irq_is_level_trigger instead of the old name irq_is_level_trigger --- xen/arch/arm/gic.c | 11 +++++++++++ xen/include/asm-arm/gic.h | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index eeb526e..06ba78c 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -238,6 +238,17 @@ static int gic_route_irq(unsigned int irq, bool_t level, return 0; } +/* Program the GIC to route an interrupt with a dt_irq */ +void gic_route_dt_irq(const struct dt_irq *irq, unsigned int cpu_mask, + unsigned int priority) +{ + bool_t level; + + level = dt_irq_is_level_triggered(irq); + + gic_route_irq(irq->irq, level, cpu_mask, priority); +} + static void __init gic_dist_init(void) { uint32_t type; diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h index 3efb2b5..78dd21a 100644 --- a/xen/include/asm-arm/gic.h +++ b/xen/include/asm-arm/gic.h @@ -133,6 +133,8 @@ #define VGIC_IRQ_EVTCHN_CALLBACK 31 #ifndef __ASSEMBLY__ +#include <xen/device_tree.h> + extern int domain_vgic_init(struct domain *d); extern void domain_vgic_free(struct domain *d); @@ -142,6 +144,9 @@ extern void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int irq,int virtual); extern void vgic_clear_pending_irqs(struct vcpu *v); extern struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq); +/* Program the GIC to route an interrupt with a dt_irq */ +extern void gic_route_dt_irq(const struct dt_irq *irq, unsigned int cpu_mask, + unsigned int priority); extern void gic_route_ppis(void); extern void gic_route_spis(void); -- 1.7.10.4
This function translates an interrupt specifier to an IRQ number and IRQ type (ie: level trigger, edge trigger,...). It''s GIC specific. Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> Changes in v2: - Use DT_IRQ_TYPE_SENSE_MASK instead of IRQ_TYPE_SENSE_MASK --- xen/arch/arm/gic.c | 20 ++++++++++++++++++++ xen/arch/arm/setup.c | 1 + xen/include/asm-arm/gic.h | 4 ++++ 3 files changed, 25 insertions(+) diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index 06ba78c..6f57d66 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -329,6 +329,26 @@ static void __cpuinit gic_hyp_disable(void) GICH[GICH_HCR] = 0; } +int gic_irq_xlate(const u32 *intspec, unsigned int intsize, + unsigned int *out_hwirq, + unsigned int *out_type) +{ + if ( intsize < 3 ) + return -EINVAL; + + /* Get the interrupt number and add 16 to skip over SGIs */ + *out_hwirq = intspec[1] + 16; + + /* For SPIs, we need to add 16 more to get the GIC irq ID number */ + if ( !intspec[0] ) + *out_hwirq += 16; + + if ( out_type ) + *out_type = intspec[2] & DT_IRQ_TYPE_SENSE_MASK; + + return 0; +} + /* Set up the GIC */ void __init gic_init(void) { diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index 81bc956..fc53117 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -431,6 +431,7 @@ void __init start_xen(unsigned long boot_phys_offset, vm_init(); dt_unflatten_host_device_tree(); + dt_irq_xlate = gic_irq_xlate; #ifdef EARLY_UART_ADDRESS /* TODO Need to get device tree or command line for UART address */ diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h index 78dd21a..6ff217c 100644 --- a/xen/include/asm-arm/gic.h +++ b/xen/include/asm-arm/gic.h @@ -189,6 +189,10 @@ extern void send_SGI_allbutself(enum gic_sgi sgi); /* print useful debug info */ extern void gic_dump_info(struct vcpu *v); +/* IRQ translation function for the device tree */ +int gic_irq_xlate(const u32 *intspec, unsigned int intsize, + unsigned int *out_hwirq, unsigned int *out_type); + #endif /* __ASSEMBLY__ */ #endif -- 1.7.10.4
This function will replace setup_irq in later patch. It takes a dt_irq as first argument instead of an unsigned int. Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> --- xen/arch/arm/gic.c | 5 +++++ xen/include/asm-arm/irq.h | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index 6f57d66..cd3144e 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -521,6 +521,11 @@ static int __setup_irq(struct irq_desc *desc, unsigned int irq, return 0; } +int __init setup_dt_irq(const struct dt_irq *irq, struct irqaction *new) +{ + return setup_irq(irq->irq, new); +} + int __init setup_irq(unsigned int irq, struct irqaction *new) { int rc; diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h index eeb733a..de3aa22 100644 --- a/xen/include/asm-arm/irq.h +++ b/xen/include/asm-arm/irq.h @@ -2,6 +2,7 @@ #define _ASM_HW_IRQ_H #include <xen/config.h> +#include <xen/device_tree.h> #define NR_VECTORS 256 /* XXX */ @@ -26,6 +27,7 @@ struct irq_cfg { #define nr_static_irqs NR_IRQS struct irq_desc; +struct irqaction; struct irq_desc *__irq_to_desc(int irq); @@ -38,6 +40,8 @@ void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq); void init_IRQ(void); void init_secondary_IRQ(void); +int __init setup_dt_irq(const struct dt_irq *irq, struct irqaction *new); + #endif /* _ASM_HW_IRQ_H */ /* * Local variables: -- 1.7.10.4
This function will replace request_irq in a later patch. It takes a dt_irq as first argument instead of an unsigned int. Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> --- xen/arch/arm/irq.c | 7 +++++++ xen/include/asm-arm/irq.h | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/xen/arch/arm/irq.c b/xen/arch/arm/irq.c index b2e486f..e83ad22 100644 --- a/xen/arch/arm/irq.c +++ b/xen/arch/arm/irq.c @@ -93,6 +93,13 @@ void __cpuinit init_secondary_IRQ(void) BUG_ON(init_local_irq_data() < 0); } +int __init request_dt_irq(const struct dt_irq *irq, + void (*handler)(int, void *, struct cpu_user_regs *), + unsigned long irqflags, const char *devname, void *dev_id) +{ + return request_irq(irq->irq, handler, irqflags, devname, dev_id); +} + int __init request_irq(unsigned int irq, void (*handler)(int, void *, struct cpu_user_regs *), unsigned long irqflags, const char * devname, void *dev_id) diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h index de3aa22..80ff68d 100644 --- a/xen/include/asm-arm/irq.h +++ b/xen/include/asm-arm/irq.h @@ -40,6 +40,10 @@ void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq); void init_IRQ(void); void init_secondary_IRQ(void); +int __init request_dt_irq(const struct dt_irq *irq, + void (*handler)(int, void *, struct cpu_user_regs *), + unsigned long irqflags, const char *devname, + void *dev_id); int __init setup_dt_irq(const struct dt_irq *irq, struct irqaction *new); #endif /* _ASM_HW_IRQ_H */ -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 22/41] xen/arm: Use hierarchical device tree to retrieve GIC information
- Remove early parsing for GIC addresses - Remove hard coded maintenance IRQ number Signed-off-by: Julien Grall <julien.grall@linaro.org> Changes in v3: - Use DOMID_XEN instead of DT_USED_BY_XEN - Re-add page-align check on each GIC base addresses Changes in v2: - Use the new function request_dt_irq --- xen/arch/arm/gic.c | 63 +++++++++++++++++++++++++++++------------ xen/common/device_tree.c | 42 --------------------------- xen/include/xen/device_tree.h | 8 ------ 3 files changed, 45 insertions(+), 68 deletions(-) diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index cd3144e..d016845 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -45,6 +45,7 @@ static struct { paddr_t hbase; /* Address of virtual interface registers */ paddr_t vbase; /* Address of virtual cpu interface registers */ unsigned int lines; /* Number of interrupts (SPIs + PPIs + SGIs) */ + struct dt_irq maintenance; /* IRQ maintenance */ unsigned int cpus; spinlock_t lock; } gic; @@ -352,28 +353,53 @@ int gic_irq_xlate(const u32 *intspec, unsigned int intsize, /* Set up the GIC */ void __init gic_init(void) { + struct dt_device_node *node; + int res; + + node = dt_find_interrupt_controller("arm,cortex-a15-gic"); + if ( !node ) + panic("Unable to find compatible GIC in the device tree\n"); + + dt_device_set_used_by(node, DOMID_XEN); + + res = dt_device_get_address(node, 0, &gic.dbase, NULL); + if ( res || !gic.dbase || (gic.dbase & ~PAGE_MASK) ) + panic("GIC: Cannot find a valid address for the distributor\n"); + + res = dt_device_get_address(node, 1, &gic.cbase, NULL); + if ( res || !gic.cbase || (gic.cbase & ~PAGE_MASK) ) + panic("GIC: Cannot find a valid address for the CPU\n"); + + res = dt_device_get_address(node, 2, &gic.hbase, NULL); + if ( res || !gic.hbase || (gic.hbase & ~PAGE_MASK) ) + panic("GIC: Cannot find a valid address for the hypervisor\n"); + + res = dt_device_get_address(node, 3, &gic.vbase, NULL); + if ( res || !gic.vbase || (gic.vbase & ~PAGE_MASK) ) + panic("GIC: Cannot find a valid address for the virtual CPU\n"); + + res = dt_device_get_irq(node, 0, &gic.maintenance); + if ( res ) + panic("GIC: Cannot find the maintenance IRQ\n"); + + /* Set the GIC as the primary interrupt controller */ + dt_interrupt_controller = node; + + /* TODO: Add check on distributor, cpu size */ + printk("GIC initialization:\n" " gic_dist_addr=%"PRIpaddr"\n" " gic_cpu_addr=%"PRIpaddr"\n" " gic_hyp_addr=%"PRIpaddr"\n" - " gic_vcpu_addr=%"PRIpaddr"\n", - early_info.gic.gic_dist_addr, early_info.gic.gic_cpu_addr, - early_info.gic.gic_hyp_addr, early_info.gic.gic_vcpu_addr); - if ( !early_info.gic.gic_dist_addr || - !early_info.gic.gic_cpu_addr || - !early_info.gic.gic_hyp_addr || - !early_info.gic.gic_vcpu_addr ) - panic("the physical address of one of the GIC interfaces is missing\n"); - if ( (early_info.gic.gic_dist_addr & ~PAGE_MASK) || - (early_info.gic.gic_cpu_addr & ~PAGE_MASK) || - (early_info.gic.gic_hyp_addr & ~PAGE_MASK) || - (early_info.gic.gic_vcpu_addr & ~PAGE_MASK) ) + " gic_vcpu_addr=%"PRIpaddr"\n" + " gic_maintenance_irq=%u\n", + gic.dbase, gic.cbase, gic.hbase, gic.vbase, + gic.maintenance.irq); + + if ( (gic.dbase & ~PAGE_MASK) || (gic.cbase & ~PAGE_MASK) || + (gic.hbase & ~PAGE_MASK) || (gic.vbase & ~PAGE_MASK) ) panic("GIC interfaces not page aligned.\n"); - gic.dbase = early_info.gic.gic_dist_addr; - gic.cbase = early_info.gic.gic_cpu_addr; - gic.hbase = early_info.gic.gic_hyp_addr; - gic.vbase = early_info.gic.gic_vcpu_addr; set_fixmap(FIXMAP_GICD, gic.dbase >> PAGE_SHIFT, DEV_SHARED); BUILD_BUG_ON(FIXMAP_ADDR(FIXMAP_GICC1) ! FIXMAP_ADDR(FIXMAP_GICC2)-PAGE_SIZE); @@ -464,7 +490,7 @@ void gic_route_ppis(void) { /* XXX should get these from DT */ /* GIC maintenance */ - gic_route_irq(25, 1, 1u << smp_processor_id(), 0xa0); + gic_route_dt_irq(&gic.maintenance, 1u << smp_processor_id(), 0xa0); /* Hypervisor Timer */ gic_route_irq(26, 1, 1u << smp_processor_id(), 0xa0); /* Virtual Timer */ @@ -851,7 +877,8 @@ void gic_dump_info(struct vcpu *v) void __cpuinit init_maintenance_interrupt(void) { - request_irq(25, maintenance_interrupt, 0, "irq-maintenance", NULL); + request_dt_irq(&gic.maintenance, maintenance_interrupt, + 0, "irq-maintenance", NULL); } /* diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c index f0f60ce..094cd60 100644 --- a/xen/common/device_tree.c +++ b/xen/common/device_tree.c @@ -442,46 +442,6 @@ static void __init process_cpu_node(const void *fdt, int node, cpumask_set_cpu(start, &cpu_possible_map); } -static void __init process_gic_node(const void *fdt, int node, - const char *name, - u32 address_cells, u32 size_cells) -{ - const struct fdt_property *prop; - const u32 *cell; - paddr_t start, size; - int interfaces; - - if ( address_cells < 1 || size_cells < 1 ) - { - early_printk("fdt: node `%s'': invalid #address-cells or #size-cells", - name); - return; - } - - prop = fdt_get_property(fdt, node, "reg", NULL); - if ( !prop ) - { - early_printk("fdt: node `%s'': missing `reg'' property\n", name); - return; - } - - cell = (const u32 *)prop->data; - interfaces = device_tree_nr_reg_ranges(prop, address_cells, size_cells); - if ( interfaces < 4 ) - { - early_printk("fdt: node `%s'': not enough ranges\n", name); - return; - } - device_tree_get_reg(&cell, address_cells, size_cells, &start, &size); - early_info.gic.gic_dist_addr = start; - device_tree_get_reg(&cell, address_cells, size_cells, &start, &size); - early_info.gic.gic_cpu_addr = start; - device_tree_get_reg(&cell, address_cells, size_cells, &start, &size); - early_info.gic.gic_hyp_addr = start; - device_tree_get_reg(&cell, address_cells, size_cells, &start, &size); - early_info.gic.gic_vcpu_addr = start; -} - static void __init process_multiboot_node(const void *fdt, int node, const char *name, u32 address_cells, u32 size_cells) @@ -533,8 +493,6 @@ static int __init early_scan_node(const void *fdt, process_memory_node(fdt, node, name, address_cells, size_cells); else if ( device_tree_type_matches(fdt, node, "cpu") ) process_cpu_node(fdt, node, name, address_cells, size_cells); - else if ( device_tree_node_compatible(fdt, node, "arm,cortex-a15-gic") ) - process_gic_node(fdt, node, name, address_cells, size_cells); else if ( device_tree_node_compatible(fdt, node, "xen,multiboot-module" ) ) process_multiboot_node(fdt, node, name, address_cells, size_cells); diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h index a7cfa94..5a2a5c6 100644 --- a/xen/include/xen/device_tree.h +++ b/xen/include/xen/device_tree.h @@ -31,13 +31,6 @@ struct dt_mem_info { struct membank bank[NR_MEM_BANKS]; }; -struct dt_gic_info { - paddr_t gic_dist_addr; - paddr_t gic_cpu_addr; - paddr_t gic_hyp_addr; - paddr_t gic_vcpu_addr; -}; - struct dt_mb_module { paddr_t start; paddr_t size; @@ -52,7 +45,6 @@ struct dt_module_info { struct dt_early_info { struct dt_mem_info mem; - struct dt_gic_info gic; struct dt_module_info modules; }; -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 23/41] xen/arm: Retrieve timer interrupts from the device tree
Signed-off-by: Julien Grall <julien.grall@linaro.org> Changes in v3: - Move enum ppi_nr in asm-arm/time.h and rename it to timer_ppi (will be used in vtimer patch) - Prefix all ppi_nr by TIMER_ - Remove spurious 2 when physical IRQ is requested Changes in v2: - Hardcode ppi_nr value - Use the new function request_dt_irq --- xen/arch/arm/gic.c | 9 ++------ xen/arch/arm/time.c | 53 +++++++++++++++++++++++++++++++++++++++----- xen/include/asm-arm/time.h | 12 ++++++++++ 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index d016845..1197d53 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -488,15 +488,10 @@ void gic_disable_cpu(void) void gic_route_ppis(void) { - /* XXX should get these from DT */ /* GIC maintenance */ gic_route_dt_irq(&gic.maintenance, 1u << smp_processor_id(), 0xa0); - /* Hypervisor Timer */ - gic_route_irq(26, 1, 1u << smp_processor_id(), 0xa0); - /* Virtual Timer */ - gic_route_irq(27, 1, 1u << smp_processor_id(), 0xa0); - /* Physical Timer */ - gic_route_irq(30, 1, 1u << smp_processor_id(), 0xa0); + /* Route timer interrupt */ + route_timer_interrupt(); } void gic_route_spis(void) diff --git a/xen/arch/arm/time.c b/xen/arch/arm/time.c index 82f69d2..ecb7626 100644 --- a/xen/arch/arm/time.c +++ b/xen/arch/arm/time.c @@ -19,6 +19,7 @@ #include <xen/config.h> #include <xen/console.h> +#include <xen/device_tree.h> #include <xen/init.h> #include <xen/irq.h> #include <xen/lib.h> @@ -46,6 +47,8 @@ uint64_t __read_mostly boot_count; * register-mapped time source in the SoC. */ unsigned long __read_mostly cpu_khz; /* CPU clock frequency in kHz. */ +static struct dt_irq timer_irq[MAX_TIMER_PPI]; + /*static inline*/ s_time_t ticks_to_ns(uint64_t ticks) { return muldiv64(ticks, SECONDS(1), 1000 * cpu_khz); @@ -90,6 +93,29 @@ static uint32_t calibrate_timer(void) /* Set up the timer on the boot CPU */ int __init init_xen_time(void) { + struct dt_device_node *dev; + int res; + unsigned int i; + + dev = dt_find_compatible_node(NULL, NULL, "arm,armv7-timer"); + if ( !dev ) + panic("Unable to find a compatible timer in the device tree\n"); + + dt_device_set_used_by(dev, DOMID_XEN); + + /* Retrieve all IRQs for the timer */ + for ( i = TIMER_PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++ ) + { + res = dt_device_get_irq(dev, i, &timer_irq[i]); + if ( res ) + panic("Timer: Unable to retrieve IRQ %u from the device tree\n", i); + } + + printk("Generic Timer IRQ: phys=%u hyp=%u virt=%u\n", + timer_irq[TIMER_PHYS_NONSECURE_PPI].irq, + timer_irq[TIMER_HYP_PPI].irq, + timer_irq[TIMER_VIRT_PPI].irq); + /* Check that this CPU supports the Generic Timer interface */ if ( !cpu_has_gentimer ) panic("CPU does not support the Generic Timer v1 interface.\n"); @@ -143,7 +169,8 @@ int reprogram_timer(s_time_t timeout) /* Handle the firing timer */ static void timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs) { - if ( irq == 26 && READ_SYSREG32(CNTHP_CTL_EL2) & CNTx_CTL_PENDING ) + if ( irq == (timer_irq[TIMER_HYP_PPI].irq) && + READ_SYSREG32(CNTHP_CTL_EL2) & CNTx_CTL_PENDING ) { /* Signal the generic timer code to do its work */ raise_softirq(TIMER_SOFTIRQ); @@ -151,7 +178,8 @@ static void timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs) WRITE_SYSREG32(0, CNTHP_CTL_EL2); } - if (irq == 30 && READ_SYSREG32(CNTP_CTL_EL0) & CNTx_CTL_PENDING ) + if ( irq == (timer_irq[TIMER_PHYS_NONSECURE_PPI].irq) && + READ_SYSREG32(CNTP_CTL_EL0) & CNTx_CTL_PENDING ) { /* Signal the generic timer code to do its work */ raise_softirq(TIMER_SOFTIRQ); @@ -167,6 +195,17 @@ static void vtimer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs) vgic_vcpu_inject_irq(current, irq, 1); } +/* Route timer''s IRQ on this CPU */ +void __cpuinit route_timer_interrupt(void) +{ + gic_route_dt_irq(&timer_irq[TIMER_PHYS_NONSECURE_PPI], + 1u << smp_processor_id(), 0xa0); + gic_route_dt_irq(&timer_irq[TIMER_HYP_PPI], + 1u << smp_processor_id(), 0xa0); + gic_route_dt_irq(&timer_irq[TIMER_VIRT_PPI], + 1u << smp_processor_id(), 0xa0); +} + /* Set up the timer interrupt on this CPU */ void __cpuinit init_timer_interrupt(void) { @@ -184,10 +223,12 @@ void __cpuinit init_timer_interrupt(void) WRITE_SYSREG32(0, CNTHP_CTL_EL2); /* Hypervisor''s timer disabled */ isb(); - /* XXX Need to find this IRQ number from devicetree? */ - request_irq(26, timer_interrupt, 0, "hyptimer", NULL); - request_irq(27, vtimer_interrupt, 0, "virtimer", NULL); - request_irq(30, timer_interrupt, 0, "phytimer", NULL); + request_dt_irq(&timer_irq[TIMER_HYP_PPI], timer_interrupt, 0, + "hyptimer", NULL); + request_dt_irq(&timer_irq[TIMER_VIRT_PPI], vtimer_interrupt, 0, + "virtimer", NULL); + request_dt_irq(&timer_irq[TIMER_PHYS_NONSECURE_PPI], timer_interrupt, 0, + "phytimer", NULL); } /* Wait a set number of microseconds */ diff --git a/xen/include/asm-arm/time.h b/xen/include/asm-arm/time.h index c16bf08..05833ce 100644 --- a/xen/include/asm-arm/time.h +++ b/xen/include/asm-arm/time.h @@ -11,6 +11,18 @@ static inline cycles_t get_cycles (void) struct tm; struct tm wallclock_time(void); +/* List of timer''s IRQ */ +enum timer_ppi +{ + TIMER_PHYS_SECURE_PPI = 0, + TIMER_PHYS_NONSECURE_PPI = 1, + TIMER_VIRT_PPI = 2, + TIMER_HYP_PPI = 3, + MAX_TIMER_PPI = 4, +}; + +/* Route timer''s IRQ on this CPU */ +extern void __cpuinit route_timer_interrupt(void); /* Set up the timer interrupt on this CPU */ extern void __cpuinit init_timer_interrupt(void); -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 24/41] xen/arm: Don''t hardcode VGIC informations
- Define VGIC base address per domain. For the moment the base addresses to dom0 base addresses. - The number of interrupt lines (ie number of SPIs) is equal to: * 0 for guests * number of host SPIs for dom0 Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> Changes in v2: - Don''t use javadoc style --- xen/arch/arm/gic.c | 21 ++++++++++++++++++--- xen/arch/arm/vgic.c | 20 ++++++++++++++------ xen/include/asm-arm/domain.h | 5 ++++- xen/include/asm-arm/gic.h | 3 +++ 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index 1197d53..873c855 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -56,6 +56,11 @@ static DEFINE_PER_CPU(uint64_t, lr_mask); static unsigned nr_lrs; +unsigned int gic_number_lines(void) +{ + return gic.lines; +} + irq_desc_t *__irq_to_desc(int irq) { if (irq < NR_LOCAL_IRQS) return &this_cpu(local_irq_desc)[irq]; @@ -775,11 +780,21 @@ void gic_interrupt(struct cpu_user_regs *regs, int is_fiq) int gicv_setup(struct domain *d) { + /* TODO: Retrieve distributor and CPU guest base address from the + * guest DTS + * For the moment we use dom0 DTS + */ + d->arch.vgic.dbase = gic.dbase; + d->arch.vgic.cbase = gic.cbase; + + + d->arch.vgic.nr_lines = 0; + /* map the gic virtual cpu interface in the gic cpu interface region of * the guest */ - return map_mmio_regions(d, gic.cbase, - gic.cbase + (2 * PAGE_SIZE) - 1, - gic.vbase); + return map_mmio_regions(d, d->arch.vgic.cbase, + d->arch.vgic.cbase + (2 * PAGE_SIZE) - 1, + gic.vbase); } static void gic_irq_eoi(void *info) diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c index f9c1a6b..7eaccb7 100644 --- a/xen/arch/arm/vgic.c +++ b/xen/arch/arm/vgic.c @@ -30,8 +30,6 @@ #include "io.h" #include <asm/gic.h> -#define VGIC_DISTR_BASE_ADDRESS 0x000000002c001000 - #define REG(n) (n/4) /* Number of ranks of interrupt registers for a domain */ @@ -80,7 +78,15 @@ int domain_vgic_init(struct domain *d) int i; d->arch.vgic.ctlr = 0; - d->arch.vgic.nr_lines = 32; + + /* Currently nr_lines in vgic and gic doesn''t have the same meanings + * Here nr_lines = number of SPIs + */ + if ( d->domain_id == 0 ) + d->arch.vgic.nr_lines = gic_number_lines() - 32; + else + d->arch.vgic.nr_lines = 0; /* We don''t need SPIs for the guest */ + d->arch.vgic.shared_irqs xmalloc_array(struct vgic_irq_rank, DOMAIN_NR_RANKS(d)); d->arch.vgic.pending_irqs @@ -163,7 +169,7 @@ static int vgic_distr_mmio_read(struct vcpu *v, mmio_info_t *info) struct cpu_user_regs *regs = guest_cpu_user_regs(); register_t *r = select_user_reg(regs, dabt.reg); struct vgic_irq_rank *rank; - int offset = (int)(info->gpa - VGIC_DISTR_BASE_ADDRESS); + int offset = (int)(info->gpa - v->domain->arch.vgic.dbase); int gicd_reg = REG(offset); switch ( gicd_reg ) @@ -440,7 +446,7 @@ static int vgic_distr_mmio_write(struct vcpu *v, mmio_info_t *info) struct cpu_user_regs *regs = guest_cpu_user_regs(); register_t *r = select_user_reg(regs, dabt.reg); struct vgic_irq_rank *rank; - int offset = (int)(info->gpa - VGIC_DISTR_BASE_ADDRESS); + int offset = (int)(info->gpa - v->domain->arch.vgic.dbase); int gicd_reg = REG(offset); uint32_t tr; @@ -620,7 +626,9 @@ write_ignore: static int vgic_distr_mmio_check(struct vcpu *v, paddr_t addr) { - return addr >= VGIC_DISTR_BASE_ADDRESS && addr < (VGIC_DISTR_BASE_ADDRESS+PAGE_SIZE); + struct domain *d = v->domain; + + return (addr >= (d->arch.vgic.dbase)) && (addr < (d->arch.vgic.dbase + PAGE_SIZE)); } const struct mmio_handler vgic_distr_mmio_handler = { diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h index cca7416..cb251cc 100644 --- a/xen/include/asm-arm/domain.h +++ b/xen/include/asm-arm/domain.h @@ -88,13 +88,16 @@ struct arch_domain */ spinlock_t lock; int ctlr; - int nr_lines; + int nr_lines; /* Number of SPIs */ struct vgic_irq_rank *shared_irqs; /* * SPIs are domain global, SGIs and PPIs are per-VCPU and stored in * struct arch_vcpu. */ struct pending_irq *pending_irqs; + /* Base address for guest GIC */ + paddr_t dbase; /* Distributor base address */ + paddr_t cbase; /* CPU base address */ } vgic; struct vpl011 { diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h index 6ff217c..e7608dc 100644 --- a/xen/include/asm-arm/gic.h +++ b/xen/include/asm-arm/gic.h @@ -189,6 +189,9 @@ extern void send_SGI_allbutself(enum gic_sgi sgi); /* print useful debug info */ extern void gic_dump_info(struct vcpu *v); +/* Number of interrupt lines */ +extern unsigned int gic_number_lines(void); + /* IRQ translation function for the device tree */ int gic_irq_xlate(const u32 *intspec, unsigned int intsize, unsigned int *out_hwirq, unsigned int *out_type); -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 25/41] xen/arm: Don''t hardcode virtual timer IRQs
Define virtual timer IRQs per VCPU Signed-off-by: Julien Grall <julien.grall@linaro.org> --- xen/arch/arm/time.c | 9 ++++++++- xen/arch/arm/vtimer.c | 13 ++++++++----- xen/include/asm-arm/time.h | 3 +++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/xen/arch/arm/time.c b/xen/arch/arm/time.c index ecb7626..2e928bc 100644 --- a/xen/arch/arm/time.c +++ b/xen/arch/arm/time.c @@ -49,6 +49,13 @@ unsigned long __read_mostly cpu_khz; /* CPU clock frequency in kHz. */ static struct dt_irq timer_irq[MAX_TIMER_PPI]; +const struct dt_irq *timer_dt_irq(enum timer_ppi ppi) +{ + ASSERT(ppi >= TIMER_PHYS_SECURE_PPI && ppi < MAX_TIMER_PPI); + + return &timer_irq[ppi]; +} + /*static inline*/ s_time_t ticks_to_ns(uint64_t ticks) { return muldiv64(ticks, SECONDS(1), 1000 * cpu_khz); @@ -192,7 +199,7 @@ static void vtimer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs) { current->arch.virt_timer.ctl = READ_SYSREG32(CNTV_CTL_EL0); WRITE_SYSREG32(current->arch.virt_timer.ctl | CNTx_CTL_MASK, CNTV_CTL_EL0); - vgic_vcpu_inject_irq(current, irq, 1); + vgic_vcpu_inject_irq(current, current->arch.virt_timer.irq, 1); } /* Route timer''s IRQ on this CPU */ diff --git a/xen/arch/arm/vtimer.c b/xen/arch/arm/vtimer.c index 6993425..aee762a 100644 --- a/xen/arch/arm/vtimer.c +++ b/xen/arch/arm/vtimer.c @@ -34,14 +34,14 @@ static void phys_timer_expired(void *data) struct vtimer *t = data; t->ctl |= CNTx_CTL_PENDING; if ( !(t->ctl & CNTx_CTL_MASK) ) - vgic_vcpu_inject_irq(t->v, 30, 1); + vgic_vcpu_inject_irq(t->v, t->irq, 1); } static void virt_timer_expired(void *data) { struct vtimer *t = data; t->ctl |= CNTx_CTL_MASK; - vgic_vcpu_inject_irq(t->v, 27, 1); + vgic_vcpu_inject_irq(t->v, t->irq, 1); } int vcpu_domain_init(struct domain *d) @@ -55,17 +55,20 @@ int vcpu_vtimer_init(struct vcpu *v) { struct vtimer *t = &v->arch.phys_timer; + /* TODO: Retrieve physical and virtual timer IRQ from the guest + * DT. For the moment we use dom0 DT + */ + init_timer(&t->timer, phys_timer_expired, t, v->processor); t->ctl = 0; t->cval = NOW(); - t->irq = 30; + t->irq = timer_dt_irq(TIMER_PHYS_NONSECURE_PPI)->irq; t->v = v; t = &v->arch.virt_timer; init_timer(&t->timer, virt_timer_expired, t, v->processor); t->ctl = 0; - t->cval = 0; - t->irq = 27; + t->irq = timer_dt_irq(TIMER_VIRT_PPI)->irq; t->v = v; return 0; diff --git a/xen/include/asm-arm/time.h b/xen/include/asm-arm/time.h index 05833ce..f7aa868 100644 --- a/xen/include/asm-arm/time.h +++ b/xen/include/asm-arm/time.h @@ -21,6 +21,9 @@ enum timer_ppi MAX_TIMER_PPI = 4, }; +/* Get one of the timer IRQ description */ +const struct dt_irq* timer_dt_irq(enum timer_ppi ppi); + /* Route timer''s IRQ on this CPU */ extern void __cpuinit route_timer_interrupt(void); -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 26/41] xen/arm: Introduce a generic way to use a device from the device tree
Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> Changes in v2: - Add missing ALIGN in xen.lds.S --- xen/arch/arm/Makefile | 1 + xen/arch/arm/device.c | 74 ++++++++++++++++++++++++++++++++++++++++++ xen/arch/arm/xen.lds.S | 7 ++++ xen/include/asm-arm/device.h | 52 +++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+) create mode 100644 xen/arch/arm/device.c create mode 100644 xen/include/asm-arm/device.h diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 8f75044..4955a21 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -28,6 +28,7 @@ obj-y += vgic.o obj-y += vtimer.o obj-y += vpl011.o obj-y += hvm.o +obj-y += device.o #obj-bin-y += ....o diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c new file mode 100644 index 0000000..dc751a9 --- /dev/null +++ b/xen/arch/arm/device.c @@ -0,0 +1,74 @@ +/* + * xen/arch/arm/device.c + * + * Helpers to use a device retrieved via the device tree. + * + * Julien Grall <julien.grall@linaro.org> + * Copyright (C) 2013 Linaro Limited. + * + * 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/device.h> +#include <xen/errno.h> +#include <xen/lib.h> + +extern const struct device_desc _sdevice[], _edevice[]; + +static bool_t __init device_is_compatible(const struct device_desc *desc, + const struct dt_device_node *dev) +{ + const char *const *compat; + + if ( !desc->compatible ) + return 0; + + for ( compat = desc->compatible; *compat; compat++ ) + { + if ( dt_device_is_compatible(dev, *compat) ) + return 1; + } + + return 0; +} + +int __init device_init(struct dt_device_node *dev, enum device_type type, + const void *data) +{ + const struct device_desc *desc; + + ASSERT(dev != NULL); + + for ( desc = _sdevice; desc != _edevice; desc++ ) + { + if ( desc->type != type ) + continue; + + if ( device_is_compatible(desc, dev) ) + { + ASSERT(desc->init != NULL); + + return desc->init(dev, data); + } + + } + + return -EBADF; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S index fd755d7..deab040 100644 --- a/xen/arch/arm/xen.lds.S +++ b/xen/arch/arm/xen.lds.S @@ -76,6 +76,13 @@ SECTIONS __lock_profile_end = .; #endif + . = ALIGN(8); + .dev.info : { + _sdevice = .; + *(.dev.info) + _edevice = .; + } :text + . = ALIGN(PAGE_SIZE); /* Init code and data */ __init_begin = .; .init.text : { diff --git a/xen/include/asm-arm/device.h b/xen/include/asm-arm/device.h new file mode 100644 index 0000000..9e47ca6 --- /dev/null +++ b/xen/include/asm-arm/device.h @@ -0,0 +1,52 @@ +#ifndef __ASM_ARM_DEVICE_H +#define __ASM_ARM_DEVICE_H + +#include <xen/init.h> +#include <xen/device_tree.h> + +enum device_type +{ + DEVICE_SERIAL +}; + +struct device_desc { + /* Device name */ + const char *name; + /* Device type */ + enum device_type type; + /* Array of device tree ''compatible'' strings */ + const char *const *compatible; + /* Device initialization */ + int (*init)(struct dt_device_node *dev, const void *data); +}; + +/** + * device_init - Initialize a device + * @dev: device to initialize + * @type: type of the device (serial, network...) + * @data: specific data for initializing the device + * + * Return 0 on success. + */ +int __init device_init(struct dt_device_node *dev, enum device_type type, + const void *data); + +#define DT_DEVICE_START(_name, _namestr, _type) \ +static const struct device_desc __dev_desc_##_name __used \ +__attribute__((__section__(".dev.info"))) = { \ + .name = _namestr, \ + .type = _type, \ + +#define DT_DEVICE_END \ +}; + +#endif /* __ASM_ARM_DEVICE_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 27/41] xen/arm: New callback in uart_driver to get device tree interrupt structure
The existing function serial_irq doesn''t allow to retrieve if the interrupt is edge or level trigger. Use this function to routes IRQs for all serial ports which Xen is using to Xen. Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> Changes in v2: - Improve commit message --- xen/arch/arm/gic.c | 12 ++++++++++++ xen/drivers/char/serial.c | 10 ++++++++++ xen/include/xen/serial.h | 5 +++++ 3 files changed, 27 insertions(+) diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index 873c855..95eb6c6 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -24,6 +24,7 @@ #include <xen/irq.h> #include <xen/sched.h> #include <xen/errno.h> +#include <xen/serial.h> #include <xen/softirq.h> #include <xen/list.h> #include <xen/device_tree.h> @@ -501,9 +502,20 @@ void gic_route_ppis(void) void gic_route_spis(void) { + int seridx; + const struct dt_irq *irq; + /* XXX should get these from DT */ /* UART */ gic_route_irq(37, 0, 1u << smp_processor_id(), 0xa0); + + for ( seridx = 0; seridx <= SERHND_IDX; seridx++ ) + { + if ( (irq = serial_dt_irq(seridx)) == NULL ) + continue; + + gic_route_dt_irq(irq, 1u << smp_processor_id(), 0xa0); + } } void __init release_irq(unsigned int irq) diff --git a/xen/drivers/char/serial.c b/xen/drivers/char/serial.c index a3d2b26..0ae7e4d 100644 --- a/xen/drivers/char/serial.c +++ b/xen/drivers/char/serial.c @@ -482,6 +482,16 @@ int __init serial_irq(int idx) return -1; } +const struct dt_irq __init *serial_dt_irq(int idx) +{ + if ( (idx >= 0) && (idx < ARRAY_SIZE(com)) && + com[idx].driver && com[idx].driver->dt_irq_get ) + return com[idx].driver->dt_irq_get(&com[idx]); + + return NULL; +} + + void serial_suspend(void) { int i; diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h index b932ed4..5de5171 100644 --- a/xen/include/xen/serial.h +++ b/xen/include/xen/serial.h @@ -71,6 +71,8 @@ struct uart_driver { int (*getc)(struct serial_port *, char *); /* Get IRQ number for this port''s serial line: returns -1 if none. */ int (*irq)(struct serial_port *); + /* Get IRQ device node for this port''s serial line: returns NULL if none. */ + const struct dt_irq *(*dt_irq_get)(struct serial_port *); }; /* ''Serial handles'' are composed from the following fields. */ @@ -120,6 +122,9 @@ void serial_end_log_everything(int handle); /* Return irq number for specified serial port (identified by index). */ int serial_irq(int idx); +/* Return irq device node for specified serial port (identified by index). */ +const struct dt_irq *serial_dt_irq(int idx); + /* Serial suspend/resume. */ void serial_suspend(void); void serial_resume(void); -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 28/41] xen/arm: Add generic UART to get the device in the device tree
This generic UART will find the right UART via xen command line with dtuart=myserial. "myserial" is the alias of the UART in the device tree. Xen will retrieve the information via the device tree and call the initialization function for this specific UART thanks to the device API. Signed-off-by: Julien Grall <julien.grall@linaro.org> CC: keir@xen.org Changes in v3: - Rename arm-uart.c (resp. arm_uart_init) to dt-uart.c (resp. dt_uart_init) - Remove serial_arm_defaults and replace by a string options - Let the specific UART driver to get its base address in the DT - Add SERHND_DTUART. The value is stolen to SERHND_COM1. Bump the value would needs some rework... Changes in v2: - Use dtuart parameter instead of com1. The first one is more arm while the latter is more x86 --- xen/arch/arm/setup.c | 3 +- xen/drivers/char/Makefile | 1 + xen/drivers/char/dt-uart.c | 69 ++++++++++++++++++++++++++++++++++++++++++ xen/drivers/char/serial.c | 6 ++++ xen/include/asm-arm/config.h | 2 +- xen/include/xen/serial.h | 5 ++- 6 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 xen/drivers/char/dt-uart.c diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index fc53117..2e331d3 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -436,8 +436,9 @@ void __init start_xen(unsigned long boot_phys_offset, #ifdef EARLY_UART_ADDRESS /* TODO Need to get device tree or command line for UART address */ pl011_init(0, FIXMAP_ADDR(FIXMAP_CONSOLE)); - console_init_preirq(); #endif + dt_uart_init(); + console_init_preirq(); system_state = SYS_STATE_boot; diff --git a/xen/drivers/char/Makefile b/xen/drivers/char/Makefile index ab2246d..9c067f9 100644 --- a/xen/drivers/char/Makefile +++ b/xen/drivers/char/Makefile @@ -2,4 +2,5 @@ obj-y += console.o obj-$(HAS_NS16550) += ns16550.o obj-$(HAS_PL011) += pl011.o obj-$(HAS_EHCI) += ehci-dbgp.o +obj-$(CONFIG_ARM) += dt-uart.o obj-y += serial.o diff --git a/xen/drivers/char/dt-uart.c b/xen/drivers/char/dt-uart.c new file mode 100644 index 0000000..93bb0f5 --- /dev/null +++ b/xen/drivers/char/dt-uart.c @@ -0,0 +1,69 @@ +/* + * xen/drivers/char/dt-uart.c + * + * Generic uart retrieved via the device tree + * + * Julien Grall <julien.grall@linaro.org> + * Copyright (c) 2013 Linaro Limited. + * + * 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/device.h> +#include <asm/early_printk.h> +#include <asm/types.h> +#include <xen/console.h> +#include <xen/device_tree.h> +#include <xen/serial.h> + +/* + * Configure UART port with a string: + * alias,options + * + * @alias: alias used in the device tree for the UART + * @options: UART speficic options (see in each UART driver) + */ +static char __initdata opt_dtuart[30] = ""; +string_param("dtuart", opt_dtuart); + +void __init dt_uart_init(void) +{ + struct dt_device_node *dev; + int ret; + const char *devalias = opt_dtuart; + char *options; + + if ( !console_has("dtuart") || !strcmp(opt_dtuart, "") ) + { + early_printk("No console\n"); + return; + } + + options = strchr(opt_dtuart, '',''); + if ( options != NULL ) + *(options++) = ''\0''; + else + options = ""; + + early_printk("Looking for UART console %s\n", devalias); + dev = dt_find_node_by_alias(devalias); + + if ( !dev ) + { + early_printk("Unable to find device \"%s\"\n", devalias); + return; + } + + ret = device_init(dev, DEVICE_SERIAL, options); + + if ( ret ) + early_printk("Unable to initialize serial: %d\n", ret); +} diff --git a/xen/drivers/char/serial.c b/xen/drivers/char/serial.c index 0ae7e4d..e1c3f47 100644 --- a/xen/drivers/char/serial.c +++ b/xen/drivers/char/serial.c @@ -271,6 +271,12 @@ int __init serial_parse_handle(char *conf) goto common; } + if ( !strncmp(conf, "dtuart", 5) ) + { + handle = SERHND_DTUART; + goto common; + } + if ( strncmp(conf, "com", 3) ) goto fail; diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h index 98a3a43..8ed72f5 100644 --- a/xen/include/asm-arm/config.h +++ b/xen/include/asm-arm/config.h @@ -39,7 +39,7 @@ #define CONFIG_VIDEO 1 -#define OPT_CONSOLE_STR "com1" +#define OPT_CONSOLE_STR "dtuart" #ifdef MAX_PHYS_CPUS #define NR_CPUS MAX_PHYS_CPUS diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h index 5de5171..8af3bc4 100644 --- a/xen/include/xen/serial.h +++ b/xen/include/xen/serial.h @@ -9,6 +9,7 @@ #ifndef __XEN_SERIAL_H__ #define __XEN_SERIAL_H__ +#include <xen/init.h> #include <xen/spinlock.h> struct cpu_user_regs; @@ -76,10 +77,11 @@ struct uart_driver { }; /* ''Serial handles'' are composed from the following fields. */ -#define SERHND_IDX (3<<0) /* COM1, COM2, or DBGP? */ +#define SERHND_IDX (3<<0) /* COM1, COM2, DBGP, DTUART? */ # define SERHND_COM1 (0<<0) # define SERHND_COM2 (1<<0) # define SERHND_DBGP (2<<0) +# define SERHND_DTUART (0<<0) /* Steal SERHND_COM1 value */ #define SERHND_HI (1<<2) /* Mux/demux each transferred char by MSB. */ #define SERHND_LO (1<<3) /* Ditto, except that the MSB is cleared. */ #define SERHND_COOKED (1<<4) /* Newline/carriage-return translation? */ @@ -156,6 +158,7 @@ void ns16550_init(int index, struct ns16550_defaults *defaults); void ehci_dbgp_init(void); void pl011_init(int index, unsigned long register_base_address); +void __init dt_uart_init(void); struct physdev_dbgp_op; int dbgp_op(const struct physdev_dbgp_op *); -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 29/41] xen/arm: Use device tree API in pl011 UART driver
Allow UART driver to retrieve all its information in the device tree. It''s possible to choose the pl011 driver via the Xen command line. Signed-off-by: Julien Grall <julien.grall@linaro.org> Changes in v3: - Remove the usage of serial_arm_defaults - Get the base address from the DT and map it with ioremap_attr - Allow only 1 PL011 assigned to SERHND_DTUART - Replace DT_USED_BY_XEN by DOMID_XEN Changes in v2: - Rework TODO - Use the new function setup_dt_irq --- xen/arch/arm/gic.c | 4 --- xen/arch/arm/setup.c | 4 --- xen/drivers/char/pl011.c | 84 ++++++++++++++++++++++++++++++++++++---------- xen/include/xen/serial.h | 1 - 4 files changed, 67 insertions(+), 26 deletions(-) diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index 95eb6c6..a46516a 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -505,10 +505,6 @@ void gic_route_spis(void) int seridx; const struct dt_irq *irq; - /* XXX should get these from DT */ - /* UART */ - gic_route_irq(37, 0, 1u << smp_processor_id(), 0xa0); - for ( seridx = 0; seridx <= SERHND_IDX; seridx++ ) { if ( (irq = serial_dt_irq(seridx)) == NULL ) diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index 2e331d3..e5f4459 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -433,10 +433,6 @@ void __init start_xen(unsigned long boot_phys_offset, dt_unflatten_host_device_tree(); dt_irq_xlate = gic_irq_xlate; -#ifdef EARLY_UART_ADDRESS - /* TODO Need to get device tree or command line for UART address */ - pl011_init(0, FIXMAP_ADDR(FIXMAP_CONSOLE)); -#endif dt_uart_init(); console_init_preirq(); diff --git a/xen/drivers/char/pl011.c b/xen/drivers/char/pl011.c index 8efd08e..9a4e3b9 100644 --- a/xen/drivers/char/pl011.c +++ b/xen/drivers/char/pl011.c @@ -22,9 +22,16 @@ #include <xen/serial.h> #include <xen/init.h> #include <xen/irq.h> +#include <asm/early_printk.h> +#include <xen/device_tree.h> +#include <xen/errno.h> +#include <asm/device.h> +#include <xen/mm.h> +#include <xen/vmap.h> static struct pl011 { - unsigned int baud, clock_hz, data_bits, parity, stop_bits, irq; + unsigned int baud, clock_hz, data_bits, parity, stop_bits; + struct dt_irq irq; volatile uint32_t *regs; /* UART with IRQ line: interrupt-driven I/O. */ struct irqaction irqaction; @@ -32,7 +39,7 @@ static struct pl011 { /* struct timer timer; */ /* unsigned int timeout_ms; */ /* bool_t probing, intr_works; */ -} pl011_com[2] = {{0}}; +} pl011_com = {0}; /* PL011 register addresses */ #define DR (0x00/4) @@ -163,13 +170,13 @@ static void __init pl011_init_postirq(struct serial_port *port) struct pl011 *uart = port->uart; int rc; - if ( uart->irq > 0 ) + if ( uart->irq.irq > 0 ) { uart->irqaction.handler = pl011_interrupt; uart->irqaction.name = "pl011"; uart->irqaction.dev_id = port; - if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 ) - printk("ERROR: Failed to allocate pl011 IRQ %d\n", uart->irq); + if ( (rc = setup_dt_irq(&uart->irq, &uart->irqaction)) != 0 ) + printk("ERROR: Failed to allocate pl011 IRQ %d\n", uart->irq.irq); } /* Clear pending error interrupts */ @@ -215,7 +222,14 @@ static int pl011_getc(struct serial_port *port, char *pc) static int __init pl011_irq(struct serial_port *port) { struct pl011 *uart = port->uart; - return ((uart->irq > 0) ? uart->irq : -1); + return ((uart->irq.irq > 0) ? uart->irq.irq : -1); +} + +static const struct dt_irq __init *pl011_dt_irq(struct serial_port *port) +{ + struct pl011 *uart = port->uart; + + return &uart->irq; } static struct uart_driver __read_mostly pl011_driver = { @@ -227,32 +241,68 @@ static struct uart_driver __read_mostly pl011_driver = { .tx_ready = pl011_tx_ready, .putc = pl011_putc, .getc = pl011_getc, - .irq = pl011_irq + .irq = pl011_irq, + .dt_irq_get = pl011_dt_irq, }; -/* TODO: Parse UART config from device-tree or command-line */ - -void __init pl011_init(int index, unsigned long register_base_address) +/* TODO: Parse UART config from the command line */ +static int __init pl011_uart_init(struct dt_device_node *dev, + const void *data) { struct pl011 *uart; + int res; + u64 addr, size; - if ( (index < 0) || (index > 1) ) - return; - - uart = &pl011_com[index]; + uart = &pl011_com; uart->clock_hz = 0x16e3600; uart->baud = 38400; uart->data_bits = 8; uart->parity = PARITY_NONE; uart->stop_bits = 1; - uart->irq = 37; /* TODO Need to find this from devicetree */ - uart->regs = (uint32_t *) register_base_address; + + res = dt_device_get_address(dev, 0, &addr, &size); + if ( res ) + { + early_printk("pl011: 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("pl011: Unable to map the UART memory\n"); + + return -ENOMEM; + } + + res = dt_device_get_irq(dev, 0, &uart->irq); + if ( res ) + { + early_printk("pl011: Unable to retrieve the IRQ\n"); + return res; + } /* Register with generic serial driver. */ - serial_register_uart(uart - pl011_com, &pl011_driver, uart); + serial_register_uart(SERHND_DTUART, &pl011_driver, uart); + + dt_device_set_used_by(dev, DOMID_XEN); + + return 0; } +static const char const *pl011_dt_compat[] __initdata +{ + "arm,pl011", + NULL +}; + +DT_DEVICE_START(pl011, "PL011 UART", DEVICE_SERIAL) + .compatible = pl011_dt_compat, + .init = pl011_uart_init, +DT_DEVICE_END + /* * Local variables: * mode: C diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h index 8af3bc4..9caf776 100644 --- a/xen/include/xen/serial.h +++ b/xen/include/xen/serial.h @@ -157,7 +157,6 @@ struct ns16550_defaults { void ns16550_init(int index, struct ns16550_defaults *defaults); void ehci_dbgp_init(void); -void pl011_init(int index, unsigned long register_base_address); void __init dt_uart_init(void); struct physdev_dbgp_op; -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 30/41] xen/arm: Use the device tree to map the address range and IRQ to dom0
- gic_route_irq_to_guest takes a dt_irq instead of an IRQ number - remove hardcoded address/IRQ Signed-off-by: Julien Grall <julien.grall@linaro.org> Changes in v3: - Rename dt_irq_is_level_trigger to dt_irq_is_level_trigerred - Rename map_device_from_device_tree to map_devices_from_device_tree - Replace all %# by 0x% in DPRINT - Use DOMID_XEN instead of DT_USED_BY_XEN - Get a rid of java doc style Changes in v2: - Use the new function dt_irq_is_level_trigger - Disable DEBUG_DT by default - Rename parse_device_tree to map_device_from_device_tree --- xen/arch/arm/domain_build.c | 146 +++++++++++++++++++++++++++++++++++++------ xen/arch/arm/gic.c | 12 ++-- xen/include/asm-arm/gic.h | 3 +- 3 files changed, 138 insertions(+), 23 deletions(-) diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c index 6581492..9a2ccad 100644 --- a/xen/arch/arm/domain_build.c +++ b/xen/arch/arm/domain_build.c @@ -14,6 +14,7 @@ #include <asm/setup.h> #include <asm/gic.h> +#include <xen/irq.h> #include "kernel.h" static unsigned int __initdata opt_dom0_max_vcpus; @@ -30,6 +31,14 @@ static void __init parse_dom0_mem(const char *s) } custom_param("dom0_mem", parse_dom0_mem); +//#define DEBUG_DT + +#ifdef DEBUG_DT +# define DPRINT(fmt, args...) printk(XENLOG_DEBUG fmt, ##args) +#else +# define DPRINT(fmt, args...) do {} while ( 0 ) +#endif + /* * Amount of extra space required to dom0''s device tree. No new nodes * are added (yet) but one terminating reserve map entry (16 bytes) is @@ -303,6 +312,124 @@ static int write_nodes(struct domain *d, struct kernel_info *kinfo, return 0; } +/* Map the device in the domain */ +static int map_device(struct domain *d, const struct dt_device_node *dev) +{ + unsigned int nirq; + unsigned int naddr; + unsigned int i; + int res; + struct dt_irq irq; + struct dt_raw_irq rirq; + u64 addr, size; + + nirq = dt_number_of_irq(dev); + naddr = dt_number_of_address(dev); + + DPRINT("%s nirq = %d naddr = %u\n", dt_node_full_name(dev), nirq, naddr); + + /* Map IRQs */ + for ( i = 0; i < nirq; i++ ) + { + res = dt_device_get_raw_irq(dev, i, &rirq); + if ( res ) + { + printk(XENLOG_ERR "Unable to retrieve irq %u for %s\n", + i, dt_node_full_name(dev)); + return res; + } + + /* + * Don''t map IRQ that have no physical meaning + * ie: IRQ whose controller is not the GIC + */ + if ( rirq.controller != dt_interrupt_controller ) + { + DPRINT("irq %u not connected to primary controller." + "Connected to %s\n", i, dt_node_full_name(rirq.controller)); + continue; + } + + res = dt_irq_translate(&rirq, &irq); + if ( res ) + { + printk(XENLOG_ERR "Unable to translate irq %u for %s\n", + i, dt_node_full_name(dev)); + return res; + } + + DPRINT("irq %u = %u type = 0x%x\n", i, irq.irq, irq.type); + /* Don''t check return because the IRQ can be use by multiple device */ + gic_route_irq_to_guest(d, &irq, dt_node_name(dev)); + } + + /* Map the address ranges */ + for ( i = 0; i < naddr; i++ ) + { + res = dt_device_get_address(dev, i, &addr, &size); + if ( res ) + { + printk(XENLOG_ERR "Unable to retrieve address %u for %s\n", + i, dt_node_full_name(dev)); + return res; + } + + DPRINT("addr %u = 0x%"PRIx64" - 0x%"PRIx64"\n", + i, addr, addr + size - 1); + + res = map_mmio_regions(d, addr & PAGE_MASK, + PAGE_ALIGN(addr + size) - 1, + addr & PAGE_MASK); + if ( res ) + { + printk(XENLOG_ERR "Unable to map 0x%"PRIx64 + " - 0x%"PRIx64" in dom0\n", + addr & PAGE_MASK, PAGE_ALIGN(addr + size) - 1); + return res; + } + } + + return 0; +} + +static int handle_node(struct domain *d, const struct dt_device_node *np) +{ + const struct dt_device_node *child; + int res; + + DPRINT("handle %s\n", dt_node_full_name(np)); + + /* Skip theses nodes and the sub-nodes */ + if ( dt_device_is_compatible(np, "xen,xen") || + dt_device_type_is_equal(np, "memory") || + !strcmp("/chosen", dt_node_full_name(np)) ) + return 0; + + if ( dt_device_used_by(np) != DOMID_XEN ) + { + res = map_device(d, np); + + if ( res ) + return res; + } + + for ( child = np->child; child != NULL; child = child->sibling ) + { + res = handle_node(d, child); + if ( res ) + return res; + } + + return 0; +} + +static int map_devices_from_device_tree(struct domain *d) +{ + ASSERT(dt_host && (dt_host->sibling == NULL)); + + return handle_node(d, dt_host); +} + static int prepare_dtb(struct domain *d, struct kernel_info *kinfo) { void *fdt; @@ -385,24 +512,7 @@ int construct_dom0(struct domain *d) if ( rc < 0 ) return rc; - printk("Map CS2 MMIO regions 1:1 in the P2M %#llx->%#llx\n", 0x18000000ULL, 0x1BFFFFFFULL); - map_mmio_regions(d, 0x18000000, 0x1BFFFFFF, 0x18000000); - printk("Map CS3 MMIO regions 1:1 in the P2M %#llx->%#llx\n", 0x1C000000ULL, 0x1FFFFFFFULL); - map_mmio_regions(d, 0x1C000000, 0x1FFFFFFF, 0x1C000000); - - printk("Routing peripheral interrupts to guest\n"); - /* TODO Get from device tree */ - gic_route_irq_to_guest(d, 34, "timer0"); - /*gic_route_irq_to_guest(d, 37, "uart0"); -- XXX used by Xen*/ - gic_route_irq_to_guest(d, 38, "uart1"); - gic_route_irq_to_guest(d, 39, "uart2"); - gic_route_irq_to_guest(d, 40, "uart3"); - gic_route_irq_to_guest(d, 41, "mmc0-1"); - gic_route_irq_to_guest(d, 42, "mmc0-2"); - gic_route_irq_to_guest(d, 44, "keyboard"); - gic_route_irq_to_guest(d, 45, "mouse"); - gic_route_irq_to_guest(d, 46, "lcd"); - gic_route_irq_to_guest(d, 47, "eth"); + map_devices_from_device_tree(d); /* The following loads use the domain''s p2m */ p2m_load_VTTBR(d); diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index a46516a..9207fad 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -696,13 +696,14 @@ void gic_inject(void) gic_inject_irq_start(); } -int gic_route_irq_to_guest(struct domain *d, unsigned int irq, +int gic_route_irq_to_guest(struct domain *d, const struct dt_irq *irq, const char * devname) { struct irqaction *action; - struct irq_desc *desc = irq_to_desc(irq); + struct irq_desc *desc = irq_to_desc(irq->irq); unsigned long flags; int retval; + bool_t level; action = xmalloc(struct irqaction); if (!action) @@ -710,6 +711,7 @@ int gic_route_irq_to_guest(struct domain *d, unsigned int irq, action->dev_id = d; action->name = devname; + action->free_on_release = 1; spin_lock_irqsave(&desc->lock, flags); spin_lock(&gic.lock); @@ -717,9 +719,11 @@ int gic_route_irq_to_guest(struct domain *d, unsigned int irq, desc->handler = &gic_guest_irq_type; desc->status |= IRQ_GUEST; - gic_set_irq_properties(irq, 1, 1u << smp_processor_id(), 0xa0); + level = dt_irq_is_level_triggered(irq); + + gic_set_irq_properties(irq->irq, level, 1u << smp_processor_id(), 0xa0); - retval = __setup_irq(desc, irq, action); + retval = __setup_irq(desc, irq->irq, action); if (retval) { xfree(action); goto out; diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h index e7608dc..513c1fc 100644 --- a/xen/include/asm-arm/gic.h +++ b/xen/include/asm-arm/gic.h @@ -157,7 +157,8 @@ extern int gic_events_need_delivery(void); extern void __cpuinit init_maintenance_interrupt(void); extern void gic_set_guest_irq(struct vcpu *v, unsigned int irq, unsigned int state, unsigned int priority); -extern int gic_route_irq_to_guest(struct domain *d, unsigned int irq, +extern int gic_route_irq_to_guest(struct domain *d, + const struct dt_irq *irq, const char * devname); /* Accept an interrupt from the GIC and dispatch its handler */ -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 31/41] xen/arm: Allow Xen to run on multiple platform without recompilation
Xen can include various platform support (ie: exynos5, versatile express...) and choose during boot time a set of callbacks for the current board. These callbacks will be called in places where each board can have specific code. For the moment the callbacks are: - platform_init: additional initialization for the platform - platform_init_time: some platform (ie: Exynos 5) needs to initialize the timer with an uncommon way - platform_specific_mapping: add mapping to dom0 which are not specified in the device tree - platform_reset: reset the platform - platform_poweroff: poweroff the platform - platform_quirks: list of quirks for a specific board. Signed-off-by: Julien Grall <julien.grall@linaro.org> Changes in v3: - Remove platform_{read,write} - Move platform_init_time before reading the timer frequency Changes in v2: - Add platform_poweroff - Add platform_quirks --- xen/arch/arm/Makefile | 1 + xen/arch/arm/domain_build.c | 4 ++ xen/arch/arm/platform.c | 137 ++++++++++++++++++++++++++++++++++++++++ xen/arch/arm/setup.c | 3 + xen/arch/arm/shutdown.c | 2 + xen/arch/arm/time.c | 6 ++ xen/arch/arm/xen.lds.S | 7 ++ xen/include/asm-arm/platform.h | 55 ++++++++++++++++ 8 files changed, 215 insertions(+) create mode 100644 xen/arch/arm/platform.c create mode 100644 xen/include/asm-arm/platform.h diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 4955a21..2717f26 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -18,6 +18,7 @@ obj-y += p2m.o obj-y += percpu.o obj-y += guestcopy.o obj-y += physdev.o +obj-y += platform.o obj-y += setup.o obj-y += time.o obj-y += smpboot.o diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c index 9a2ccad..75c3006 100644 --- a/xen/arch/arm/domain_build.c +++ b/xen/arch/arm/domain_build.c @@ -12,6 +12,7 @@ #include <xen/libfdt/libfdt.h> #include <xen/guest_access.h> #include <asm/setup.h> +#include <asm/platform.h> #include <asm/gic.h> #include <xen/irq.h> @@ -513,6 +514,9 @@ int construct_dom0(struct domain *d) return rc; map_devices_from_device_tree(d); + rc = platform_specific_mapping(d); + if ( rc < 0 ) + return rc; /* The following loads use the domain''s p2m */ p2m_load_VTTBR(d); diff --git a/xen/arch/arm/platform.c b/xen/arch/arm/platform.c new file mode 100644 index 0000000..b621f3a --- /dev/null +++ b/xen/arch/arm/platform.c @@ -0,0 +1,137 @@ +/* + * xen/arch/arm/platform.c + * + * Helpers to execute platform specific code. + * + * Julien Grall <julien.grall@linaro.org> + * Copyright (C) 2013 Linaro Limited. + * + * 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/platform.h> +#include <xen/device_tree.h> +#include <xen/init.h> + +extern const struct platform_desc _splatform[], _eplatform[]; + +/* Pointer to the current platform description */ +static const struct platform_desc *platform; + + +static bool_t __init platform_is_compatible(const struct platform_desc *plat) +{ + const char *const *compat; + + if ( !plat->compatible ) + return 0; + + for ( compat = plat->compatible; *compat; compat++ ) + { + if ( dt_machine_is_compatible(*compat) ) + return 1; + } + + return 0; +} + +/* List of possible platform */ +static void dump_platform_table(void) +{ + const struct platform_desc *p; + + printk("Available platform support:\n"); + + for ( p = _splatform; p != _eplatform; p++ ) + printk(" - %s\n", p->name); +} + +int __init platform_init(void) +{ + int res = 0; + + ASSERT(platform == NULL); + + /* Looking for the platform description */ + for ( platform = _splatform; platform != _eplatform; platform++ ) + { + if ( platform_is_compatible(platform) ) + break; + } + + /* We don''t have specific operations for this platform */ + if ( platform == _eplatform ) + { + /* TODO: List compatible */ + printk(XENLOG_WARNING "WARNING: Unrecognized/unsupported device tree " + "compatible list\n"); + dump_platform_table(); + platform = NULL; + } + else + printk(XENLOG_INFO "Platform: %s\n", platform->name); + + if ( platform && platform->init ) + res = platform->init(); + + return res; +} + +int __init platform_init_time(void) +{ + int res = 0; + + if ( platform && platform->init_time ) + res = platform->init_time(); + + return res; +} + +int __init platform_specific_mapping(struct domain *d) +{ + int res = 0; + + if ( platform && platform->specific_mapping ) + res = platform->specific_mapping(d); + + return res; +} + +void platform_reset(void) +{ + if ( platform && platform->reset ) + platform->reset(); +} + +void platform_poweroff(void) +{ + if ( platform && platform->poweroff ) + platform->poweroff(); +} + +bool_t platform_has_quirk(uint32_t quirk) +{ + uint32_t quirks = 0; + + if ( platform && platform->quirks ) + quirks = platform->quirks(); + + return !!(quirks & quirk); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index e5f4459..da2a734 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -42,6 +42,7 @@ #include <asm/early_printk.h> #include <asm/gic.h> #include <asm/cpufeature.h> +#include <asm/platform.h> struct cpuinfo_arm __read_mostly boot_cpu_data; @@ -440,6 +441,8 @@ void __init start_xen(unsigned long boot_phys_offset, processor_id(); + platform_init(); + init_xen_time(); gic_init(); diff --git a/xen/arch/arm/shutdown.c b/xen/arch/arm/shutdown.c index 61b5280..0903842 100644 --- a/xen/arch/arm/shutdown.c +++ b/xen/arch/arm/shutdown.c @@ -5,6 +5,7 @@ #include <xen/lib.h> #include <xen/mm.h> #include <xen/smp.h> +#include <asm/platform.h> static void raw_machine_reset(void) { @@ -21,6 +22,7 @@ static void raw_machine_reset(void) dsb(); isb(); clear_fixmap(FIXMAP_MISC); #endif + platform_reset(); } static void halt_this_cpu(void *arg) diff --git a/xen/arch/arm/time.c b/xen/arch/arm/time.c index 2e928bc..db849cf 100644 --- a/xen/arch/arm/time.c +++ b/xen/arch/arm/time.c @@ -33,6 +33,7 @@ #include <asm/time.h> #include <asm/gic.h> #include <asm/cpufeature.h> +#include <asm/platform.h> /* * Unfortunately the hypervisor timer interrupt appears to be buggy in @@ -123,11 +124,16 @@ int __init init_xen_time(void) timer_irq[TIMER_HYP_PPI].irq, timer_irq[TIMER_VIRT_PPI].irq); + res = platform_init_time(); + if ( res ) + return res; + /* Check that this CPU supports the Generic Timer interface */ if ( !cpu_has_gentimer ) panic("CPU does not support the Generic Timer v1 interface.\n"); cpu_khz = READ_SYSREG32(CNTFRQ_EL0) / 1000; + boot_count = READ_SYSREG64(CNTPCT_EL0); printk("Using generic timer at %lu KHz\n", cpu_khz); diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S index deab040..3b60668 100644 --- a/xen/arch/arm/xen.lds.S +++ b/xen/arch/arm/xen.lds.S @@ -77,6 +77,13 @@ SECTIONS #endif . = ALIGN(8); + .arch.info : { + _splatform = .; + *(.arch.info) + _eplatform = .; + } :text + + . = ALIGN(8); .dev.info : { _sdevice = .; *(.dev.info) diff --git a/xen/include/asm-arm/platform.h b/xen/include/asm-arm/platform.h new file mode 100644 index 0000000..e116265 --- /dev/null +++ b/xen/include/asm-arm/platform.h @@ -0,0 +1,55 @@ +#ifndef __ASM_ARM_PLATFORM_H +#define __ASM_ARM_PLATFORM_H + +#include <xen/init.h> +#include <xen/sched.h> +#include <xen/mm.h> + +/* Describe specific operation for a board */ +struct platform_desc { + /* Platform name */ + const char *name; + /* Array of device tree ''compatible'' strings */ + const char *const *compatible; + /* Platform initialization */ + int (*init)(void); + int (*init_time)(void); + /* Specific mapping for dom0 */ + int (*specific_mapping)(struct domain *d); + /* Platform reset */ + void (*reset)(void); + /* Platform power-off */ + void (*poweroff)(void); + /* + * Platform quirks + * Defined has a function because a platform can support multiple + * board with different quirk on each + */ + uint32_t (*quirks)(void); +}; + +int __init platform_init(void); +int __init platform_init_time(void); +int __init platform_specific_mapping(struct domain *d); +void platform_reset(void); +void platform_poweroff(void); +bool_t platform_has_quirk(uint32_t quirk); + +#define PLATFORM_START(_name, _namestr) \ +static const struct platform_desc __plat_desc_##_name __used \ +__attribute__((__section__(".arch.info"))) = { \ + .name = _namestr, + +#define PLATFORM_END \ +}; + +#endif /* __ASM_ARM_PLATFORM_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 32/41] xen/arm: WORKAROUND 1:1 memory mapping for dom0
Currently xen doesn''t implement SYS MMU. When a device will talk with dom0 with DMA request the domain will use GFN instead of MFN. For instance on the arndale board, without this patch the network doesn''t work. The 1:1 mapping is a workaround and MUST be remove as soon as a SYS MMU is implemented in XEN. Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> Changes in v2: - Add quirk in platform code to only enable 1:1 mapping if the board really need it to run (ie: SYS MMU is not yet implemented in Xen). --- xen/arch/arm/domain_build.c | 41 ++++++++++++++++++++++++++++++++++++++++ xen/include/asm-arm/platform.h | 6 ++++++ 2 files changed, 47 insertions(+) diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c index 75c3006..8369099 100644 --- a/xen/arch/arm/domain_build.c +++ b/xen/arch/arm/domain_build.c @@ -62,6 +62,43 @@ struct vcpu *__init alloc_dom0_vcpu0(void) return alloc_vcpu(dom0, 0, 0); } +static int set_memory_reg_11(struct domain *d, struct kernel_info *kinfo, + const void *fdt, const u32 *cell, int len, + int address_cells, int size_cells, u32 *new_cell) +{ + int reg_size = (address_cells + size_cells) * sizeof(*cell); + paddr_t start; + paddr_t size; + struct page_info *pg; + unsigned int order = get_order_from_bytes(dom0_mem); + int res; + paddr_t spfn; + + pg = alloc_domheap_pages(d, order, 0); + if ( !pg ) + panic("Failed to allocate contiguous memory for dom0\n"); + + spfn = page_to_mfn(pg); + start = spfn << PAGE_SHIFT; + size = (1 << order) << PAGE_SHIFT; + + // 1:1 mapping + printk("Populate P2M %#"PRIx64"->%#"PRIx64" (1:1 mapping for dom0)\n", + start, start + size); + res = guest_physmap_add_page(d, spfn, spfn, order); + + if ( res ) + panic("Unable to add pages in DOM0: %d\n", res); + + device_tree_set_reg(&new_cell, address_cells, size_cells, start, size); + + kinfo->mem.bank[0].start = start; + kinfo->mem.bank[0].size = size; + kinfo->mem.nr_banks = 1; + + return reg_size; +} + static int set_memory_reg(struct domain *d, struct kernel_info *kinfo, const void *fdt, const u32 *cell, int len, int address_cells, int size_cells, u32 *new_cell) @@ -71,6 +108,10 @@ static int set_memory_reg(struct domain *d, struct kernel_info *kinfo, u64 start; u64 size; + if ( platform_has_quirk(PLATFORM_QUIRK_DOM0_MAPPING_11) ) + return set_memory_reg_11(d, kinfo, fdt, cell, len, address_cells, + size_cells, new_cell); + while ( kinfo->unassigned_mem > 0 && l + reg_size <= len && kinfo->mem.nr_banks < NR_MEM_BANKS ) { diff --git a/xen/include/asm-arm/platform.h b/xen/include/asm-arm/platform.h index e116265..f460e9c 100644 --- a/xen/include/asm-arm/platform.h +++ b/xen/include/asm-arm/platform.h @@ -28,6 +28,12 @@ struct platform_desc { uint32_t (*quirks)(void); }; +/* + * Quirk to map dom0 memory in 1:1 + * Usefull on platform where System MMU is not yet implemented + */ +#define PLATFORM_QUIRK_DOM0_MAPPING_11 (1 << 0) + int __init platform_init(void); int __init platform_init_time(void); int __init platform_specific_mapping(struct domain *d); -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 33/41] xen/arm: Add versatile express platform
This platform contains nearly nothing specific except the reset function. Signed-off-by: Julien Grall <julien.grall@linaro.org> Changes in v3: - Use ioremap_attr instead of fixmap Changes in v2: - Add TODO to retrieve reset base address in the DT --- xen/arch/arm/platforms/vexpress.c | 43 ++++++++++++++++++++++++++++++ xen/arch/arm/shutdown.c | 14 ---------- xen/include/asm-arm/config.h | 3 --- xen/include/asm-arm/platforms/vexpress.h | 3 +++ 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/xen/arch/arm/platforms/vexpress.c b/xen/arch/arm/platforms/vexpress.c index fd4ce74..9b35e9d 100644 --- a/xen/arch/arm/platforms/vexpress.c +++ b/xen/arch/arm/platforms/vexpress.c @@ -18,7 +18,9 @@ */ #include <asm/platforms/vexpress.h> +#include <asm/platform.h> #include <xen/mm.h> +#include <xen/vmap.h> #define DCC_SHIFT 26 #define FUNCTION_SHIFT 20 @@ -91,6 +93,47 @@ out: } /* + * TODO: Get base address from the device tree + * See arm,vexpress-reset node + */ +static void vexpress_reset(void) +{ + void __iomem *base; + void __iomem *sp810; + + /* Use the SP810 system controller to force a reset */ + base = ioremap_attr(SP810_ADDRESS & PAGE_MASK, PAGE_SIZE, + PAGE_HYPERVISOR_NOCACHE); + if ( !base ) + { + dprintk(XENLOG_ERR, "Unable to map SP810\n"); + return; + } + + sp810 = base + (SP810_ADDRESS & ~PAGE_MASK); + + /* switch to slow mode */ + iowritel(sp810, 0x3); + dsb(); isb(); + /* writing any value to SCSYSSTAT reg will reset the system */ + iowritel(sp810 + 4, 0x1); + dsb(); isb(); + + iounmap(base); +} + +static const char const *vexpress_dt_compat[] __initdata +{ + "arm,vexpress", + NULL +}; + +PLATFORM_START(vexpress, "VERSATILE EXPRESS") + .compatible = vexpress_dt_compat, + .reset = vexpress_reset, +PLATFORM_END + +/* * Local variables: * mode: C * c-file-style: "BSD" diff --git a/xen/arch/arm/shutdown.c b/xen/arch/arm/shutdown.c index 0903842..767cc12 100644 --- a/xen/arch/arm/shutdown.c +++ b/xen/arch/arm/shutdown.c @@ -3,25 +3,11 @@ #include <xen/cpu.h> #include <xen/delay.h> #include <xen/lib.h> -#include <xen/mm.h> #include <xen/smp.h> #include <asm/platform.h> static void raw_machine_reset(void) { - /* XXX get this from device tree */ -#ifdef SP810_ADDRESS - /* Use the SP810 system controller to force a reset */ - volatile uint32_t *sp810; - set_fixmap(FIXMAP_MISC, SP810_ADDRESS >> PAGE_SHIFT, DEV_SHARED); - sp810 = ((uint32_t *) - (FIXMAP_ADDR(FIXMAP_MISC) + (SP810_ADDRESS & ~PAGE_MASK))); - sp810[0] = 0x3; /* switch to slow mode */ - dsb(); isb(); - sp810[1] = 0x1; /* writing any value to SCSYSSTAT reg will reset system */ - dsb(); isb(); - clear_fixmap(FIXMAP_MISC); -#endif platform_reset(); } diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h index 8ed72f5..7599202 100644 --- a/xen/include/asm-arm/config.h +++ b/xen/include/asm-arm/config.h @@ -149,9 +149,6 @@ extern unsigned long frametable_virt_end; #define GIC_CR_OFFSET 0x2000 #define GIC_HR_OFFSET 0x4000 /* Guess work http://lists.infradead.org/pipermail/linux-arm-kernel/2011-September/064219.html */ #define GIC_VR_OFFSET 0x6000 /* Virtual Machine CPU interface) */ -/* Board-specific: base address of system controller */ -#define SP810_ADDRESS 0x1C020000 - #endif /* __ARM_CONFIG_H__ */ /* diff --git a/xen/include/asm-arm/platforms/vexpress.h b/xen/include/asm-arm/platforms/vexpress.h index 67f8fef..5cf3aba 100644 --- a/xen/include/asm-arm/platforms/vexpress.h +++ b/xen/include/asm-arm/platforms/vexpress.h @@ -23,6 +23,9 @@ #define V2M_SYS_CFG_OSC4 4 #define V2M_SYS_CFG_OSC5 5 +/* Board-specific: base address of system controller */ +#define SP810_ADDRESS 0x1C020000 + #ifndef __ASSEMBLY__ #include <xen/inttypes.h> -- 1.7.10.4
All calls to this function in ARM code have been removed. This function SHOULD not be used. If someone calls this function, a linking error will occur. Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> --- xen/arch/arm/irq.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/xen/arch/arm/irq.c b/xen/arch/arm/irq.c index e83ad22..2fe4296 100644 --- a/xen/arch/arm/irq.c +++ b/xen/arch/arm/irq.c @@ -97,13 +97,6 @@ int __init request_dt_irq(const struct dt_irq *irq, void (*handler)(int, void *, struct cpu_user_regs *), unsigned long irqflags, const char *devname, void *dev_id) { - return request_irq(irq->irq, handler, irqflags, devname, dev_id); -} - -int __init request_irq(unsigned int irq, - void (*handler)(int, void *, struct cpu_user_regs *), - unsigned long irqflags, const char * devname, void *dev_id) -{ struct irqaction *action; int retval; @@ -113,7 +106,7 @@ int __init request_irq(unsigned int irq, * which interrupt is which (messes up the interrupt freeing * logic etc). */ - if (irq >= nr_irqs) + if (irq->irq >= nr_irqs) return -EINVAL; if (!handler) return -EINVAL; @@ -127,7 +120,7 @@ int __init request_irq(unsigned int irq, action->dev_id = dev_id; action->free_on_release = 1; - retval = setup_irq(irq, action); + retval = setup_dt_irq(irq, action); if (retval) xfree(action); -- 1.7.10.4
All calls to this function in ARM code have been removed. This function SHOULD not be used. If someone calls this function, a linking error will occur. Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> --- xen/arch/arm/gic.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index 9207fad..6b30cf9 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -557,20 +557,15 @@ static int __setup_irq(struct irq_desc *desc, unsigned int irq, int __init setup_dt_irq(const struct dt_irq *irq, struct irqaction *new) { - return setup_irq(irq->irq, new); -} - -int __init setup_irq(unsigned int irq, struct irqaction *new) -{ int rc; unsigned long flags; struct irq_desc *desc; - desc = irq_to_desc(irq); + desc = irq_to_desc(irq->irq); spin_lock_irqsave(&desc->lock, flags); - rc = __setup_irq(desc, irq, new); + rc = __setup_irq(desc, irq->irq, new); spin_unlock_irqrestore(&desc->lock, flags); -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 36/41] xen/arm: Don''t use pl011 UART by default for early printk
Add CONFIG_EARLY_PRINTK options in configs/arm{32,64}.mk to let the user to choose if he wants to have early output, ie before the console is initialized. This code is specific for each UART. When CONFIG_EARLY_PRINTK is enabled, Xen will only be able to run on a board with this UART. If a developper wants to add support for a new UART, he must implement the following assembly macro/define: - EALY_UART_BASE_ADDRESS: variable which contains the physical base address for the UART - early_uart_init: initialize the UART - early_uart_ready: check and wait until the UART can transmit a new character - early_uart_transmit: transmit a character For more details about the parameters of each function, see arm{32,64}/debug-pl011.inc comments. Signed-off-by: Julien Grall <julien.grall@linaro.org> Changes in v3: - Fix clobbers list in UART functions - Remove obj-$(CONFIG_EARLY_PL011) in arm32/Makefile. Was unused. - Set EARLY_PRINTK to y only if EARLY_DEBUG_INC is not null. Will avoid redundant line per early printk machine Changes in v2: - Use macro instead of function for assembly early printk API - Add documentation for ARM early printk --- docs/misc/arm/early-printk.txt | 14 ++++++++ xen/arch/arm/Makefile | 2 +- xen/arch/arm/Rules.mk | 18 ++++++++++ xen/arch/arm/arm32/Makefile | 4 ++- xen/arch/arm/arm32/debug-pl011.inc | 58 ++++++++++++++++++++++++++++++ xen/arch/arm/arm32/debug.S | 33 +++++++++++++++++ xen/arch/arm/arm32/head.S | 65 ++++++++++++++++----------------- xen/arch/arm/arm64/Makefile | 2 ++ xen/arch/arm/arm64/debug-pl011.inc | 59 ++++++++++++++++++++++++++++++ xen/arch/arm/arm64/debug.S | 33 +++++++++++++++++ xen/arch/arm/arm64/head.S | 69 ++++++++++++++++-------------------- xen/arch/arm/early_printk.c | 16 +-------- xen/include/asm-arm/config.h | 2 -- xen/include/asm-arm/early_printk.h | 2 +- 14 files changed, 285 insertions(+), 92 deletions(-) create mode 100644 docs/misc/arm/early-printk.txt create mode 100644 xen/arch/arm/arm32/debug-pl011.inc create mode 100644 xen/arch/arm/arm32/debug.S create mode 100644 xen/arch/arm/arm64/debug-pl011.inc create mode 100644 xen/arch/arm/arm64/debug.S diff --git a/docs/misc/arm/early-printk.txt b/docs/misc/arm/early-printk.txt new file mode 100644 index 0000000..4065811 --- /dev/null +++ b/docs/misc/arm/early-printk.txt @@ -0,0 +1,14 @@ +How to enable early printk + +Early printk can only be enabled if debug=y. You may want to enable it if +you are debbuging code that executes before the console is initialized. + +Note that selecting this option will limit Xen to a single UART definition. +Attempting to boot Xen image on a different platform *will not work*, so this +option should not be enable for Xens that are intended to be portable. + +CONFIG_EARLY_PRINTK=mach +where mach is the name of the machine: + - vexpress: printk with pl011 for versatile express + +By default early printk is disabled. diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 2717f26..87fabe1 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -2,7 +2,7 @@ subdir-$(arm32) += arm32 subdir-$(arm64) += arm64 subdir-y += platforms -obj-y += early_printk.o +obj-$(EARLY_PRINTK) += early_printk.o obj-y += cpu.o obj-y += domain.o obj-y += psci.o diff --git a/xen/arch/arm/Rules.mk b/xen/arch/arm/Rules.mk index a0a14e0..297385b 100644 --- a/xen/arch/arm/Rules.mk +++ b/xen/arch/arm/Rules.mk @@ -36,3 +36,21 @@ endif ifneq ($(call cc-option,$(CC),-fvisibility=hidden,n),n) CFLAGS += -DGCC_HAS_VISIBILITY_ATTRIBUTE endif + +EARLY_PRINTK := n + +ifeq ($(debug),y) + +# Early printk for versatile express +# TODO handle UART base address from make command line +ifeq ($(CONFIG_EARLY_PRINTK), vexpress) +EARLY_PRINTK_INC := pl011 +endif + +ifneq ($(EARLY_PRINTK_INC),) +EARLY_PRINTK := y +endif + +CFLAGS-$(EARLY_PRINTK) += -DEARLY_PRINTK +CFLAGS-$(EARLY_PRINTK) += -DEARLY_PRINTK_INC=\"debug-$(EARLY_PRINTK_INC).inc\" +endif diff --git a/xen/arch/arm/arm32/Makefile b/xen/arch/arm/arm32/Makefile index 1ad3364..aaf277a 100644 --- a/xen/arch/arm/arm32/Makefile +++ b/xen/arch/arm/arm32/Makefile @@ -5,4 +5,6 @@ obj-y += mode_switch.o obj-y += proc-ca15.o obj-y += traps.o -obj-y += domain.o \ No newline at end of file +obj-y += domain.o + +obj-$(EARLY_PRINTK) += debug.o diff --git a/xen/arch/arm/arm32/debug-pl011.inc b/xen/arch/arm/arm32/debug-pl011.inc new file mode 100644 index 0000000..6954aeb --- /dev/null +++ b/xen/arch/arm/arm32/debug-pl011.inc @@ -0,0 +1,58 @@ +/* + * xen/arch/arm/arm32/debug-pl011.inc + * + * PL011 specific debug code + * + * 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. + */ + +#define EARLY_UART_BASE_ADDRESS 0x1c090000 + +/* PL011 UART initialization + * rb: register which contains the UART base address + * rc: scratch register 1 + * rd: scratch register 2 (unused here) */ +.macro early_uart_init rb, rc, rd + mov \rc, #0x0 + str \rc, [\rb, #0x28] /* -> UARTFBRD (Baud divisor fraction) */ + mov \rc, #0x4 /* 7.3728MHz / 0x4 == 16 * 115200 */ + str \rc, [\rb, #0x24] /* -> UARTIBRD (Baud divisor integer) */ + mov \rc, #0x60 /* 8n1 */ + str \rc, [\rb, #0x2C] /* -> UARTLCR_H (Line control) */ + ldr \rc, =0x00000301 /* RXE | TXE | UARTEN */ + str \rc, [\rb, #0x30] /* -> UARTCR (Control Register) */ +.endm + +/* PL011 UART wait UART to be ready to transmit + * rb: register which contains the UART base address + * rc: scratch register */ +.macro early_uart_ready rb, rc +1: + ldr \rc, [\rb, #0x18] /* <- UARTFR (Flag register) */ + tst \rc, #0x8 /* Check BUSY bit */ + bne 1b /* Wait for the UART to be ready */ +.endm + +/* PL011 UART transmit character + * rb: register which contains the UART base address + * rt: register which contains the character to transmit */ +.macro early_uart_transmit rb, rt + str \rt, [\rb] /* -> UARTDR (Data Register) */ +.endm + +/* + * Local variables: + * mode: ASM + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/arm/arm32/debug.S b/xen/arch/arm/arm32/debug.S new file mode 100644 index 0000000..09e5cae --- /dev/null +++ b/xen/arch/arm/arm32/debug.S @@ -0,0 +1,33 @@ +/* + * xen/arch/arm/arm32/debug.S + * + * Wrapper for early printk + * + * Julien Grall <julien.grall@linaro.org> + * Copyright (c) 2013 Linaro Limited. + * + * 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/config.h> + +#ifdef EARLY_PRINTK_INC +#include EARLY_PRINTK_INC +#endif + +.globl early_putch +/* Print a character on the UART - this function is called by C + * r0: character to print */ +early_putch: + ldr r1, =FIXMAP_ADDR(FIXMAP_CONSOLE) /* r1 := VA UART base address */ + early_uart_ready r1, r2 + early_uart_transmit r1, r0 + mov pc, lr diff --git a/xen/arch/arm/arm32/head.S b/xen/arch/arm/arm32/head.S index 0b4cfde..d452b59 100644 --- a/xen/arch/arm/arm32/head.S +++ b/xen/arch/arm/arm32/head.S @@ -32,9 +32,13 @@ #define PT_UPPER(x) (PT_##x & 0xf00) #define PT_LOWER(x) (PT_##x & 0x0ff) +#if (defined (EARLY_PRINTK)) && (defined (EARLY_PRINTK_INC)) +#include EARLY_PRINTK_INC +#endif + /* Macro to print a string to the UART, if there is one. * Clobbers r0-r3. */ -#ifdef EARLY_UART_ADDRESS +#ifdef EARLY_PRINTK #define PRINT(_s) \ adr r0, 98f ; \ bl puts ; \ @@ -42,9 +46,9 @@ 98: .asciz _s ; \ .align 2 ; \ 99: -#else +#else /* EARLY_PRINTK */ #define PRINT(s) -#endif +#endif /* !EARLY_PRINTK */ .arm @@ -106,8 +110,8 @@ past_zImage: bne 1b boot_cpu: -#ifdef EARLY_UART_ADDRESS - ldr r11, =EARLY_UART_ADDRESS /* r11 := UART base address */ +#ifdef EARLY_PRINTK + ldr r11, =EARLY_UART_BASE_ADDRESS /* r11 := UART base address */ teq r12, #0 /* CPU 0 sets up the UART too */ bleq init_uart PRINT("- CPU ") @@ -216,7 +220,7 @@ skip_bss: bne pt_ready /* console fixmap */ -#ifdef EARLY_UART_ADDRESS +#if defined(EARLY_PRINTK) ldr r1, =xen_fixmap add r1, r1, r10 /* r1 := paddr (xen_fixmap) */ mov r3, #0 @@ -279,7 +283,7 @@ pt_ready: paging: -#ifdef EARLY_UART_ADDRESS +#ifdef EARLY_PRINTK /* Use a virtual address to access the UART. */ ldr r11, =FIXMAP_ADDR(FIXMAP_CONSOLE) #endif @@ -345,49 +349,42 @@ fail: PRINT("- Boot failed -\r\n") 1: wfe b 1b -#ifdef EARLY_UART_ADDRESS -/* Bring up the UART. Specific to the PL011 UART. +#ifdef EARLY_PRINTK +/* Bring up the UART. + * r11: Early UART base address * Clobbers r0-r2 */ init_uart: - mov r1, #0x0 - str r1, [r11, #0x28] /* -> UARTFBRD (Baud divisor fraction) */ - mov r1, #0x4 /* 7.3728MHz / 0x4 == 16 * 115200 */ - str r1, [r11, #0x24] /* -> UARTIBRD (Baud divisor integer) */ - mov r1, #0x60 /* 8n1 */ - str r1, [r11, #0x2C] /* -> UARTLCR_H (Line control) */ - ldr r1, =0x00000301 /* RXE | TXE | UARTEN */ - str r1, [r11, #0x30] /* -> UARTCR (Control Register) */ + early_uart_init r11, r1, r2 adr r0, 1f - b puts + b puts /* Jump to puts */ 1: .asciz "- UART enabled -\r\n" .align 4 -/* Print early debug messages. Specific to the PL011 UART. +/* Print early debug messages. * r0: Nul-terminated string to print. - * Clobbers r0-r2 */ + * r11: Early UART base address + * Clobbers r0-r1 */ puts: - ldr r2, [r11, #0x18] /* <- UARTFR (Flag register) */ - tst r2, #0x8 /* Check BUSY bit */ - bne puts /* Wait for the UART to be ready */ - ldrb r2, [r0], #1 /* Load next char */ - teq r2, #0 /* Exit on nul */ + early_uart_ready r11, r1 + ldrb r1, [r0], #1 /* Load next char */ + teq r1, #0 /* Exit on nul */ moveq pc, lr - str r2, [r11] /* -> UARTDR (Data Register) */ - b puts + early_uart_transmit r11, r1 + b puts /* Print a 32-bit number in hex. Specific to the PL011 UART. * r0: Number to print. - * clobbers r0-r3 */ + * r11: Early UART base address + * Clobbers r0-r3 */ putn: adr r1, hex mov r3, #8 -1: ldr r2, [r11, #0x18] /* <- UARTFR (Flag register) */ - tst r2, #0x8 /* Check BUSY bit */ - bne 1b /* Wait for the UART to be ready */ +1: + early_uart_ready r11, r2 and r2, r0, #0xf0000000 /* Mask off the top nybble */ ldrb r2, [r1, r2, lsr #28] /* Convert to a char */ - str r2, [r11] /* -> UARTDR (Data Register) */ + early_uart_transmit r11, r2 lsl r0, #4 /* Roll it through one nybble at a time */ subs r3, r3, #1 bne 1b @@ -396,7 +393,7 @@ putn: hex: .ascii "0123456789abcdef" .align 2 -#else /* EARLY_UART_ADDRESS */ +#else /* EARLY_PRINTK */ init_uart: .global early_puts @@ -404,7 +401,7 @@ early_puts: puts: putn: mov pc, lr -#endif /* EARLY_UART_ADDRESS */ +#endif /* !EARLY_PRINTK */ /* * Local variables: diff --git a/xen/arch/arm/arm64/Makefile b/xen/arch/arm/arm64/Makefile index be41f43..9484548 100644 --- a/xen/arch/arm/arm64/Makefile +++ b/xen/arch/arm/arm64/Makefile @@ -5,3 +5,5 @@ obj-y += mode_switch.o obj-y += traps.o obj-y += domain.o + +obj-$(EARLY_PRINTK) += debug.o diff --git a/xen/arch/arm/arm64/debug-pl011.inc b/xen/arch/arm/arm64/debug-pl011.inc new file mode 100644 index 0000000..ee6e0e0 --- /dev/null +++ b/xen/arch/arm/arm64/debug-pl011.inc @@ -0,0 +1,59 @@ +/* + * xen/arch/arm/arm64/debug-pl011.S + * + * PL011 specific debug code + * + * 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/asm_defns.h> + +#define EARLY_UART_BASE_ADDRESS 0x1c090000 + +/* PL011 UART initialization + * xb: register which containts the UART base address + * c: scratch register number */ +.macro early_uart_init xb, c + mov x\c, #0x0 + strh w\c, [\xb, #0x28] /* -> UARTFBRD (Baud divisor fraction) */ + mov x\c, #0x4 /* 7.3728MHz / 0x4 == 16 * 115200 */ + strh w\c, [\xb, #0x24] /* -> UARTIBRD (Baud divisor integer) */ + mov x\c, #0x60 /* 8n1 */ + str w\c, [\xb, #0x2C] /* -> UARTLCR_H (Line control) */ + ldr x\c, =0x00000301 /* RXE | TXE | UARTEN */ + str w\c, [\xb, #0x30] /* -> UARTCR (Control Register) */ +.endm + +/* PL011 UART wait UART to be ready to transmit + * xb: register which contains the UART base address + * c: scratch register number */ +.macro early_uart_ready xb, c +1: + ldrh w\c, [\xb, #0x18] /* <- UARTFR (Flag register) */ + tst w\c, #0x8 /* Check BUSY bit */ + b.ne 1b /* Wait for the UART to be ready */ +.endm + +/* PL011 UART transmit character + * xb: register which contains the UART base address + * wt: register which contains the character to transmit */ +.macro early_uart_transmit xb, wt + strb \wt, [\xb] /* -> UARTDR (Data Register) */ +.endm + +/* + * Local variables: + * mode: ASM + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/arm/arm64/debug.S b/xen/arch/arm/arm64/debug.S new file mode 100644 index 0000000..48a6567 --- /dev/null +++ b/xen/arch/arm/arm64/debug.S @@ -0,0 +1,33 @@ +/* + * xen/arch/arm/arm64/debug.S + * + * Wrapper for early printk + * + * Julien Grall <julien.grall@linaro.org> + * Copyright (c) 2013 Linaro Limited. + * + * 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/config.h> + +#ifdef EARLY_PRINTK_INC +#include EARLY_PRINTK_INC +#endif + +.globl early_putch +/* Print a character on the UART - this function is called by C + * x0: character to print */ +early_putch: + ldr x23, =FIXMAP_ADDR(FIXMAP_CONSOLE) + early_uart_ready x23, 1 + early_uart_transmit x23, w0 + ret diff --git a/xen/arch/arm/arm64/head.S b/xen/arch/arm/arm64/head.S index ef02899..79a8da9 100644 --- a/xen/arch/arm/arm64/head.S +++ b/xen/arch/arm/arm64/head.S @@ -29,9 +29,13 @@ #define PT_DEV 0xe71 /* nG=1 AF=1 SH=10 AP=01 NS=1 ATTR=100 T=0 P=1 */ #define PT_DEV_L3 0xe73 /* nG=1 AF=1 SH=10 AP=01 NS=1 ATTR=100 T=1 P=1 */ +#if (defined (EARLY_PRINTK)) && (defined (EARLY_PRINTK_INC)) +#include EARLY_PRINTK_INC +#endif + /* Macro to print a string to the UART, if there is one. - * Clobbers r0-r3. */ -#ifdef EARLY_UART_ADDRESS + * Clobbers x0-x3. */ +#ifdef EARLY_PRINTK #define PRINT(_s) \ adr x0, 98f ; \ bl puts ; \ @@ -39,9 +43,9 @@ 98: .asciz _s ; \ .align 2 ; \ 99: -#else +#else /* EARLY_PRINTK */ #define PRINT(s) -#endif +#endif /* !EARLY_PRINTK */ /*.aarch64*/ @@ -109,8 +113,8 @@ real_start: 2: boot_cpu: -#ifdef EARLY_UART_ADDRESS - ldr x23, =EARLY_UART_ADDRESS /* x23 := UART base address */ +#ifdef EARLY_PRINTK + ldr x23, =EARLY_UART_BASE_ADDRESS /* x23 := UART base address */ cbnz x22, 1f bl init_uart /* CPU 0 sets up the UART too */ 1: PRINT("- CPU ") @@ -206,7 +210,6 @@ skip_bss: mov x4, x1 /* Next level into xen_first */ /* console fixmap */ -#ifdef EARLY_UART_ADDRESS ldr x1, =xen_fixmap add x1, x1, x20 /* x1 := paddr (xen_fixmap) */ lsr x2, x23, #12 @@ -214,7 +217,6 @@ skip_bss: mov x3, #PT_DEV_L3 orr x2, x2, x3 /* x2 := 4K dev map including UART */ str x2, [x1, #(FIXMAP_CONSOLE*8)] /* Map it in the first fixmap''s slot */ -#endif /* Build the baseline idle pagetable''s first-level entries */ ldr x1, =xen_second @@ -266,10 +268,8 @@ pt_ready: br x1 /* Get a proper vaddr into PC */ paging: -#ifdef EARLY_UART_ADDRESS /* Use a virtual address to access the UART. */ ldr x23, =FIXMAP_ADDR(FIXMAP_CONSOLE) -#endif PRINT("- Ready -\r\n") @@ -329,51 +329,44 @@ fail: PRINT("- Boot failed -\r\n") 1: wfe b 1b -#ifdef EARLY_UART_ADDRESS +#ifdef EARLY_PRINTK -/* Bring up the UART. Specific to the PL011 UART. - * Clobbers r0-r2 */ +/* Bring up the UART. + * x23: Early UART base address + * Clobbers x0-x1 */ init_uart: - mov x1, #0x0 - strh w1, [x23, #0x24] /* -> UARTIBRD (Baud divisor fraction) */ - mov x1, #0x4 /* 7.3728MHz / 0x4 == 16 * 115200 */ - strh w1, [x23, #0x24] /* -> UARTIBRD (Baud divisor integer) */ - mov x1, #0x60 /* 8n1 */ - strh w1, [x23, #0x24] /* -> UARTLCR_H (Line control) */ - ldr x1, =0x00000301 /* RXE | TXE | UARTEN */ - strh w1, [x23, #0x30] /* -> UARTCR (Control Register) */ + early_uart_init x23, 0 adr x0, 1f b puts 1: .asciz "- UART enabled -\r\n" .align 4 -/* Print early debug messages. Specific to the PL011 UART. - * r0: Nul-terminated string to print. - * Clobbers r0-r2 */ +/* Print early debug messages. + * x0: Nul-terminated string to print. + * x23: Early UART base address + * Clobbers x0-x1 */ puts: - ldrh w2, [x23, #0x18] /* <- UARTFR (Flag register) */ - tst w2, #0x8 /* Check BUSY bit */ - b.ne puts /* Wait for the UART to be ready */ - ldrb w2, [x0], #1 /* Load next char */ - cbz w2, 1f /* Exit on nul */ - str w2, [x23] /* -> UARTDR (Data Register) */ + early_uart_ready x23, 1 + ldrb w1, [x0], #1 /* Load next char */ + cbz w1, 1f /* Exit on nul */ + early_uart_transmit x23, w1 b puts 1: ret /* Print a 32-bit number in hex. Specific to the PL011 UART. - * r0: Number to print. - * clobbers r0-r3 */ + * x0: Number to print. + * x23: Early UART base address + * Clobbers x0-x3 */ putn: adr x1, hex mov x3, #8 -1: ldrh w2, [x23, #0x18] /* <- UARTFR (Flag register) */ - tst w2, #0x8 /* Check BUSY bit */ - b.ne 1b /* Wait for the UART to be ready */ +1: + early_uart_ready x23, 2 and x2, x0, #0xf0000000 /* Mask off the top nybble */ lsr x2, x2, #28 ldrb w2, [x1, x2] /* Convert to a char */ - strb w2, [x23] /* -> UARTDR (Data Register) */ + early_uart_transmit x23, w2 lsl x0, x0, #4 /* Roll it through one nybble at a time */ subs x3, x3, #1 b.ne 1b @@ -382,7 +375,7 @@ putn: hex: .ascii "0123456789abcdef" .align 2 -#else /* EARLY_UART_ADDRESS */ +#else /* EARLY_PRINTK */ init_uart: .global early_puts @@ -390,4 +383,4 @@ early_puts: puts: putn: ret -#endif /* EARLY_UART_ADDRESS */ +#endif /* EARLY_PRINTK */ diff --git a/xen/arch/arm/early_printk.c b/xen/arch/arm/early_printk.c index 65fa912..90ace5d 100644 --- a/xen/arch/arm/early_printk.c +++ b/xen/arch/arm/early_printk.c @@ -15,19 +15,7 @@ #include <xen/string.h> #include <asm/early_printk.h> -#ifdef EARLY_UART_ADDRESS - -void __init early_putch(char c) -{ - volatile uint32_t *r; - - r = (uint32_t *)(XEN_VIRT_START + (1 << 21)); - - /* XXX: assuming a PL011 UART. */ - while(*(r + 0x6) & 0x8) - ; - *r = c; -} +void early_putch(char c); /* Early printk buffer */ static char __initdata buf[512]; @@ -68,5 +56,3 @@ early_panic(const char *fmt, ...) while(1); } - -#endif /* #ifdef EARLY_UART_ADDRESS */ diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h index 7599202..6414c89 100644 --- a/xen/include/asm-arm/config.h +++ b/xen/include/asm-arm/config.h @@ -141,8 +141,6 @@ extern unsigned long frametable_virt_end; #define watchdog_disable() ((void)0) #define watchdog_enable() ((void)0) -/* Board-specific: base address of PL011 UART */ -#define EARLY_UART_ADDRESS 0x1c090000 /* Board-specific: base address of GIC + its regs */ #define GIC_BASE_ADDRESS 0x2c000000 #define GIC_DR_OFFSET 0x1000 diff --git a/xen/include/asm-arm/early_printk.h b/xen/include/asm-arm/early_printk.h index 7083199..c5b99cd 100644 --- a/xen/include/asm-arm/early_printk.h +++ b/xen/include/asm-arm/early_printk.h @@ -14,7 +14,7 @@ #include <xen/init.h> #include <xen/stdarg.h> -#ifdef EARLY_UART_ADDRESS +#ifdef EARLY_PRINTK void __init early_vprintk(const char *fmt, va_list args); void early_printk(const char *fmt, ...); -- 1.7.10.4
From: Anthony PERARD <anthony.perard@citrix.com> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> Signed-off-by: Julien Grall <julien.grall@linaro.org> Changes in v3: - Directly get the base address from the DT and map it with ioremap_attr - Use ioreadl/iowritel - Typoes - Remove the remaining // Changes in v2: - Use defines where it''s possible - Move UART definition in a separate header. Will be used for early UART - Replace all // by /* ... */ - Add Anthony as first author --- config/arm32.mk | 1 + xen/drivers/char/Makefile | 1 + xen/drivers/char/exynos4210-uart.c | 359 +++++++++++++++++++++++++++++++++ xen/include/asm-arm/exynos4210-uart.h | 111 ++++++++++ 4 files changed, 472 insertions(+) create mode 100644 xen/drivers/char/exynos4210-uart.c create mode 100644 xen/include/asm-arm/exynos4210-uart.h diff --git a/config/arm32.mk b/config/arm32.mk index f64f0c1..d8e958b 100644 --- a/config/arm32.mk +++ b/config/arm32.mk @@ -8,6 +8,7 @@ CONFIG_ARM_$(XEN_OS) := y CFLAGS += -marm HAS_PL011 := y +HAS_EXYNOS4210 := y # Use only if calling $(LD) directly. #LDFLAGS_DIRECT_OpenBSD = _obsd diff --git a/xen/drivers/char/Makefile b/xen/drivers/char/Makefile index 9c067f9..37543f0 100644 --- a/xen/drivers/char/Makefile +++ b/xen/drivers/char/Makefile @@ -1,6 +1,7 @@ obj-y += console.o obj-$(HAS_NS16550) += ns16550.o obj-$(HAS_PL011) += pl011.o +obj-$(HAS_EXYNOS4210) += exynos4210-uart.o obj-$(HAS_EHCI) += ehci-dbgp.o obj-$(CONFIG_ARM) += dt-uart.o obj-y += serial.o diff --git a/xen/drivers/char/exynos4210-uart.c b/xen/drivers/char/exynos4210-uart.c new file mode 100644 index 0000000..437a987 --- /dev/null +++ b/xen/drivers/char/exynos4210-uart.c @@ -0,0 +1,359 @@ +/* + * xen/drivers/char/exynos4210-uart.c + * + * Driver for Exynos 4210 UART. + * + * Anthony PERARD <anthony.perard@citrix.com> + * Copyright (c) 2012 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 <xen/config.h> +#include <xen/console.h> +#include <xen/errno.h> +#include <xen/serial.h> +#include <xen/init.h> +#include <xen/irq.h> +#include <xen/mm.h> +#include <asm/early_printk.h> +#include <asm/device.h> +#include <asm/exynos4210-uart.h> + +static struct exynos4210_uart { + unsigned int baud, clock_hz, data_bits, parity, stop_bits; + struct dt_irq irq; + void *regs; + struct irqaction irqaction; +} exynos4210_com = {0}; + +/* These parity settings can be ORed directly into the ULCON. */ +#define PARITY_NONE (0) +#define PARITY_ODD (0x4) +#define PARITY_EVEN (0x5) +#define FORCED_CHECKED_AS_ONE (0x6) +#define FORCED_CHECKED_AS_ZERO (0x7) + +#define exynos4210_read(uart, off) ioreadl((uart)->regs + off) +#define exynos4210_write(uart, off, val) iowritel((uart->regs) + off, val) + +static void exynos4210_uart_interrupt(int irq, void *data, struct cpu_user_regs *regs) +{ + struct serial_port *port = data; + struct exynos4210_uart *uart = port->uart; + unsigned int status; + + status = exynos4210_read(uart, UINTP); + + while ( status != 0 ) + { + /* Clear all pending interrupts + * but should take care of ERROR and MODEM + */ + + if ( status & UINTM_ERROR ) + { + uint32_t error_bit; + + error_bit = exynos4210_read(uart, UERSTAT); + + if ( error_bit & UERSTAT_OVERRUN ) + dprintk(XENLOG_ERR, "uart: overrun error\n"); + if ( error_bit & UERSTAT_PARITY ) + dprintk(XENLOG_ERR, "uart: parity error\n"); + if ( error_bit & UERSTAT_FRAME ) + dprintk(XENLOG_ERR, "uart: frame error\n"); + if ( error_bit & UERSTAT_BREAK ) + dprintk(XENLOG_ERR, "uart: break detected\n"); + /* Clear error pending interrupt */ + exynos4210_write(uart, UINTP, UINTM_ERROR); + } + + + if ( status & (UINTM_RXD | UINTM_ERROR) ) + { + /* uart->regs[UINTM] |= RXD|ERROR; */ + serial_rx_interrupt(port, regs); + /* uart->regs[UINTM] &= ~(RXD|ERROR); */ + exynos4210_write(uart, UINTP, UINTM_RXD | UINTM_ERROR); + } + + if ( status & (UINTM_TXD | UINTM_MODEM) ) + { + /* uart->regs[UINTM] |= TXD|MODEM; */ + serial_tx_interrupt(port, regs); + /* uart->regs[UINTM] &= ~(TXD|MODEM); */ + exynos4210_write(uart, UINTP, UINTM_TXD | UINTM_MODEM); + } + + status = exynos4210_read(uart, UINTP); + } +} + +static void __init exynos4210_uart_init_preirq(struct serial_port *port) +{ + struct exynos4210_uart *uart = port->uart; + unsigned int divisor; + uint32_t ulcon; + + /* reset, TX/RX disables */ + exynos4210_write(uart, UCON, 0); + + /* No Interrupt, auto flow control */ + exynos4210_write(uart, UMCON, 0); + + /* Line control and baud-rate generator. */ + if ( uart->baud != BAUD_AUTO ) + { + /* Baud rate specified: program it into the divisor latch. */ + divisor = ((uart->clock_hz) / (uart->baud)) - 1; + /* FIXME: will use a hacked divisor, assuming the src clock and bauds */ + exynos4210_write(uart, UFRACVAL, 53); + exynos4210_write(uart, UBRDIV, 4); + } + else + { + /* + * TODO: should be updated + * Baud rate already set: read it out from the divisor latch. + * divisor = (uart->regs[IBRD] << 6) | uart->regs[FBRD]; + * uart->baud = (uart->clock_hz << 2) / divisor; + */ + } + + /* + * Number of bits per character + * 0 => 5 bits + * 1 => 6 bits + * 2 => 7 bits + * 3 => 8 bits + */ + ASSERT(uart->data_bits >= 5 && uart->data_bits <= 8); + ulcon = (uart->data_bits - 5); + + /* + * Stop bits + * 0 => 1 stop bit per frame + * 1 => 2 stop bit per frame + */ + ASSERT(uart->stop_bits >= 1 && uart->stop_bits <= 2); + ulcon |= (uart->stop_bits - 1) << ULCON_STOPB_SHIFT; + + + /* Parity */ + ulcon |= uart->parity << ULCON_PARITY_SHIFT; + + exynos4210_write(uart, ULCON, ulcon); + + /* Mask and clear the interrupts */ + exynos4210_write(uart, UINTM, UINTM_ALLI); + exynos4210_write(uart, UINTP, UINTM_ALLI); + + /* reset FIFO */ + exynos4210_write(uart, UFCON, UFCON_FIFO_RESET); + + /* TODO: Add timeout to avoid infinite loop */ + while ( exynos4210_read(uart, UFCON) & UFCON_FIFO_RESET ) + ; + + /* + * Enable FIFO and set the trigger level of Tx FIFO + * The trigger level is always set to b101, an interrupt will be + * generated when data count of Tx FIFO is less than or equal to the + * following value: + * UART0 => 160 bytes + * UART1 => 40 bytes + * UART2 => 10 bytes + * UART3 => 10 bytes + */ + exynos4210_write(uart, UFCON, UFCON_FIFO_TX_TRIGGER | UFCON_FIFO_EN); + + /* + * Enable the UART for Rx and Tx + * - Use only interrupt request + * - Interrupts are level trigger + * - Enable Rx timeout + */ + exynos4210_write(uart, UCON, + UCON_RX_IRQ_LEVEL | UCON_TX_IRQ_LEVEL | UCON_RX_IRQ | + UCON_TX_IRQ | UCON_RX_TIMEOUT); +} + +static void __init exynos4210_uart_init_postirq(struct serial_port *port) +{ + struct exynos4210_uart *uart = port->uart; + int rc; + + uart->irqaction.handler = exynos4210_uart_interrupt; + uart->irqaction.name = "exynos4210_uart"; + uart->irqaction.dev_id = port; + + if ( (rc = setup_dt_irq(&uart->irq, &uart->irqaction)) != 0 ) + dprintk(XENLOG_ERR, "Failed to allocated exynos4210_uart IRQ %d\n", + uart->irq.irq); + + /* Unmask interrupts */ + exynos4210_write(uart, UINTM, ~UINTM_ALLI); + + /* Clear pending interrupts */ + exynos4210_write(uart, UINTP, UINTM_ALLI); + + /* Enable interrupts */ + exynos4210_write(uart, UMCON, exynos4210_read(uart, UMCON) | UMCON_INT_EN); +} + +static void exynos4210_uart_suspend(struct serial_port *port) +{ + BUG(); // XXX +} + +static void exynos4210_uart_resume(struct serial_port *port) +{ + BUG(); // XXX +} + +static unsigned int exynos4210_uart_tx_ready(struct serial_port *port) +{ + struct exynos4210_uart *uart = port->uart; + + /* Tx fifo full ? */ + if ( exynos4210_read(uart, UFSTAT) & UFSTAT_TX_FULL ) + return 0; + else + { + uint32_t val = exynos4210_read(uart, UFSTAT); + + val = (val & UFSTAT_TX_COUNT_MASK) >> UFSTAT_TX_COUNT_SHIFT; + + /* XXX: Here we assume that we use UART 2/3, on the others + * UART the buffer is bigger + */ + ASSERT(val >= 0 && val <= FIFO_MAX_SIZE); + + return (FIFO_MAX_SIZE - val); + } +} + +static void exynos4210_uart_putc(struct serial_port *port, char c) +{ + struct exynos4210_uart *uart = port->uart; + + exynos4210_write(uart, UTXH, (uint32_t)(unsigned char)c); +} + +static int exynos4210_uart_getc(struct serial_port *port, char *pc) +{ + struct exynos4210_uart *uart = port->uart; + uint32_t ufstat = exynos4210_read(uart, UFSTAT); + uint32_t count; + + count = (ufstat & UFSTAT_RX_COUNT_MASK) >> UFSTAT_RX_COUNT_SHIFT; + + /* Check if Rx fifo is full or if the is something in it */ + if ( ufstat & UFSTAT_RX_FULL || count ) + { + *pc = exynos4210_read(uart, URXH) & URXH_DATA_MASK; + return 1; + } + else + return 0; +} + +static int __init exynos4210_uart_irq(struct serial_port *port) +{ + struct exynos4210_uart *uart = port->uart; + + return uart->irq.irq; +} + +static const struct dt_irq __init *exynos4210_uart_dt_irq(struct serial_port *port) +{ + struct exynos4210_uart *uart = port->uart; + + return &uart->irq; +} + +static struct uart_driver __read_mostly exynos4210_uart_driver = { + .init_preirq = exynos4210_uart_init_preirq, + .init_postirq = exynos4210_uart_init_postirq, + .endboot = NULL, + .suspend = exynos4210_uart_suspend, + .resume = exynos4210_uart_resume, + .tx_ready = exynos4210_uart_tx_ready, + .putc = exynos4210_uart_putc, + .getc = exynos4210_uart_getc, + .irq = exynos4210_uart_irq, + .dt_irq_get = exynos4210_uart_dt_irq, +}; + +/* TODO: Parse UART config from the command line */ +static int __init exynos4210_uart_init(struct dt_device_node *dev, + const void *data) +{ + struct exynos4210_uart *uart; + int res; + u64 addr, size; + + uart = &exynos4210_com; + + /* uart->clock_hz = 0x16e3600; */ + uart->baud = BAUD_AUTO; + uart->data_bits = 8; + uart->parity = PARITY_NONE; + uart->stop_bits = 1; + + res = dt_device_get_address(dev, 0, &addr, &size); + if ( res ) + { + early_printk("exynos4210: 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("exynos4210: Unable to map the UART memory\n"); + } + res = dt_device_get_irq(dev, 0, &uart->irq); + if ( res ) + { + early_printk("exynos4210: Unable to retrieve the IRQ\n"); + return res; + } + + /* Register with generic serial driver. */ + serial_register_uart(SERHND_DTUART, &exynos4210_uart_driver, uart); + + dt_device_set_used_by(dev, DOMID_XEN); + + return 0; +} + +static const char const *exynos4210_dt_compat[] __initdata +{ + "samsung,exynos4210-uart", + NULL +}; + +DT_DEVICE_START(exynos4210, "Exynos 4210 UART", DEVICE_SERIAL) + .compatible = exynos4210_dt_compat, + .init = exynos4210_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/asm-arm/exynos4210-uart.h b/xen/include/asm-arm/exynos4210-uart.h new file mode 100644 index 0000000..330e1c0 --- /dev/null +++ b/xen/include/asm-arm/exynos4210-uart.h @@ -0,0 +1,111 @@ +/* + * xen/include/asm-arm/exynos4210-uart.h + * + * Common constant definition between early printk and the UART driver + * for the exynos 4210 UART + * + * Julien Grall <julien.grall@linaro.org> + * Copyright (c) 2013 Linaro Limited. + * + * 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. + */ + +#ifndef __ASM_ARM_EXYNOS4210_H +#define __ASM_ARM_EXYNOS4210_H + + +/* + * this value is only valid for UART 2 and UART 3 + * XXX: define per UART + */ +#define FIFO_MAX_SIZE 16 + +/* register addresses */ +#define ULCON (0x00) +#define UCON (0x04) +#define UFCON (0x08) +#define UMCON (0x0c) +#define UTRSTAT (0x10) +#define UERSTAT (0x14) +#define UFSTAT (0x18) +#define UMSTAT (0x1c) +#define UTXH (0x20) +#define URXH (0x24) +#define UBRDIV (0x28) +#define UFRACVAL (0x2c) +#define UINTP (0x30) +#define UINTS (0x34) +#define UINTM (0x38) + +/* UCON */ +#define UCON_RX_IRQ (1 << 0) +#define UCON_TX_IRQ (1 << 2) +#define UCON_RX_TIMEOUT (1 << 7) + +/* + * FIXME: IRQ_LEVEL should be 1 << n but with this value, the IRQ + * handler will never end... + */ +#define UCON_RX_IRQ_LEVEL (0 << 8) +#define UCON_TX_IRQ_LEVEL (0 << 9) + +/* ULCON */ +#define ULCON_STOPB_SHIFT 2 +#define ULCON_PARITY_SHIFT 3 + +/* UFCON */ +#define UFCON_FIFO_TX_RESET (1 << 2) +#define UFCON_FIFO_RX_RESET (1 << 1) +#define UFCON_FIFO_RESET (UFCON_FIFO_TX_RESET | UFCON_FIFO_RX_RESET) +#define UFCON_FIFO_EN (1 << 0) + +#define UFCON_FIFO_TX_TRIGGER (0x6 << 8) + +/* UMCON */ +#define UMCON_INT_EN (1 << 3) + +/* UERSTAT */ +#define UERSTAT_OVERRUN (1 << 0) +#define UERSTAT_PARITY (1 << 1) +#define UERSTAT_FRAME (1 << 2) +#define UERSTAT_BREAK (1 << 3) + +/* UFSTAT */ +#define UFSTAT_TX_FULL (1 << 24) +#define UFSTAT_TX_COUNT_SHIFT (16) +#define UFSTAT_TX_COUNT_MASK (0xff << UFSTAT_TX_COUNT_SHIFT) +#define UFSTAT_RX_FULL (1 << 8) +#define UFSTAT_RX_COUNT_SHIFT (0) +#define UFSTAT_RX_COUNT_MASK (0xff << UFSTAT_RX_COUNT_SHIFT) + +/* UTRSTAT */ +#define UTRSTAT_TX_EMPTY (1 << 1) + +/* URHX */ +#define URXH_DATA_MASK (0xff) + +/* Interrupt bits (UINTP, UINTS, UINTM) */ +#define UINTM_MODEM (1 << 3) +#define UINTM_TXD (1 << 2) +#define UINTM_ERROR (1 << 1) +#define UINTM_RXD (1 << 0) +#define UINTM_ALLI (UINTM_MODEM | UINTM_TXD | UINTM_ERROR | UINTM_RXD) + +#endif /* __ASM_ARM_EXYNOS4210_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 38/41] xen/arm: Add Exynos 4210 UART support for early printk
From: Anthony PERARD <anthony.perard@citrix.com> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> Signed-off-by: Julien Grall <julien.grall@linaro.org> Changes in v3: - Typoes - Remove $(CONFIG_EARLY_PL011) because of a bad merge in v2 - Remove redundant line EARLY_PRINTK :=y Changes in v2: - Use assembly macro instead of function - Add Anthony as first author --- docs/misc/arm/early-printk.txt | 1 + xen/arch/arm/Rules.mk | 3 ++ xen/arch/arm/arm32/debug-exynos4210.inc | 77 +++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 xen/arch/arm/arm32/debug-exynos4210.inc diff --git a/docs/misc/arm/early-printk.txt b/docs/misc/arm/early-printk.txt index 4065811..d5cae85 100644 --- a/docs/misc/arm/early-printk.txt +++ b/docs/misc/arm/early-printk.txt @@ -10,5 +10,6 @@ option should not be enable for Xens that are intended to be portable. CONFIG_EARLY_PRINTK=mach where mach is the name of the machine: - vexpress: printk with pl011 for versatile express + - exynos5250: printk with the second UART By default early printk is disabled. diff --git a/xen/arch/arm/Rules.mk b/xen/arch/arm/Rules.mk index 297385b..b6a6890 100644 --- a/xen/arch/arm/Rules.mk +++ b/xen/arch/arm/Rules.mk @@ -46,6 +46,9 @@ ifeq ($(debug),y) ifeq ($(CONFIG_EARLY_PRINTK), vexpress) EARLY_PRINTK_INC := pl011 endif +ifeq ($(CONFIG_EARLY_PRINTK), exynos5250) +EARLY_PRINTK_INC := exynos4210 +endif ifneq ($(EARLY_PRINTK_INC),) EARLY_PRINTK := y diff --git a/xen/arch/arm/arm32/debug-exynos4210.inc b/xen/arch/arm/arm32/debug-exynos4210.inc new file mode 100644 index 0000000..4241640 --- /dev/null +++ b/xen/arch/arm/arm32/debug-exynos4210.inc @@ -0,0 +1,77 @@ +/* + * xen/arch/arm/arm32/debug-exynos4210.inc + * + * Exynos 5 specific debug code + * + * 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/exynos4210-uart.h> + +#define EARLY_UART_BASE_ADDRESS 0x12c20000 + +/* Exynos 5 UART initialization + * rb: register which contains the UART base address + * rc: scratch register 1 + * rd: scratch register 2 */ +.macro early_uart_init rb rc rd + /* init clock */ + ldr \rc, =0x10020000 + /* select MPLL (800MHz) source clock */ + ldr \rd, [\rc, #0x250] + and \rd, \rd, #(~(0xf<<8)) + orr \rd, \rd, #(0x6<<8) + str \rd, [\rc, #0x250] + /* ratio 800/(7+1) */ + ldr \rd, [\rc, #0x558] + and \rd, \rd, #(~(0xf<<8)) + orr \rd, \rd, #(0x7<<8) + str \rd, [\rc, #0x558] + + mov \rc, #4 + str \rc, [\rb, #UFRACVAL] /* -> UFRACVAL (Baud divisor fraction) */ + mov \rc, #53 + str \rc, [\rb, #UBRDIV] /* -> UBRDIV (Baud divisor integer) */ + mov \rc, #3 /* 8n1 */ + str \rc, [\rb, #ULCON] /* -> (Line control) */ + ldr \rc, =UCON_TX_IRQ /* TX IRQMODE */ + str \rc, [\rb, #UCON] /* -> (Control Register) */ + mov \rc, #0x0 + str \rc, [\rb, #UFCON] /* disable FIFO */ + mov \rc, #0x0 + str \rc, [\rb, #UMCON] /* no auto flow control */ +.endm + +/* Exynos 5 UART wait UART to be ready to transmit + * rb: register which contains the UART base address + * rc: scratch register */ +.macro early_uart_ready rb rc +1: + ldr \rc, [\rb, #UTRSTAT] /* <- UTRSTAT (Flag register) */ + tst \rc, #UTRSTAT_TX_EMPTY /* Check BUSY bit */ + beq 1b /* Wait for the UART to be ready */ +.endm + +/* Exynos 5 UART transmit character + * rb: register which contains the UART base address + * rt: register which contains the character to transmit */ +.macro early_uart_transmit rb rt + str \rt, [\rb, #UTXH] /* -> UTXH (Data Register) */ +.endm + +/* + * Local variables: + * mode: ASM + * indent-tabs-mode: nil + * End: + */ -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 39/41] xen/arm: Add platform specific code for the exynos5
Signed-off-by: Julien Grall <julien.grall@linaro.org> Changes in v3: - Remove hard tab - Typoes - Replace platform_{read,write} call to ioremap_attr, ioreadl, iowritel Changes in v2: - Add dom0 1:1 mapping quirk for the arndale board - s/mapping/mappings/ in comment - Remove debug trap (unnecessary with linux 3.9) --- xen/arch/arm/platforms/Makefile | 1 + xen/arch/arm/platforms/exynos5.c | 110 +++++++++++++++++++++++++++++++ xen/include/asm-arm/platforms/exynos5.h | 39 +++++++++++ 3 files changed, 150 insertions(+) create mode 100644 xen/arch/arm/platforms/exynos5.c create mode 100644 xen/include/asm-arm/platforms/exynos5.h diff --git a/xen/arch/arm/platforms/Makefile b/xen/arch/arm/platforms/Makefile index 4313e95..ff2b65b 100644 --- a/xen/arch/arm/platforms/Makefile +++ b/xen/arch/arm/platforms/Makefile @@ -1 +1,2 @@ obj-y += vexpress.o +obj-y += exynos5.o diff --git a/xen/arch/arm/platforms/exynos5.c b/xen/arch/arm/platforms/exynos5.c new file mode 100644 index 0000000..513a7be --- /dev/null +++ b/xen/arch/arm/platforms/exynos5.c @@ -0,0 +1,110 @@ +/* + * xen/arch/arm/platforms/exynos5.c + * + * Exynos5 specific settings + * + * Julien Grall <julien.grall@linaro.org> + * Copyright (c) 2013 Linaro Limited. + * + * 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 <xen/device_tree.h> +#include <xen/domain_page.h> +#include <xen/mm.h> +#include <xen/vmap.h> +#include <asm/platforms/exynos5.h> +#include <asm/platform.h> + +static int exynos5_init_time(void) +{ + uint32_t reg; + void __iomem *mct; + + BUILD_BUG_ON(EXYNOS5_MCT_G_TCON >= PAGE_SIZE); + + mct = ioremap_attr(EXYNOS5_MCT_BASE, PAGE_SIZE, PAGE_HYPERVISOR_NOCACHE); + if ( !mct ) + { + dprintk(XENLOG_ERR, "Unable to map MCT\n"); + return -ENOMEM; + } + + /* Enable timer on Exynos 5250 should probably be done by u-boot */ + reg = ioreadl(mct + EXYNOS5_MCT_G_TCON); + iowritel(mct + EXYNOS5_MCT_G_TCON, reg | EXYNOS5_MCT_G_TCON_START); + + iounmap(mct); + + return 0; +} + +/* Additional mappings for dom0 (Not in the DTS) */ +static int exynos5_specific_mapping(struct domain *d) +{ + /* Map the chip ID */ + map_mmio_regions(d, EXYNOS5_PA_CHIPID, EXYNOS5_PA_CHIPID + PAGE_SIZE - 1, + EXYNOS5_PA_CHIPID); + + /* Map the PWM region */ + map_mmio_regions(d, EXYNOS5_PA_TIMER, + EXYNOS5_PA_TIMER + (PAGE_SIZE * 2) - 1, + EXYNOS5_PA_TIMER); + + return 0; +} + +static void exynos5_reset(void) +{ + void __iomem *pmu; + + BUILD_BUG_ON(EXYNOS5_SWRESET >= PAGE_SIZE); + + pmu = ioremap_attr(EXYNOS5_PA_PMU, PAGE_SIZE, PAGE_HYPERVISOR_NOCACHE); + if ( !pmu ) + { + dprintk(XENLOG_ERR, "Unable to map PMU\n"); + return; + } + + iowritel(pmu + EXYNOS5_SWRESET, 1); + iounmap(pmu); +} + +static uint32_t exynos5_quirks(void) +{ + return PLATFORM_QUIRK_DOM0_MAPPING_11; +} + +static const char const *exynos5_dt_compat[] __initdata +{ + "samsung,exynos5250", + NULL +}; + +PLATFORM_START(exynos5, "SAMSUNG EXYNOS5") + .compatible = exynos5_dt_compat, + .init_time = exynos5_init_time, + .specific_mapping = exynos5_specific_mapping, + .reset = exynos5_reset, + .quirks = exynos5_quirks, +PLATFORM_END + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-arm/platforms/exynos5.h b/xen/include/asm-arm/platforms/exynos5.h new file mode 100644 index 0000000..ee5bdfa --- /dev/null +++ b/xen/include/asm-arm/platforms/exynos5.h @@ -0,0 +1,39 @@ +#ifndef __ASM_ARM_PLATFORMS_EXYNOS5_H +#define __ASM_ASM_PLATFORMS_EXYSNO5_H + +#define EXYNOS5_MCT_BASE 0x101c0000 +#define EXYNOS5_MCT_G_TCON 0x240 /* Relative to MCT_BASE */ +#define EXYNOS5_MCT_G_TCON_START (1 << 8) + +#define EXYNOS5_PA_CHIPID 0x10000000 +#define EXYNOS5_PA_TIMER 0x12dd0000 +/* Base address of system controller */ +#define EXYNOS5_PA_PMU 0x10040000 + +#define EXYNOS5_SWRESET 0x0400 /* Relative to PA_PMU */ + +#define S5P_PA_SYSRAM 0x02020000 + +/* Constants below is only used in assembly because the DTS is not yet parsed */ +#ifdef __ASSEMBLY__ + +/* GIC Base Address */ +#define EXYNOS5_GIC_BASE_ADDRESS 0x10480000 + +/* Timer''s frequency */ +#define EXYNOS5_TIMER_FREQUENCY (24 * 1000 * 1000) /* 24 MHz */ + +/* Arndale machine ID */ +#define MACH_TYPE_SMDK5250 3774 + +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_ARM_PLATFORMS_EXYNOS5_H */ +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 40/41] xen/arm: WORKAROUND Support kick cpus and switch to hypervisor for the exynos5
Use machine ID to know what is the current board. This value is only given to the first CPU by the bootloader. When the exynos 5 starts, there is only one CPU up. Xen needs to start the secondary cpu. The latter boots in secure mode. Theses modifications aim to be removed as soon as possible. It should be moved either in a platform specific boot-wrapper (which will be start before Xen), or in the bootloader (assuming U-Boot/Grub will support SMP). Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> Changes in v2: - Add WORKAROUND keyword in the title - Add fall-through comment when it''s needed - Setup the spis with a while loop instead of a do-while loop --- xen/arch/arm/arm32/head.S | 19 +++++++- xen/arch/arm/arm32/mode_switch.S | 75 +++++++++++++++++++++++------- xen/include/asm-arm/platforms/vexpress.h | 11 +++++ 3 files changed, 86 insertions(+), 19 deletions(-) diff --git a/xen/arch/arm/arm32/head.S b/xen/arch/arm/arm32/head.S index d452b59..ec7f640 100644 --- a/xen/arch/arm/arm32/head.S +++ b/xen/arch/arm/arm32/head.S @@ -76,7 +76,7 @@ past_zImage: cpsid aif /* Disable all interrupts */ /* Save the bootloader arguments in less-clobberable registers */ - /* No need to save r1 == Unused ARM-linux machine type */ + mov r5, r1 /* r5: ARM-linux machine type */ mov r8, r2 /* r8 := DTB base address */ /* Find out where we are */ @@ -119,11 +119,25 @@ boot_cpu: bl putn PRINT(" booting -\r\n") #endif + /* Secondary CPUs doesn''t have machine ID + * - Store machine ID on boot CPU + * - Load machine ID on secondary CPUs + * Machine ID is needed in kick_cpus and enter_hyp_mode */ + ldr r0, =machine_id /* VA of machine_id */ + add r0, r0, r10 /* PA of machine_id */ + teq r12, #0 + streq r5, [r0] /* On boot CPU save machine ID */ + ldrne r5, [r0] /* If non boot cpu r5 := machine ID */ /* Wake up secondary cpus */ teq r12, #0 bleq kick_cpus + PRINT("- Machine ID ") + mov r0, r5 + bl putn + PRINT(" -\r\n") + /* Check that this CPU has Hyp mode */ mrc CP32(r0, ID_PFR1) and r0, r0, #0xf000 /* Bits 12-15 define virt extensions */ @@ -403,6 +417,9 @@ putn: mov pc, lr #endif /* !EARLY_PRINTK */ +/* Place holder for machine ID */ +machine_id: .word 0x0 + /* * Local variables: * mode: ASM diff --git a/xen/arch/arm/arm32/mode_switch.S b/xen/arch/arm/arm32/mode_switch.S index d6741d0..c92a1cf 100644 --- a/xen/arch/arm/arm32/mode_switch.S +++ b/xen/arch/arm/arm32/mode_switch.S @@ -20,14 +20,21 @@ #include <asm/config.h> #include <asm/page.h> #include <asm/platforms/vexpress.h> +#include <asm/platforms/exynos5.h> #include <asm/asm_defns.h> #include <asm/gic.h> - -/* XXX: Versatile Express specific code */ -/* wake up secondary cpus */ +/* Wake up secondary cpus + * This code relies on Machine ID and only works for Vexpress and the Arndale + * TODO: Move this code either later (via platform specific desc) or in a bootwrapper + * r5: Machine ID + * Clobber r0 r2 */ .globl kick_cpus kick_cpus: + ldr r0, =MACH_TYPE_SMDK5250 + teq r5, r0 /* Are we running on the arndale? */ + beq kick_cpus_arndale + /* otherwise versatile express */ /* write start paddr to v2m sysreg FLAGSSET register */ ldr r0, =(V2M_SYS_MMIO_BASE) /* base V2M sysreg MMIO address */ dsb @@ -38,8 +45,20 @@ kick_cpus: add r2, r2, r10 str r2, [r0, #(V2M_SYS_FLAGSSET)] dsb + ldr r2, =V2M_GIC_BASE_ADDRESS /* r2 := VE gic base address */ + b kick_cpus_sgi +kick_cpus_arndale: + /* write start paddr to CPU 1 sysreg register */ + ldr r0, =(S5P_PA_SYSRAM) + ldr r2, =start + add r2, r2, r10 + str r2, [r0] + dsb + ldr r2, =EXYNOS5_GIC_BASE_ADDRESS /* r2 := Exynos5 gic base address */ +kick_cpus_sgi: /* send an interrupt */ - ldr r0, =(GIC_BASE_ADDRESS + GIC_DR_OFFSET) /* base GICD MMIO address */ + ldr r0, =GIC_DR_OFFSET /* GIC distributor offset */ + add r0, r2 /* r0 := r0 + gic base address */ mov r2, #0x1 str r2, [r0, #(GICD_CTLR * 4)] /* enable distributor */ mov r2, #0xfe0000 @@ -51,13 +70,15 @@ kick_cpus: /* Get up a CPU into Hyp mode. Clobbers r0-r3. * - * Expects r12 == CPU number + * r5: Machine ID + * r12: CPU number * - * This code is specific to the VE model, and not intended to be used + * This code is specific to the VE model/Arndale, and not intended to be used * on production systems. As such it''s a bit hackier than the main * boot code in head.S. In future it will be replaced by better * integration with the bootloader/firmware so that Xen always starts - * in Hyp mode. */ + * in Hyp mode. + * Clobber r0 - r4 */ .globl enter_hyp_mode enter_hyp_mode: @@ -68,33 +89,51 @@ enter_hyp_mode: orr r0, r0, #0xb1 /* Set SCD, AW, FW and NS */ bic r0, r0, #0xe /* Clear EA, FIQ and IRQ */ mcr CP32(r0, SCR) + + ldr r2, =MACH_TYPE_SMDK5250 /* r4 := Arndale machine ID */ + /* By default load Arndale defaults values */ + ldr r0, =EXYNOS5_TIMER_FREQUENCY /* r0 := timer''s frequency */ + ldr r1, =EXYNOS5_GIC_BASE_ADDRESS /* r1 := GIC base address */ + /* If it''s not the Arndale machine ID, load VE values */ + teq r5, r2 + ldrne r0, =V2M_TIMER_FREQUENCY + ldrne r1, =V2M_GIC_BASE_ADDRESS + /* Ugly: the system timer''s frequency register is only * programmable in Secure state. Since we don''t know where its * memory-mapped control registers live, we can''t find out the - * right frequency. Use the VE model''s default frequency here. */ - ldr r0, =0x5f5e100 /* 100 MHz */ + * right frequency. */ mcr CP32(r0, CNTFRQ) ldr r0, =0x40c00 /* SMP, c11, c10 in non-secure mode */ mcr CP32(r0, NSACR) - mov r0, #GIC_BASE_ADDRESS - add r0, r0, #GIC_DR_OFFSET + + add r0, r1, #GIC_DR_OFFSET /* Disable the GIC distributor, on the boot CPU only */ - mov r1, #0 + mov r4, #0 teq r12, #0 /* Is this the boot CPU? */ - streq r1, [r0] + streq r4, [r0] /* Continuing ugliness: Set up the GIC so NS state owns interrupts, * The first 32 interrupts (SGIs & PPIs) must be configured on all * CPUs while the remainder are SPIs and only need to be done one, on * the boot CPU. */ add r0, r0, #0x80 /* GICD_IGROUP0 */ mov r2, #0xffffffff /* All interrupts to group 1 */ - teq r12, #0 /* Boot CPU? */ str r2, [r0] /* Interrupts 0-31 (SGI & PPI) */ - streq r2, [r0, #4] /* Interrupts 32-63 (SPI) */ - streq r2, [r0, #8] /* Interrupts 64-95 (SPI) */ + teq r12, #0 /* Boot CPU? */ + bne skip_spis /* Don''t route SPIs on secondary CPUs */ + + add r4, r1, #GIC_DR_OFFSET + ldr r4, [r4, #4] /* r4 := Interrupt Controller Type Reg */ + and r4, r4, #GICD_TYPE_LINES /* r4 := number of SPIs */ +1: teq r4, #0 + beq skip_spis + add r0, r0, #4 /* Go to the new group */ + str r2, [r0] /* Update the group */ + sub r4, r4, #1 + b 1b +skip_spis: /* Disable the GIC CPU interface on all processors */ - mov r0, #GIC_BASE_ADDRESS - add r0, r0, #GIC_CR_OFFSET + add r0, r1, #GIC_CR_OFFSET mov r1, #0 str r1, [r0] /* Must drop priority mask below 0x80 before entering NS state */ diff --git a/xen/include/asm-arm/platforms/vexpress.h b/xen/include/asm-arm/platforms/vexpress.h index 5cf3aba..982a293 100644 --- a/xen/include/asm-arm/platforms/vexpress.h +++ b/xen/include/asm-arm/platforms/vexpress.h @@ -32,6 +32,17 @@ int vexpress_syscfg(int write, int function, int device, uint32_t *data); #endif +/* Constants below is only used in assembly because the DTS is not yet parsed */ +#ifdef __ASSEMBLY__ + +/* GIC base address */ +#define V2M_GIC_BASE_ADDRESS 0x2c000000 + +/* Timer''s frequency */ +#define V2M_TIMER_FREQUENCY 0x5f5e100 /* 100 Mhz */ + +#endif /* __ASSEMBLY__ */ + #endif /* __ASM_ARM_PLATFORMS_VEXPRESS_H */ /* * Local variables: -- 1.7.10.4
Julien Grall
2013-May-10 02:18 UTC
[PATCH V3 41/41] xen/arm64: Remove hardcoded value for gic in assembly code
- arm64: use V2M_GIC_BASE_ADDRESS - only expose GIC_*_ADDRESS to assembly. The C code uses base addresses provide by the device tree Signed-off-by: Julien Grall <julien.grall@linaro.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> Changes in v2: - Remove stray gi"t diff" --- xen/arch/arm/arm64/mode_switch.S | 7 ++++--- xen/include/asm-arm/config.h | 8 ++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/xen/arch/arm/arm64/mode_switch.S b/xen/arch/arm/arm64/mode_switch.S index 4c38181..d115706 100644 --- a/xen/arch/arm/arm64/mode_switch.S +++ b/xen/arch/arm/arm64/mode_switch.S @@ -21,6 +21,7 @@ #include <asm/config.h> #include <asm/page.h> #include <asm/asm_defns.h> +#include <asm/platforms/vexpress.h> /* Get up a CPU into EL2. Clobbers x0-x3. * @@ -53,18 +54,18 @@ enter_el2_mode: */ cbnz x22, 1f - ldr x1, =(GIC_BASE_ADDRESS+GIC_DR_OFFSET) // GICD_CTLR + ldr x1, =(V2M_GIC_BASE_ADDRESS+GIC_DR_OFFSET) // GICD_CTLR mov w0, #3 // EnableGrp0 | EnableGrp1 str w0, [x1] -1: ldr x1, =(GIC_BASE_ADDRESS+GIC_DR_OFFSET+0x80) // GICD_IGROUPR +1: ldr x1, =(V2M_GIC_BASE_ADDRESS+GIC_DR_OFFSET+0x80) // GICD_IGROUPR mov w0, #~0 // Grp1 interrupts str w0, [x1], #4 b.ne 2f // Only local interrupts for secondary CPUs str w0, [x1], #4 str w0, [x1], #4 -2: ldr x1, =(GIC_BASE_ADDRESS+GIC_CR_OFFSET) // GICC_CTLR +2: ldr x1, =(V2M_GIC_BASE_ADDRESS+GIC_CR_OFFSET) // GICC_CTLR ldr w0, [x1] mov w0, #3 // EnableGrp0 | EnableGrp1 str w0, [x1] diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h index 6414c89..e3cfaf1 100644 --- a/xen/include/asm-arm/config.h +++ b/xen/include/asm-arm/config.h @@ -141,12 +141,16 @@ extern unsigned long frametable_virt_end; #define watchdog_disable() ((void)0) #define watchdog_enable() ((void)0) -/* Board-specific: base address of GIC + its regs */ -#define GIC_BASE_ADDRESS 0x2c000000 +#ifdef __ASSEMBLY__ +/* Board-specific: regs base address for the GIC + * Theses constants are only intend to be used in assembly file + * because the DT is not yet parsed. + */ #define GIC_DR_OFFSET 0x1000 #define GIC_CR_OFFSET 0x2000 #define GIC_HR_OFFSET 0x4000 /* Guess work http://lists.infradead.org/pipermail/linux-arm-kernel/2011-September/064219.html */ #define GIC_VR_OFFSET 0x6000 /* Virtual Machine CPU interface) */ +#endif /* __ASSEMBLY__ */ #endif /* __ARM_CONFIG_H__ */ /* -- 1.7.10.4
Jan Beulich
2013-May-10 08:23 UTC
Re: [PATCH V3 10/41] xen/mm: Align virtual address on PAGE_SIZE in iounmap
>>> On 10.05.13 at 04:17, Julien Grall <julien.grall@linaro.org> wrote: > ioremap function can unlikely return an unaligned virtual address if > the physical address itself is unaligned on a page size. > > Signed-off-by: Julien Grall <julien.grall@linaro.org>If you used "unsigned long" instead of "vaddr_t" (I don''t think this would build on x86 without doing so, as vaddr-t appear to be ARM specific): Acked-by: Jan Beulich <jbeulich@suse.com>> --- a/xen/include/xen/vmap.h > +++ b/xen/include/xen/vmap.h > @@ -15,7 +15,9 @@ void __iomem *ioremap(paddr_t, size_t); > > static inline void iounmap(void __iomem *va) > { > - vunmap((void __force *)va); > + vaddr_t addr = (vaddr_t)(void __force *)va; > + > + vunmap((void *)(addr & PAGE_MASK)); > } > > void vm_init(void);
Ian Campbell
2013-May-10 08:40 UTC
Re: [PATCH V3 07/41] xen/arm: Switch to SYS_STATE_boot right after console setup
On Fri, 2013-05-10 at 03:17 +0100, Julien Grall wrote:> Signed-off-by: Julien Grall <julien.grall@linaro.org>Acked-by: Ian Campbell <ian.campbell@citrix.com>
On Fri, 2013-05-10 at 03:17 +0100, Julien Grall wrote:> Signed-off-by: Julien Grall <julien.grall@linaro.org>Acked-by: Ian Campbell <ian.campbell@citrix.com>
Ian Campbell
2013-May-10 08:54 UTC
Re: [PATCH V3 14/41] xen/arm: Create a hierarchical device tree
On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote:> +static void dt_printk(const char *fmt, ...) > +{ > + static char buf[512]; > + va_list args; > + > + va_start(args, fmt); > + > + if ( system_state == SYS_STATE_early_boot ) > + early_vprintk(fmt, args); > + else > + { > + vsnprintf(buf, sizeof(buf), fmt, args); > + printk(buf); > + } > + va_end(args); > +}It''s unfortunate that there is no vprintk, since printk is just going to do a nop reformatting into yet another buffer. I wonder what Keir thinks about refactoring printk to allow this? It looks like it would be trivial. Of course having done that then we might almost as well go back to the longer term plan of adding an #ifdef EARLY_PRINTK with this check to printk itself. If we don''t want to do any of this for 4.3 than I think a macro might actually be better here: #define DT_PRINTK(fmt,...) \ if ( system_state == SYS_STATE_early_boot ) \ early_vprintk(fmt, __VA_ARGS__); \ else \ printk(fmt, __VAR_ARGS__); Normally I''d be against using a macro, but avoiding the double buffer seems worth it. Ian.
Ian Campbell
2013-May-10 08:54 UTC
Re: [PATCH V3 15/41] xen/arm: Add helpers to use the device tree
On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote:> Signed-off-by: Julien Grall <julien.grall@linaro.org>Acked-by: Ian Campbell <ian.campbell@citrix.com>
Ian Campbell
2013-May-10 09:08 UTC
Re: [PATCH V3 09/41] xen/arm: Extend create_xen_entries prototype to take mapping attribute
On Fri, 2013-05-10 at 03:17 +0100, Julien Grall wrote:> Signed-off-by: Julien Grall <julien.grall@linaro.org> > --- > xen/arch/arm/mm.c | 9 +++++---- > 1 file changed, 5 insertions(+), 4 deletions(-) > > diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c > index 2836cbb..96297d3 100644 > --- a/xen/arch/arm/mm.c > +++ b/xen/arch/arm/mm.c > @@ -631,7 +631,8 @@ enum xenmap_operation { > static int create_xen_entries(enum xenmap_operation op, > unsigned long virt, > unsigned long mfn, > - unsigned long nr_mfns) > + unsigned long nr_mfns, > + unsigned int flags)Please can we call this "attr" or "ai" to reflect the field which it goes in. Otherwise: Acked-by: Ian Campbell <ian.campbell@citrix.com>
On Fri, 2013-05-10 at 03:17 +0100, Julien Grall wrote:> Map physical range in virtual memory with a specific mapping attribute. > Also add new mapping attributes for ARM: PAGE_HYPERVISOR_NOCACHE > and PAGE_HYPERVISOR_WC.I think it would be useful, e.g. if when we want to reuse Linux SYS MMU code, to follow the Linux convention here which is ioremap_(no)cache, ioremap_wc etc.> diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c > index 29447ef..a667db4 100644 > --- a/xen/arch/arm/setup.c > +++ b/xen/arch/arm/setup.c > @@ -429,6 +429,8 @@ void __init start_xen(unsigned long boot_phys_offset, > setup_pagetables(boot_phys_offset, get_xen_paddr()); > setup_mm(fdt_paddr, fdt_size); > > + vm_init(); > + > #ifdef EARLY_UART_ADDRESS > /* TODO Need to get device tree or command line for UART address */ > pl011_init(0, FIXMAP_ADDR(FIXMAP_CONSOLE)); > @@ -483,8 +485,6 @@ void __init start_xen(unsigned long boot_phys_offset, > > console_init_postirq(); > > - vm_init(); > - > do_presmp_initcalls(); > > for_each_present_cpu ( i )This movement seems to be unrelated to the purpose of this patch?
> @@ -59,6 +59,8 @@ > #define DEV_CACHED WRITEBACK > > #define PAGE_HYPERVISOR (MATTR_MEM)I should have noticed this when Stefano''s original vmap patch went in but this is wrong. MATTR_* are Second stage paging attributes (i.e. p2m ones) and are not suitable for the Xen first stage page tables. MATTR_MEM == 0xf, which I think will be truncated to 0x7 when written to the ai field in the PT (which is only 3 bits) and so this is equivalent to using WRITEALLOC. I suspect that isn''t at all desirable... Stefano, can you fix this please? Ian.
On Fri, 2013-05-10 at 03:17 +0100, Julien Grall wrote:> In the commit "xen/arm: Introduce ioremap_attr", vamp initialization has been > moved earlier in the boot process.Any reason not to make that move as part of this change? This pushes vm_init before a whole bunch of other setup -- has this been checked for safety?
Ian Campbell
2013-May-10 09:23 UTC
Re: [PATCH V3 22/41] xen/arm: Use hierarchical device tree to retrieve GIC information
On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote:> - Remove early parsing for GIC addresses > - Remove hard coded maintenance IRQ number > > Signed-off-by: Julien Grall <julien.grall@linaro.org>Acked-by: Ian Campbell <ian.campbell@citrix.com>
Ian Campbell
2013-May-10 09:23 UTC
Re: [PATCH V3 23/41] xen/arm: Retrieve timer interrupts from the device tree
On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote:> Signed-off-by: Julien Grall <julien.grall@linaro.org>Acked-by: Ian Campbell <ian.campbell@citrix.com>
Ian Campbell
2013-May-10 09:25 UTC
Re: [PATCH V3 25/41] xen/arm: Don''t hardcode virtual timer IRQs
On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote:> Define virtual timer IRQs per VCPU > > Signed-off-by: Julien Grall <julien.grall@linaro.org>Acked-by: Ian Campbell <ian.campbell@citrix.com>
Ian Campbell
2013-May-10 09:32 UTC
Re: [PATCH V3 28/41] xen/arm: Add generic UART to get the device in the device tree
On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote:> This generic UART will find the right UART via xen command line > with dtuart=myserial. > > "myserial" is the alias of the UART in the device tree. Xen will retrieve > the information via the device tree and call the initialization function for > this specific UART thanks to the device API. > > Signed-off-by: Julien Grall <julien.grall@linaro.org>> CC: keir@xen.org > > Changes in v3: > - Rename arm-uart.c (resp. arm_uart_init) to dt-uart.c (resp. dt_uart_init) > - Remove serial_arm_defaults and replace by a string options > - Let the specific UART driver to get its base address in the DT > - Add SERHND_DTUART. The value is stolen to SERHND_COM1. Bump the > value would needs some rework...This is because of the need to encode the value in 2 bits? Due to SERHND_IDX etc, is the rework more than simply changing the mask and bumping the flags up and increasing the size of the com array? I''m OK with this for 4.3 if Keir is: Acked-by: Ian Campbell <ian.campbell@citrix.com>
Ian Campbell
2013-May-10 09:35 UTC
Re: [PATCH V3 29/41] xen/arm: Use device tree API in pl011 UART driver
On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote:> Allow UART driver to retrieve all its information in the device tree. > It''s possible to choose the pl011 driver via the Xen command line. > > Signed-off-by: Julien Grall <julien.grall@linaro.org>Acked-by: Ian Campbell <ian.campbell@citrix.com> +static int __init pl011_uart_init(struct dt_device_node *dev, + const void *data) ... might be worth warning if (char *)data != ""? Ian.
Ian Campbell
2013-May-10 09:37 UTC
Re: [PATCH V3 30/41] xen/arm: Use the device tree to map the address range and IRQ to dom0
On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote:> - gic_route_irq_to_guest takes a dt_irq instead of an IRQ number > - remove hardcoded address/IRQ > > Signed-off-by: Julien Grall <julien.grall@linaro.org> > > Changes in v3: > - Rename dt_irq_is_level_trigger to dt_irq_is_level_trigerredSorry, this is my fault, it should be "triggered" Oh, but it''s right in the code, yay! Acked-by: Ian Campbell <ian.campbell@citrix.com>
Ian Campbell
2013-May-10 09:41 UTC
Re: [PATCH V3 31/41] xen/arm: Allow Xen to run on multiple platform without recompilation
On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote:> Xen can include various platform support (ie: exynos5, versatile express...) > and choose during boot time a set of callbacks for the current board. > These callbacks will be called in places where each board can have specific > code. For the moment the callbacks are: > - platform_init: additional initialization for the platform > - platform_init_time: some platform (ie: Exynos 5) needs to initialize > the timer with an uncommon way > - platform_specific_mapping: add mapping to dom0 which are not specified > in the device tree > - platform_reset: reset the platform > - platform_poweroff: poweroff the platform > - platform_quirks: list of quirks for a specific board. > > Signed-off-by: Julien Grall <julien.grall@linaro.org>Acked-by: Ian Campbell <ian.campbell@citrix.com> [...]> +/* List of possible platform */ > +static void dump_platform_table(void) > +{ > + const struct platform_desc *p; > + > + printk("Available platform support:\n"); > + > + for ( p = _splatform; p != _eplatform; p++ ) > + printk(" - %s\n", p->name); > +} > + > +int __init platform_init(void) > +{[...]> + /* We don''t have specific operations for this platform */ > + if ( platform == _eplatform ) > + { > + /* TODO: List compatible */ > + printk(XENLOG_WARNING "WARNING: Unrecognized/unsupported device tree " > + "compatible list\n"); > + dump_platform_table();Looks like the TODO is obsolete? Ian.
Ian Campbell
2013-May-10 09:44 UTC
Re: [PATCH V3 33/41] xen/arm: Add versatile express platform
> @@ -91,6 +93,47 @@ out: > } > > /* > + * TODO: Get base address from the device tree > + * See arm,vexpress-reset node > + */ > +static void vexpress_reset(void) > +{ > + void __iomem *base; > + void __iomem *sp810; > + > + /* Use the SP810 system controller to force a reset */ > + base = ioremap_attr(SP810_ADDRESS & PAGE_MASK, PAGE_SIZE, > + PAGE_HYPERVISOR_NOCACHE); > + if ( !base ) > + { > + dprintk(XENLOG_ERR, "Unable to map SP810\n"); > + return; > + } > + > + sp810 = base + (SP810_ADDRESS & ~PAGE_MASK);Didn''t I see you making vunmap, which iounmap is based on, take care of the page offsets itself in an earlier patch? Or is that not the reason you are going through the base + offset dance?> + > + /* switch to slow mode */ > + iowritel(sp810, 0x3); > + dsb(); isb(); > + /* writing any value to SCSYSSTAT reg will reset the system */ > + iowritel(sp810 + 4, 0x1); > + dsb(); isb();Hopefully we don''t get here ;-)> + iounmap(base); > +} > +
Ian Campbell
2013-May-10 09:46 UTC
Re: [PATCH V3 36/41] xen/arm: Don''t use pl011 UART by default for early printk
On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote:> Add CONFIG_EARLY_PRINTK options in configs/arm{32,64}.mk to let the user > to choose if he wants to have early output, ie before the console is initialized. > > This code is specific for each UART. When CONFIG_EARLY_PRINTK is enabled, > Xen will only be able to run on a board with this UART. > > If a developper wants to add support for a new UART, he must implement the > following assembly macro/define: > - EALY_UART_BASE_ADDRESS: variable which contains the physical base address > for the UART > - early_uart_init: initialize the UART > - early_uart_ready: check and wait until the UART can transmit a new > character > - early_uart_transmit: transmit a character > > For more details about the parameters of each function, > see arm{32,64}/debug-pl011.inc comments. > > Signed-off-by: Julien Grall <julien.grall@linaro.org>Acked-by: Ian Campbell <ian.campbell@citrix.com>
George Dunlap
2013-May-10 09:48 UTC
Re: [PATCH V3 00/41] Support multiple ARM platform in Xen
On Fri, May 10, 2013 at 3:17 AM, Julien Grall <julien.grall@linaro.org> wrote:> Hi, > > This is the third version of the patch series. It''s rebased on the lastest > master, with 3 others patches.If this version isn''t ready for acceptance yet, is there any way we could at least accept some of these patches? A 40+ patch series is awfully long to be reposted every few days... -George
Ian Campbell
2013-May-10 09:49 UTC
Re: [PATCH V3 37/41] xen/arm: Add exynos 4210 UART support
On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote:> From: Anthony PERARD <anthony.perard@citrix.com> > > Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> > Signed-off-by: Julien Grall <julien.grall@linaro.org> > [...]> + if ( status & UINTM_ERROR ) > + { > + uint32_t error_bit; > + > + error_bit = exynos4210_read(uart, UERSTAT); > + > + if ( error_bit & UERSTAT_OVERRUN ) > + dprintk(XENLOG_ERR, "uart: overrun error\n"); > + if ( error_bit & UERSTAT_PARITY ) > + dprintk(XENLOG_ERR, "uart: parity error\n"); > + if ( error_bit & UERSTAT_FRAME ) > + dprintk(XENLOG_ERR, "uart: frame error\n"); > + if ( error_bit & UERSTAT_BREAK ) > + dprintk(XENLOG_ERR, "uart: break detected\n");I''m curious to know where these dprintk''s go ;-), but: Acked-by: Ian Campbell <ian.campbell@citrix.com> Ian.
Ian Campbell
2013-May-10 09:49 UTC
Re: [PATCH V3 38/41] xen/arm: Add Exynos 4210 UART support for early printk
On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote:> From: Anthony PERARD <anthony.perard@citrix.com> > > Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> > Signed-off-by: Julien Grall <julien.grall@linaro.org>Acked-by: Ian Campbell <ian.campbell@citrix.com>
Ian Campbell
2013-May-10 09:50 UTC
Re: [PATCH V3 39/41] xen/arm: Add platform specific code for the exynos5
On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote:> Signed-off-by: Julien Grall <julien.grall@linaro.org>Acked-by: Ian Campbell <ian.campbell@citrix.com>
On 05/10/2013 10:13 AM, Ian Campbell wrote:> On Fri, 2013-05-10 at 03:17 +0100, Julien Grall wrote: >> Map physical range in virtual memory with a specific mapping attribute. >> Also add new mapping attributes for ARM: PAGE_HYPERVISOR_NOCACHE >> and PAGE_HYPERVISOR_WC. > > I think it would be useful, e.g. if when we want to reuse Linux SYS MMU > code, to follow the Linux convention here which is ioremap_(no)cache, > ioremap_wc etc.Is macro for those functions is sufficient?>> diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c >> index 29447ef..a667db4 100644 >> --- a/xen/arch/arm/setup.c >> +++ b/xen/arch/arm/setup.c >> @@ -429,6 +429,8 @@ void __init start_xen(unsigned long boot_phys_offset, >> setup_pagetables(boot_phys_offset, get_xen_paddr()); >> setup_mm(fdt_paddr, fdt_size); >> >> + vm_init(); >> + >> #ifdef EARLY_UART_ADDRESS >> /* TODO Need to get device tree or command line for UART address */ >> pl011_init(0, FIXMAP_ADDR(FIXMAP_CONSOLE)); >> @@ -483,8 +485,6 @@ void __init start_xen(unsigned long boot_phys_offset, >> >> console_init_postirq(); >> >> - vm_init(); >> - >> do_presmp_initcalls(); >> >> for_each_present_cpu ( i ) > > This movement seems to be unrelated to the purpose of this patch?vm_init initialize vmap code, which is used by ioremap_attr. The function was called to late. -- Julien
On Fri, 2013-05-10 at 13:00 +0100, Julien Grall wrote:> On 05/10/2013 10:13 AM, Ian Campbell wrote: > > > On Fri, 2013-05-10 at 03:17 +0100, Julien Grall wrote: > >> Map physical range in virtual memory with a specific mapping attribute. > >> Also add new mapping attributes for ARM: PAGE_HYPERVISOR_NOCACHE > >> and PAGE_HYPERVISOR_WC. > > > > I think it would be useful, e.g. if when we want to reuse Linux SYS MMU > > code, to follow the Linux convention here which is ioremap_(no)cache, > > ioremap_wc etc.> Is macro for those functions is sufficient?If need be that''s fine, is there anything which breaks if you use a static inline?> >> diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c > >> index 29447ef..a667db4 100644 > >> --- a/xen/arch/arm/setup.c > >> +++ b/xen/arch/arm/setup.c > >> @@ -429,6 +429,8 @@ void __init start_xen(unsigned long boot_phys_offset, > >> setup_pagetables(boot_phys_offset, get_xen_paddr()); > >> setup_mm(fdt_paddr, fdt_size); > >> > >> + vm_init(); > >> + > >> #ifdef EARLY_UART_ADDRESS > >> /* TODO Need to get device tree or command line for UART address */ > >> pl011_init(0, FIXMAP_ADDR(FIXMAP_CONSOLE)); > >> @@ -483,8 +485,6 @@ void __init start_xen(unsigned long boot_phys_offset, > >> > >> console_init_postirq(); > >> > >> - vm_init(); > >> - > >> do_presmp_initcalls(); > >> > >> for_each_present_cpu ( i ) > > > > This movement seems to be unrelated to the purpose of this patch? > > vm_init initialize vmap code, which is used by ioremap_attr. The > function was called to late.But this patch doesn''t add any callers, I presume that a caller will subsequently be introduce which then requires it to be earlier. I think it would be sufficient to simply mention this upcoming caller in the commit message and refer to the need to init sooner. Ian.
On 05/10/2013 10:21 AM, Ian Campbell wrote:> On Fri, 2013-05-10 at 03:17 +0100, Julien Grall wrote: >> In the commit "xen/arm: Introduce ioremap_attr", vamp initialization has been >> moved earlier in the boot process. > > Any reason not to make that move as part of this change?No. I will merge with the previous patch.> This pushes vm_init before a whole bunch of other setup -- has this been > checked for safety?I didn''t find any reason to not move vm_init. It only relies on memory, which is initialized by setup_mm. -- Julien
On 05/10/2013 01:07 PM, Ian Campbell wrote:> On Fri, 2013-05-10 at 13:00 +0100, Julien Grall wrote: >> On 05/10/2013 10:13 AM, Ian Campbell wrote: >> >>> On Fri, 2013-05-10 at 03:17 +0100, Julien Grall wrote: >>>> Map physical range in virtual memory with a specific mapping attribute. >>>> Also add new mapping attributes for ARM: PAGE_HYPERVISOR_NOCACHE >>>> and PAGE_HYPERVISOR_WC. >>> >>> I think it would be useful, e.g. if when we want to reuse Linux SYS MMU >>> code, to follow the Linux convention here which is ioremap_(no)cache, >>> ioremap_wc etc. > >> Is macro for those functions is sufficient? > > If need be that''s fine, is there anything which breaks if you use a > static inline?I will use it on the next version of this patch.>>>> diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c >>>> index 29447ef..a667db4 100644 >>>> --- a/xen/arch/arm/setup.c >>>> +++ b/xen/arch/arm/setup.c >>>> @@ -429,6 +429,8 @@ void __init start_xen(unsigned long boot_phys_offset, >>>> setup_pagetables(boot_phys_offset, get_xen_paddr()); >>>> setup_mm(fdt_paddr, fdt_size); >>>> >>>> + vm_init(); >>>> + >>>> #ifdef EARLY_UART_ADDRESS >>>> /* TODO Need to get device tree or command line for UART address */ >>>> pl011_init(0, FIXMAP_ADDR(FIXMAP_CONSOLE)); >>>> @@ -483,8 +485,6 @@ void __init start_xen(unsigned long boot_phys_offset, >>>> >>>> console_init_postirq(); >>>> >>>> - vm_init(); >>>> - >>>> do_presmp_initcalls(); >>>> >>>> for_each_present_cpu ( i ) >>> >>> This movement seems to be unrelated to the purpose of this patch? >> >> vm_init initialize vmap code, which is used by ioremap_attr. The >> function was called to late. > > But this patch doesn''t add any callers, I presume that a caller will > subsequently be introduce which then requires it to be earlier. I think > it would be sufficient to simply mention this upcoming caller in the > commit message and refer to the need to init sooner.I will mention it on the commit message. -- Julien
Julien Grall
2013-May-10 12:40 UTC
Re: [PATCH V3 10/41] xen/mm: Align virtual address on PAGE_SIZE in iounmap
On 05/10/2013 09:23 AM, Jan Beulich wrote:>>>> On 10.05.13 at 04:17, Julien Grall <julien.grall@linaro.org> wrote: >> ioremap function can unlikely return an unaligned virtual address if >> the physical address itself is unaligned on a page size. >> >> Signed-off-by: Julien Grall <julien.grall@linaro.org> > > If you used "unsigned long" instead of "vaddr_t" (I don''t think this > would build on x86 without doing so, as vaddr-t appear to be ARM > specific):Right. I will fix it.> Acked-by: Jan Beulich <jbeulich@suse.com> > >> --- a/xen/include/xen/vmap.h >> +++ b/xen/include/xen/vmap.h >> @@ -15,7 +15,9 @@ void __iomem *ioremap(paddr_t, size_t); >> >> static inline void iounmap(void __iomem *va) >> { >> - vunmap((void __force *)va); >> + vaddr_t addr = (vaddr_t)(void __force *)va; >> + >> + vunmap((void *)(addr & PAGE_MASK)); >> } >> >> void vm_init(void); > >-- Julien
Julien Grall
2013-May-10 12:47 UTC
Re: [PATCH V3 28/41] xen/arm: Add generic UART to get the device in the device tree
On 05/10/2013 10:32 AM, Ian Campbell wrote:> On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote: >> This generic UART will find the right UART via xen command line >> with dtuart=myserial. >> >> "myserial" is the alias of the UART in the device tree. Xen will retrieve >> the information via the device tree and call the initialization function for >> this specific UART thanks to the device API. >> >> Signed-off-by: Julien Grall <julien.grall@linaro.org> > > >> CC: keir@xen.org >> >> Changes in v3: >> - Rename arm-uart.c (resp. arm_uart_init) to dt-uart.c (resp. dt_uart_init) >> - Remove serial_arm_defaults and replace by a string options >> - Let the specific UART driver to get its base address in the DT >> - Add SERHND_DTUART. The value is stolen to SERHND_COM1. Bump the >> value would needs some rework... > > This is because of the need to encode the value in 2 bits? Due to > SERHND_IDX etc, is the rework more than simply changing the mask and > bumping the flags up and increasing the size of the com array?The console code is able to handle one serial at time so, I don''t understand why we need to "waste" another memory space for a new kind of serial.> I''m OK with this for 4.3 if Keir is: > > Acked-by: Ian Campbell <ian.campbell@citrix.com> > >-- Julien
Julien Grall
2013-May-10 12:50 UTC
Re: [PATCH V3 00/41] Support multiple ARM platform in Xen
On 05/10/2013 10:48 AM, George Dunlap wrote:> On Fri, May 10, 2013 at 3:17 AM, Julien Grall <julien.grall@linaro.org> wrote: >> Hi, >> >> This is the third version of the patch series. It''s rebased on the lastest >> master, with 3 others patches. > > If this version isn''t ready for acceptance yet, is there any way we > could at least accept some of these patches? A 40+ patch series is > awfully long to be reposted every few days...I think patches 1-8 can be applied. For the next patch series, I will only resend the modified patches. -- Julien
Julien Grall
2013-May-10 13:00 UTC
Re: [PATCH V3 37/41] xen/arm: Add exynos 4210 UART support
On 05/10/2013 10:49 AM, Ian Campbell wrote:> On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote: >> From: Anthony PERARD <anthony.perard@citrix.com> >> >> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> >> Signed-off-by: Julien Grall <julien.grall@linaro.org> >> [...] > >> + if ( status & UINTM_ERROR ) >> + { >> + uint32_t error_bit; >> + >> + error_bit = exynos4210_read(uart, UERSTAT); >> + >> + if ( error_bit & UERSTAT_OVERRUN ) >> + dprintk(XENLOG_ERR, "uart: overrun error\n"); >> + if ( error_bit & UERSTAT_PARITY ) >> + dprintk(XENLOG_ERR, "uart: parity error\n"); >> + if ( error_bit & UERSTAT_FRAME ) >> + dprintk(XENLOG_ERR, "uart: frame error\n"); >> + if ( error_bit & UERSTAT_BREAK ) >> + dprintk(XENLOG_ERR, "uart: break detected\n"); > > I''m curious to know where these dprintk''s go ;-), but:Directly on the UART :/. But I''m not sure if UART errors are fatal or not.> Acked-by: Ian Campbell <ian.campbell@citrix.com>-- Julien
Ian Campbell
2013-May-10 13:34 UTC
Re: [PATCH V3 00/41] Support multiple ARM platform in Xen
On Fri, 2013-05-10 at 13:50 +0100, Julien Grall wrote:> On 05/10/2013 10:48 AM, George Dunlap wrote: > > > On Fri, May 10, 2013 at 3:17 AM, Julien Grall <julien.grall@linaro.org> wrote: > >> Hi, > >> > >> This is the third version of the patch series. It''s rebased on the lastest > >> master, with 3 others patches. > > > > If this version isn''t ready for acceptance yet, is there any way we > > could at least accept some of these patches? A 40+ patch series is > > awfully long to be reposted every few days... > > > I think patches 1-8 can be applied. For the next patch series, I will > only resend the modified patches.Applied 1-7. #8 was "xen/arm: Export early_vprintk" which I think changes I suggested to the dt_printk patch have made unnecessary. Ian.
Julien Grall
2013-May-10 14:00 UTC
Re: [PATCH V3 33/41] xen/arm: Add versatile express platform
On 05/10/2013 10:44 AM, Ian Campbell wrote:>> @@ -91,6 +93,47 @@ out: >> } >> >> /* >> + * TODO: Get base address from the device tree >> + * See arm,vexpress-reset node >> + */ >> +static void vexpress_reset(void) >> +{ >> + void __iomem *base; >> + void __iomem *sp810; >> + >> + /* Use the SP810 system controller to force a reset */ >> + base = ioremap_attr(SP810_ADDRESS & PAGE_MASK, PAGE_SIZE, >> + PAGE_HYPERVISOR_NOCACHE); >> + if ( !base ) >> + { >> + dprintk(XENLOG_ERR, "Unable to map SP810\n"); >> + return; >> + } >> + >> + sp810 = base + (SP810_ADDRESS & ~PAGE_MASK); > > Didn''t I see you making vunmap, which iounmap is based on, take care of > the page offsets itself in an earlier patch? Or is that not the reason > you are going through the base + offset dance?I wasn''t not sure if you will accept ioremap(..., 8); I will fix the patch.> >> + >> + /* switch to slow mode */ >> + iowritel(sp810, 0x3); >> + dsb(); isb(); >> + /* writing any value to SCSYSSTAT reg will reset the system */ >> + iowritel(sp810 + 4, 0x1); >> + dsb(); isb(); > > Hopefully we don''t get here ;-) > >> + iounmap(base); >> +} >> + >-- Julien
Ian Campbell
2013-May-10 14:06 UTC
Re: [PATCH V3 33/41] xen/arm: Add versatile express platform
On Fri, 2013-05-10 at 15:00 +0100, Julien Grall wrote:> On 05/10/2013 10:44 AM, Ian Campbell wrote: > > >> @@ -91,6 +93,47 @@ out: > >> } > >> > >> /* > >> + * TODO: Get base address from the device tree > >> + * See arm,vexpress-reset node > >> + */ > >> +static void vexpress_reset(void) > >> +{ > >> + void __iomem *base; > >> + void __iomem *sp810; > >> + > >> + /* Use the SP810 system controller to force a reset */ > >> + base = ioremap_attr(SP810_ADDRESS & PAGE_MASK, PAGE_SIZE, > >> + PAGE_HYPERVISOR_NOCACHE); > >> + if ( !base ) > >> + { > >> + dprintk(XENLOG_ERR, "Unable to map SP810\n"); > >> + return; > >> + } > >> + > >> + sp810 = base + (SP810_ADDRESS & ~PAGE_MASK); > > > > Didn''t I see you making vunmap, which iounmap is based on, take care of > > the page offsets itself in an earlier patch? Or is that not the reason > > you are going through the base + offset dance? > > > I wasn''t not sure if you will accept ioremap(..., 8); I will fix the patch.I''m confused, what does (..., 8) mean? What I was asking about was why the code couldn''t be: sp810 = ioremap_..(SP810_ADDRESS, PAGE_SIZE,..) iowritel(sp810,...) iounmap(sp810) Ian.
Julien Grall
2013-May-10 14:10 UTC
Re: [PATCH V3 33/41] xen/arm: Add versatile express platform
On 05/10/2013 03:06 PM, Ian Campbell wrote:> On Fri, 2013-05-10 at 15:00 +0100, Julien Grall wrote: >> On 05/10/2013 10:44 AM, Ian Campbell wrote: >> >>>> @@ -91,6 +93,47 @@ out: >>>> } >>>> >>>> /* >>>> + * TODO: Get base address from the device tree >>>> + * See arm,vexpress-reset node >>>> + */ >>>> +static void vexpress_reset(void) >>>> +{ >>>> + void __iomem *base; >>>> + void __iomem *sp810; >>>> + >>>> + /* Use the SP810 system controller to force a reset */ >>>> + base = ioremap_attr(SP810_ADDRESS & PAGE_MASK, PAGE_SIZE, >>>> + PAGE_HYPERVISOR_NOCACHE); >>>> + if ( !base ) >>>> + { >>>> + dprintk(XENLOG_ERR, "Unable to map SP810\n"); >>>> + return; >>>> + } >>>> + >>>> + sp810 = base + (SP810_ADDRESS & ~PAGE_MASK); >>> >>> Didn''t I see you making vunmap, which iounmap is based on, take care of >>> the page offsets itself in an earlier patch? Or is that not the reason >>> you are going through the base + offset dance? >> >> >> I wasn''t not sure if you will accept ioremap(..., 8); I will fix the patch. > > I''m confused, what does (..., 8) mean?Because we only need to the first 8 bytes.> What I was asking about was why the code couldn''t be: > sp810 = ioremap_..(SP810_ADDRESS, PAGE_SIZE,..) > iowritel(sp810,...) > iounmap(sp810)Indeed. I will use this solution. -- Julien
Ian Campbell
2013-May-10 14:19 UTC
Re: [PATCH V3 33/41] xen/arm: Add versatile express platform
On Fri, 2013-05-10 at 15:10 +0100, Julien Grall wrote:> On 05/10/2013 03:06 PM, Ian Campbell wrote: > > > On Fri, 2013-05-10 at 15:00 +0100, Julien Grall wrote: > >> On 05/10/2013 10:44 AM, Ian Campbell wrote: > >> > >>>> @@ -91,6 +93,47 @@ out: > >>>> } > >>>> > >>>> /* > >>>> + * TODO: Get base address from the device tree > >>>> + * See arm,vexpress-reset node > >>>> + */ > >>>> +static void vexpress_reset(void) > >>>> +{ > >>>> + void __iomem *base; > >>>> + void __iomem *sp810; > >>>> + > >>>> + /* Use the SP810 system controller to force a reset */ > >>>> + base = ioremap_attr(SP810_ADDRESS & PAGE_MASK, PAGE_SIZE, > >>>> + PAGE_HYPERVISOR_NOCACHE); > >>>> + if ( !base ) > >>>> + { > >>>> + dprintk(XENLOG_ERR, "Unable to map SP810\n"); > >>>> + return; > >>>> + } > >>>> + > >>>> + sp810 = base + (SP810_ADDRESS & ~PAGE_MASK); > >>> > >>> Didn''t I see you making vunmap, which iounmap is based on, take care of > >>> the page offsets itself in an earlier patch? Or is that not the reason > >>> you are going through the base + offset dance? > >> > >> > >> I wasn''t not sure if you will accept ioremap(..., 8); I will fix the patch. > > > > I''m confused, what does (..., 8) mean? > > Because we only need to the first 8 bytes.If ioremap is happy with sub-page sizes then I''m ok with this or using PAGE_SIZE. Ian.
Julien Grall
2013-May-10 14:47 UTC
Re: [PATCH V3 31/41] xen/arm: Allow Xen to run on multiple platform without recompilation
On 05/10/2013 10:41 AM, Ian Campbell wrote:> On Fri, 2013-05-10 at 03:18 +0100, Julien Grall wrote: >> Xen can include various platform support (ie: exynos5, versatile express...) >> and choose during boot time a set of callbacks for the current board. >> These callbacks will be called in places where each board can have specific >> code. For the moment the callbacks are: >> - platform_init: additional initialization for the platform >> - platform_init_time: some platform (ie: Exynos 5) needs to initialize >> the timer with an uncommon way >> - platform_specific_mapping: add mapping to dom0 which are not specified >> in the device tree >> - platform_reset: reset the platform >> - platform_poweroff: poweroff the platform >> - platform_quirks: list of quirks for a specific board. >> >> Signed-off-by: Julien Grall <julien.grall@linaro.org> > > Acked-by: Ian Campbell <ian.campbell@citrix.com> > [...] >> +/* List of possible platform */ >> +static void dump_platform_table(void) >> +{ >> + const struct platform_desc *p; >> + >> + printk("Available platform support:\n"); >> + >> + for ( p = _splatform; p != _eplatform; p++ ) >> + printk(" - %s\n", p->name); >> +} >> + >> +int __init platform_init(void) >> +{ > [...] >> + /* We don''t have specific operations for this platform */ >> + if ( platform == _eplatform ) >> + { >> + /* TODO: List compatible */ >> + printk(XENLOG_WARNING "WARNING: Unrecognized/unsupported device tree " >> + "compatible list\n"); >> + dump_platform_table(); > > Looks like the TODO is obsolete?No. It means we need to print the compatible list of the DTS. Here Xen only prints which platform is actually supported. I will update the comment. -- Julien
Stefano Stabellini
2013-May-10 17:06 UTC
Re: [PATCH V3 11/41] xen/arm: Introduce ioremap_attr
On Fri, 10 May 2013, Ian Campbell wrote:> > @@ -59,6 +59,8 @@ > > #define DEV_CACHED WRITEBACK > > > > #define PAGE_HYPERVISOR (MATTR_MEM) > > I should have noticed this when Stefano''s original vmap patch went in > but this is wrong. > > MATTR_* are Second stage paging attributes (i.e. p2m ones) and are not > suitable for the Xen first stage page tables. > > MATTR_MEM == 0xf, which I think will be truncated to 0x7 when written to > the ai field in the PT (which is only 3 bits) and so this is equivalent > to using WRITEALLOC. I suspect that isn''t at all desirable... > > Stefano, can you fix this please?Yep