xtensa: add device trees support
authorMax Filippov <jcmvbkbc@gmail.com>
Sat, 3 Nov 2012 20:30:13 +0000 (00:30 +0400)
committerChris Zankel <chris@zankel.net>
Wed, 19 Dec 2012 05:10:23 +0000 (21:10 -0800)
Device trees allow specification of hardware topology and device
parameters at runtime instead of hard-coding them in platform setup
code. This allows running single binary kernel on a range of compatible
boards.

New boot parameters tag BP_TAG_FDT is allocated and a pointer to flat
device tree is passed in it.

Note that current interrupt mapping scheme uses single cell for
interrupt identification. That means that IRQ numbers used in DTS must
be CPU internal IRQ numbers, not external. It is possible to extend
interrupt identification to two cells, and use second cell to tell
external IRQ numbers form internal. That would allow to use single DTS
on multiple boards with different mapping of external IRQ numbers.

Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Chris Zankel <chris@zankel.net>
arch/xtensa/Kconfig
arch/xtensa/Makefile
arch/xtensa/boot/Makefile
arch/xtensa/include/asm/bootparam.h
arch/xtensa/include/asm/prom.h [new file with mode: 0644]
arch/xtensa/kernel/irq.c
arch/xtensa/kernel/setup.c

index 1816abc5c8d563e905e9e0073c7d16a160838501..8ceb5b2ffbb1732901a83cc0f6851136edb7ae42 100644 (file)
@@ -178,6 +178,17 @@ config CMDLINE
          time by entering them here. As a minimum, you should specify the
          memory size and the root device (e.g., mem=64M root=/dev/nfs).
 
+config USE_OF
+       bool "Flattened Device Tree support"
+       select OF
+       select OF_EARLY_FLATTREE
+       help
+         Include support for flattened device tree machine descriptions.
+
+config BUILTIN_DTB
+       string "DTB to build into the kernel image"
+       depends on OF
+
 source "mm/Kconfig"
 
 source "drivers/pcmcia/Kconfig"
index ab63c9beb93003f3369a9c45cf89bdb5a2e1cffb..227f658fc2b90ddf9219d589243d32f3cb230d6d 100644 (file)
@@ -79,6 +79,10 @@ core-y               += $(buildvar) $(buildplf)
 
 libs-y         += arch/xtensa/lib/ $(LIBGCC)
 
+ifneq ($(CONFIG_BUILTIN_DTB),"")
+core-y += arch/xtensa/boot/
+endif
+
 boot           := arch/xtensa/boot
 
 all: zImage
@@ -88,6 +92,9 @@ bzImage : zImage
 zImage: vmlinux
        $(Q)$(MAKE) $(build)=$(boot) $@
 
+%.dtb:
+       $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
+
 define archhelp
   @echo '* zImage      - Compressed kernel image (arch/xtensa/boot/images/zImage.*)'
 endef
index ff974fc2935b468dc539c0d74aea745d773a307b..cb5ff4dbdf8837ac616e224dcc2e52fb3206790a 100644 (file)
@@ -25,6 +25,17 @@ bootdir-$(CONFIG_XTENSA_PLATFORM_ISS)         += boot-elf
 bootdir-$(CONFIG_XTENSA_PLATFORM_XT2000) += boot-redboot boot-elf boot-uboot
 
 
+BUILTIN_DTB := $(patsubst "%",%,$(CONFIG_BUILTIN_DTB)).dtb.o
+ifneq ($(CONFIG_BUILTIN_DTB),"")
+obj-y += $(BUILTIN_DTB)
+endif
+
+# Rule to build device tree blobs
+$(obj)/%.dtb: $(src)/dts/%.dts FORCE
+       $(call if_changed_dep,dtc)
+
+clean-files := *.dtb.S
+
 zImage Image: $(bootdir-y)
 
 $(bootdir-y): $(addprefix $(obj)/,$(subdir-y)) \
index 9983f2c1b7ee905ad6e15770808d4cce1507cfb1..057077e96c98ec1f3b88dcbb997cf6d0601a9877 100644 (file)
@@ -22,6 +22,7 @@
 #define BP_TAG_MEMORY          0x1003  /* memory addr and size (bp_meminfo) */
 #define BP_TAG_SERIAL_BAUSRATE 0x1004  /* baud rate of current console. */
 #define BP_TAG_SERIAL_PORT     0x1005  /* serial device of current console */
+#define BP_TAG_FDT             0x1006  /* flat device tree addr */
 
 #define BP_TAG_FIRST           0x7B0B  /* first tag with a version number */
 #define BP_TAG_LAST            0x7E0B  /* last tag */
