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>
This commit is contained in:
Max Filippov 2012-11-04 00:30:13 +04:00 коммит произвёл Chris Zankel
Родитель 2206d5dd9a
Коммит da844a8177
7 изменённых файлов: 166 добавлений и 27 удалений

Просмотреть файл

@ -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"

Просмотреть файл

@ -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

Просмотреть файл

@ -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)) \

Просмотреть файл

@ -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 */

Просмотреть файл

@ -0,0 +1,6 @@
#ifndef _XTENSA_ASM_PROM_H
#define _XTENSA_ASM_PROM_H
#define HAVE_ARCH_DEVTREE_FIXUPS
#endif /* _XTENSA_ASM_PROM_H */

Просмотреть файл

@ -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();

Просмотреть файл

@ -22,6 +22,11 @@
#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,26 +117,31 @@ typedef struct tagtable {
/* parse current tag */
static int __init add_sysmem_bank(unsigned long type, unsigned long start,
unsigned long end)
{
if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) {
printk(KERN_WARNING
"Ignoring memory bank 0x%08lx size %ldKB\n",
start, end - start);
return -EINVAL;
}
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);
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);
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.nr_banks++;
return 0;
return add_sysmem_bank(mi->type, mi->start, mi->end);
}
__tagtable(BP_TAG_MEMORY, parse_tag_mem);
@ -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();