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:
Родитель
2206d5dd9a
Коммит
da844a8177
|
@ -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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче