Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xtensa: add device trees support

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>

authored by

Max Filippov and committed by
Chris Zankel
da844a81 2206d5dd

+166 -27
+11
arch/xtensa/Kconfig
··· 178 178 time by entering them here. As a minimum, you should specify the 179 179 memory size and the root device (e.g., mem=64M root=/dev/nfs). 180 180 181 + config USE_OF 182 + bool "Flattened Device Tree support" 183 + select OF 184 + select OF_EARLY_FLATTREE 185 + help 186 + Include support for flattened device tree machine descriptions. 187 + 188 + config BUILTIN_DTB 189 + string "DTB to build into the kernel image" 190 + depends on OF 191 + 181 192 source "mm/Kconfig" 182 193 183 194 source "drivers/pcmcia/Kconfig"
+7
arch/xtensa/Makefile
··· 79 79 80 80 libs-y += arch/xtensa/lib/ $(LIBGCC) 81 81 82 + ifneq ($(CONFIG_BUILTIN_DTB),"") 83 + core-y += arch/xtensa/boot/ 84 + endif 85 + 82 86 boot := arch/xtensa/boot 83 87 84 88 all: zImage ··· 91 87 92 88 zImage: vmlinux 93 89 $(Q)$(MAKE) $(build)=$(boot) $@ 90 + 91 + %.dtb: 92 + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ 94 93 95 94 define archhelp 96 95 @echo '* zImage - Compressed kernel image (arch/xtensa/boot/images/zImage.*)'
+11
arch/xtensa/boot/Makefile
··· 25 25 bootdir-$(CONFIG_XTENSA_PLATFORM_XT2000) += boot-redboot boot-elf boot-uboot 26 26 27 27 28 + BUILTIN_DTB := $(patsubst "%",%,$(CONFIG_BUILTIN_DTB)).dtb.o 29 + ifneq ($(CONFIG_BUILTIN_DTB),"") 30 + obj-y += $(BUILTIN_DTB) 31 + endif 32 + 33 + # Rule to build device tree blobs 34 + $(obj)/%.dtb: $(src)/dts/%.dts FORCE 35 + $(call if_changed_dep,dtc) 36 + 37 + clean-files := *.dtb.S 38 + 28 39 zImage Image: $(bootdir-y) 29 40 30 41 $(bootdir-y): $(addprefix $(obj)/,$(subdir-y)) \
+1
arch/xtensa/include/asm/bootparam.h
··· 22 22 #define BP_TAG_MEMORY 0x1003 /* memory addr and size (bp_meminfo) */ 23 23 #define BP_TAG_SERIAL_BAUSRATE 0x1004 /* baud rate of current console. */ 24 24 #define BP_TAG_SERIAL_PORT 0x1005 /* serial device of current console */ 25 + #define BP_TAG_FDT 0x1006 /* flat device tree addr */ 25 26 26 27 #define BP_TAG_FIRST 0x7B0B /* first tag with a version number */ 27 28 #define BP_TAG_LAST 0x7E0B /* last tag */
+6
arch/xtensa/include/asm/prom.h
··· 1 + #ifndef _XTENSA_ASM_PROM_H 2 + #define _XTENSA_ASM_PROM_H 3 + 4 + #define HAVE_ARCH_DEVTREE_FIXUPS 5 + 6 + #endif /* _XTENSA_ASM_PROM_H */
+10
arch/xtensa/kernel/irq.c
··· 19 19 #include <linux/irq.h> 20 20 #include <linux/kernel_stat.h> 21 21 #include <linux/irqdomain.h> 22 + #include <linux/of.h> 22 23 23 24 #include <asm/uaccess.h> 24 25 #include <asm/platform.h> ··· 200 199 cached_irq_mask = 0; 201 200 set_sr(~0, intclear); 202 201 202 + #ifdef CONFIG_OF 203 + /* The interrupt controller device node is mandatory */ 204 + intc = of_find_compatible_node(NULL, NULL, "xtensa,pic"); 205 + BUG_ON(!intc); 206 + 207 + root_domain = irq_domain_add_linear(intc, NR_IRQS, 208 + &xtensa_irq_domain_ops, NULL); 209 + #else 203 210 root_domain = irq_domain_add_legacy(intc, NR_IRQS, 0, 0, 204 211 &xtensa_irq_domain_ops, NULL); 212 + #endif 205 213 irq_set_default_host(root_domain); 206 214 207 215 variant_init_irq();
+120 -27
arch/xtensa/kernel/setup.c
··· 22 22 #include <linux/bootmem.h> 23 23 #include <linux/kernel.h> 24 24 25 + #ifdef CONFIG_OF 26 + #include <linux/of_fdt.h> 27 + #include <linux/of_platform.h> 28 + #endif 29 + 25 30 #if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE) 26 31 # include <linux/console.h> 27 32 #endif ··· 70 65 extern int initrd_below_start_ok; 71 66 #endif 72 67 68 + #ifdef CONFIG_OF 69 + extern u32 __dtb_start[]; 70 + void *dtb_start = __dtb_start; 71 + #endif 72 + 73 73 unsigned char aux_device_present; 74 74 extern unsigned long loops_per_jiffy; 75 75 ··· 94 84 static inline void init_mmu(void) { } 95 85 #endif 96 86 87 + extern int mem_reserve(unsigned long, unsigned long, int); 88 + extern void bootmem_init(void); 97 89 extern void zones_init(void); 98 90 99 91 /* ··· 117 105 118 106 /* parse current tag */ 119 107 108 + static int __init add_sysmem_bank(unsigned long type, unsigned long start, 109 + unsigned long end) 110 + { 111 + if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) { 112 + printk(KERN_WARNING 113 + "Ignoring memory bank 0x%08lx size %ldKB\n", 114 + start, end - start); 115 + return -EINVAL; 116 + } 117 + sysmem.bank[sysmem.nr_banks].type = type; 118 + sysmem.bank[sysmem.nr_banks].start = PAGE_ALIGN(start); 119 + sysmem.bank[sysmem.nr_banks].end = end & PAGE_MASK; 120 + sysmem.nr_banks++; 121 + 122 + return 0; 123 + } 124 + 120 125 static int __init parse_tag_mem(const bp_tag_t *tag) 121 126 { 122 - meminfo_t *mi = (meminfo_t*)(tag->data); 127 + meminfo_t *mi = (meminfo_t *)(tag->data); 123 128 124 129 if (mi->type != MEMORY_TYPE_CONVENTIONAL) 125 130 return -1; 126 131 127 - if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) { 128 - printk(KERN_WARNING 129 - "Ignoring memory bank 0x%08lx size %ldKB\n", 130 - (unsigned long)mi->start, 131 - (unsigned long)mi->end - (unsigned long)mi->start); 132 - return -EINVAL; 133 - } 134 - sysmem.bank[sysmem.nr_banks].type = mi->type; 135 - sysmem.bank[sysmem.nr_banks].start = PAGE_ALIGN(mi->start); 136 - sysmem.bank[sysmem.nr_banks].end = mi->end & PAGE_MASK; 137 - sysmem.nr_banks++; 138 - 139 - return 0; 132 + return add_sysmem_bank(mi->type, mi->start, mi->end); 140 133 } 141 134 142 135 __tagtable(BP_TAG_MEMORY, parse_tag_mem); ··· 160 143 161 144 __tagtable(BP_TAG_INITRD, parse_tag_initrd); 162 145 146 + #ifdef CONFIG_OF 147 + 148 + static int __init parse_tag_fdt(const bp_tag_t *tag) 149 + { 150 + dtb_start = (void *)(tag->data[0]); 151 + return 0; 152 + } 153 + 154 + __tagtable(BP_TAG_FDT, parse_tag_fdt); 155 + 156 + void __init early_init_dt_setup_initrd_arch(unsigned long start, 157 + unsigned long end) 158 + { 159 + initrd_start = (void *)__va(start); 160 + initrd_end = (void *)__va(end); 161 + initrd_below_start_ok = 1; 162 + } 163 + 164 + #endif /* CONFIG_OF */ 165 + 163 166 #endif /* CONFIG_BLK_DEV_INITRD */ 164 167 165 168 static int __init parse_tag_cmdline(const bp_tag_t* tag) 166 169 { 167 - strncpy(command_line, (char*)(tag->data), COMMAND_LINE_SIZE); 168 - command_line[COMMAND_LINE_SIZE - 1] = '\0'; 170 + strlcpy(command_line, (char *)(tag->data), COMMAND_LINE_SIZE); 169 171 return 0; 170 172 } 171 173 ··· 222 186 return 0; 223 187 } 224 188 189 + #ifdef CONFIG_OF 190 + 191 + void __init early_init_dt_add_memory_arch(u64 base, u64 size) 192 + { 193 + size &= PAGE_MASK; 194 + add_sysmem_bank(MEMORY_TYPE_CONVENTIONAL, base, base + size); 195 + } 196 + 197 + void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) 198 + { 199 + return __alloc_bootmem(size, align, 0); 200 + } 201 + 202 + void __init early_init_devtree(void *params) 203 + { 204 + /* Setup flat device-tree pointer */ 205 + initial_boot_params = params; 206 + 207 + /* Retrieve various informations from the /chosen node of the 208 + * device-tree, including the platform type, initrd location and 209 + * size, TCE reserve, and more ... 210 + */ 211 + if (!command_line[0]) 212 + of_scan_flat_dt(early_init_dt_scan_chosen, command_line); 213 + 214 + /* Scan memory nodes and rebuild MEMBLOCKs */ 215 + of_scan_flat_dt(early_init_dt_scan_root, NULL); 216 + if (sysmem.nr_banks == 0) 217 + of_scan_flat_dt(early_init_dt_scan_memory, NULL); 218 + } 219 + 220 + static void __init copy_devtree(void) 221 + { 222 + void *alloc = early_init_dt_alloc_memory_arch( 223 + be32_to_cpu(initial_boot_params->totalsize), 0); 224 + if (alloc) { 225 + memcpy(alloc, initial_boot_params, 226 + be32_to_cpu(initial_boot_params->totalsize)); 227 + initial_boot_params = alloc; 228 + } 229 + } 230 + 231 + static int __init xtensa_device_probe(void) 232 + { 233 + of_platform_populate(NULL, NULL, NULL, NULL); 234 + return 0; 235 + } 236 + 237 + device_initcall(xtensa_device_probe); 238 + 239 + #endif /* CONFIG_OF */ 240 + 225 241 /* 226 242 * Initialize architecture. (Early stage) 227 243 */ ··· 282 194 { 283 195 sysmem.nr_banks = 0; 284 196 285 - #ifdef CONFIG_CMDLINE_BOOL 286 - strcpy(command_line, default_command_line); 287 - #endif 288 - 289 197 /* Parse boot parameters */ 290 198 291 199 if (bp_start) 292 - parse_bootparam(bp_start); 200 + parse_bootparam(bp_start); 201 + 202 + #ifdef CONFIG_OF 203 + early_init_devtree(dtb_start); 204 + #endif 293 205 294 206 if (sysmem.nr_banks == 0) { 295 207 sysmem.nr_banks = 1; ··· 297 209 sysmem.bank[0].end = PLATFORM_DEFAULT_MEM_START 298 210 + PLATFORM_DEFAULT_MEM_SIZE; 299 211 } 212 + 213 + #ifdef CONFIG_CMDLINE_BOOL 214 + if (!command_line[0]) 215 + strlcpy(command_line, default_command_line, COMMAND_LINE_SIZE); 216 + #endif 300 217 301 218 /* Early hook for platforms */ 302 219 ··· 448 355 449 356 void __init setup_arch(char **cmdline_p) 450 357 { 451 - extern int mem_reserve(unsigned long, unsigned long, int); 452 - extern void bootmem_init(void); 453 - 454 - memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); 455 - boot_command_line[COMMAND_LINE_SIZE-1] = '\0'; 358 + strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); 456 359 *cmdline_p = command_line; 457 360 458 361 check_s32c1i(); ··· 484 395 485 396 bootmem_init(); 486 397 487 - platform_setup(cmdline_p); 398 + #ifdef CONFIG_OF 399 + copy_devtree(); 400 + unflatten_device_tree(); 401 + #endif 488 402 403 + platform_setup(cmdline_p); 489 404 490 405 paging_init(); 491 406 zones_init();