diff --git a/arch/xtensa/include/asm/prom.h b/arch/xtensa/include/asm/prom.h
new file mode 100644 (file)
index 0000000..f3d7cd2
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _XTENSA_ASM_PROM_H
+#define _XTENSA_ASM_PROM_H
+
+#define HAVE_ARCH_DEVTREE_FIXUPS
+
+#endif /* _XTENSA_ASM_PROM_H */
index e90d782111955e16c671e994233eda217556b860..6f4f9749cff773f229a76a35bf50373ec18731ab 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/irq.h>
 #include <linux/kernel_stat.h>
 #include <linux/irqdomain.h>
+#include <linux/of.h>
 
 #include <asm/uaccess.h>
 #include <asm/platform.h>
@@ -199,8 +200,17 @@ void __init init_IRQ(void)
        cached_irq_mask = 0;
        set_sr(~0, intclear);
 
+#ifdef CONFIG_OF
+       /* The interrupt controller device node is mandatory */
+       intc = of_find_compatible_node(NULL, NULL, "xtensa,pic");
+       BUG_ON(!intc);
+
+       root_domain = irq_domain_add_linear(intc, NR_IRQS,
+                       &xtensa_irq_domain_ops, NULL);
+#else
        root_domain = irq_domain_add_legacy(intc, NR_IRQS, 0, 0,
                        &xtensa_irq_domain_ops, NULL);
+#endif
        irq_set_default_host(root_domain);
 
        variant_init_irq();
index 45217617c60d5508623c191d4efa608c747a921a..64d80e4b0bd4b6916e9b93e6d7503c651026e329 100644 (file)
 #include <linux/bootmem.h>
 #include <linux/kernel.h>
 
+#ifdef CONFIG_OF
+#include <linux/of_fdt.h>
+#include <linux/of_platform.h>
+#endif
+
 #if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
 # include <linux/console.h>
 #endif
@@ -65,6 +70,11 @@ int initrd_is_mapped = 0;
 extern int initrd_below_start_ok;
 #endif
 
+#ifdef CONFIG_OF
+extern u32 __dtb_start[];
+void *dtb_start = __dtb_start;
+#endif
+
 unsigned char aux_device_present;
 extern unsigned long loops_per_jiffy;
 
@@ -84,6 +94,8 @@ extern void init_mmu(void);
 static inline void init_mmu(void) { }
 #endif
 
+extern int mem_reserve(unsigned long, unsigned long, int);
+extern void bootmem_init(void);
 extern void zones_init(void);
 
 /*
@@ -105,28 +117,33 @@ typedef struct tagtable {
 
 /* parse current tag */
 
-static int __init parse_tag_mem(const bp_tag_t *tag)
+static int __init add_sysmem_bank(unsigned long type, unsigned long start,
+               unsigned long end)
 {
-       meminfo_t *mi = (meminfo_t*)(tag->data);
-
-       if (mi->type != MEMORY_TYPE_CONVENTIONAL)
-               return -1;
-
        if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) {
                printk(KERN_WARNING
-                      "Ignoring memory bank 0x%08lx size %ldKB\n",
-                      (unsigned long)mi->start,
-                      (unsigned long)mi->end - (unsigned long)mi->start);
+                               "Ignoring memory bank 0x%08lx size %ldKB\n",
+                               start, end - start);
                return -EINVAL;
        }
-       sysmem.bank[sysmem.nr_banks].type  = mi->type;
-       sysmem.bank[sysmem.nr_banks].start = PAGE_ALIGN(mi->start);
-       sysmem.bank[sysmem.nr_banks].end   = mi->end & PAGE_MASK;
+       sysmem.bank[sysmem.nr_banks].type  = type;
+       sysmem.bank[sysmem.nr_banks].start = PAGE_ALIGN(start);
+       sysmem.bank[sysmem.nr_banks].end   = end & PAGE_MASK;
        sysmem.nr_banks++;
 
        return 0;
 }
 
+static int __init parse_tag_mem(const bp_tag_t *tag)
+{
+       meminfo_t *mi = (meminfo_t *)(tag->data);
+
+       if (mi->type != MEMORY_TYPE_CONVENTIONAL)
+               return -1;
+
+       return add_sysmem_bank(mi->type, mi->start, mi->end);
+}
+
 __tagtable(BP_TAG_MEMORY, parse_tag_mem);
 
 #ifdef CONFIG_BLK_DEV_INITRD
@@ -143,12 +160,31 @@ static int __init parse_tag_initrd(const bp_tag_t* tag)
 
 __tagtable(BP_TAG_INITRD, parse_tag_initrd);
 
+#ifdef CONFIG_OF
+
+static int __init parse_tag_fdt(const bp_tag_t *tag)
+{
+       dtb_start = (void *)(tag->data[0]);
+       return 0;
+}
+
+__tagtable(BP_TAG_FDT, parse_tag_fdt);
+
+void __init early_init_dt_setup_initrd_arch(unsigned long start,
+               unsigned long end)
+{
+       initrd_start = (void *)__va(start);
+       initrd_end = (void *)__va(end);
+       initrd_below_start_ok = 1;
+}
+
+#endif /* CONFIG_OF */
+
 #endif /* CONFIG_BLK_DEV_INITRD */
 
 static int __init parse_tag_cmdline(const bp_tag_t* tag)
 {
-       strncpy(command_line, (char*)(tag->data), COMMAND_LINE_SIZE);
-       command_line[COMMAND_LINE_SIZE - 1] = '\0';
+       strlcpy(command_line, (char *)(tag->data), COMMAND_LINE_SIZE);
        return 0;
 }
 
@@ -186,6 +222,58 @@ static int __init parse_bootparam(const bp_tag_t* tag)
        return 0;
 }
 
+#ifdef CONFIG_OF
+
+void __init early_init_dt_add_memory_arch(u64 base, u64 size)
+{
+       size &= PAGE_MASK;
+       add_sysmem_bank(MEMORY_TYPE_CONVENTIONAL, base, base + size);
+}
+
+void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
+{
+       return __alloc_bootmem(size, align, 0);
+}
+
+void __init early_init_devtree(void *params)
+{
+       /* Setup flat device-tree pointer */
+       initial_boot_params = params;
+
+       /* Retrieve various informations from the /chosen node of the
+        * device-tree, including the platform type, initrd location and
+        * size, TCE reserve, and more ...
+        */
+       if (!command_line[0])
+               of_scan_flat_dt(early_init_dt_scan_chosen, command_line);
+
+       /* Scan memory nodes and rebuild MEMBLOCKs */
+       of_scan_flat_dt(early_init_dt_scan_root, NULL);
+       if (sysmem.nr_banks == 0)
+               of_scan_flat_dt(early_init_dt_scan_memory, NULL);
+}
+
+static void __init copy_devtree(void)
+{
+       void *alloc = early_init_dt_alloc_memory_arch(
+                       be32_to_cpu(initial_boot_params->totalsize), 0);
+       if (alloc) {
+               memcpy(alloc, initial_boot_params,
+                               be32_to_cpu(initial_boot_params->totalsize));
+               initial_boot_params = alloc;
+       }
+}
+
+static int __init xtensa_device_probe(void)
+{
+       of_platform_populate(NULL, NULL, NULL, NULL);
+       return 0;
+}
+
+device_initcall(xtensa_device_probe);
+
+#endif /* CONFIG_OF */
+
 /*
  * Initialize architecture. (Early stage)
  */
@@ -194,14 +282,14 @@ void __init init_arch(bp_tag_t *bp_start)
 {
        sysmem.nr_banks = 0;
 
-#ifdef CONFIG_CMDLINE_BOOL
-       strcpy(command_line, default_command_line);
-#endif
-
        /* Parse boot parameters */
 
         if (bp_start)
-         parse_bootparam(bp_start);
+               parse_bootparam(bp_start);
+
+#ifdef CONFIG_OF
+       early_init_devtree(dtb_start);
+#endif
 
        if (sysmem.nr_banks == 0) {
                sysmem.nr_banks = 1;
@@ -210,6 +298,11 @@ void __init init_arch(bp_tag_t *bp_start)
                                     + PLATFORM_DEFAULT_MEM_SIZE;
        }
 
+#ifdef CONFIG_CMDLINE_BOOL
+       if (!command_line[0])
+               strlcpy(command_line, default_command_line, COMMAND_LINE_SIZE);
+#endif
+
        /* Early hook for platforms */
 
        platform_init(bp_start);
@@ -355,11 +448,7 @@ void __init check_s32c1i(void)
 
 void __init setup_arch(char **cmdline_p)
 {
-       extern int mem_reserve(unsigned long, unsigned long, int);
-       extern void bootmem_init(void);
-
-       memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
-       boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
+       strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
        *cmdline_p = command_line;
 
        check_s32c1i();
@@ -395,8 +484,12 @@ void __init setup_arch(char **cmdline_p)
 
        bootmem_init();
 
-       platform_setup(cmdline_p);
+#ifdef CONFIG_OF
+       copy_devtree();
+       unflatten_device_tree();
+#endif
 
+       platform_setup(cmdline_p);
 
        paging_init();
        zones_init();