Commit 26bcd8b7 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'devicetree-for-linus' of git://git.secretlab.ca/git/linux

Pull Exynos platform DT fix from Grant Likely:
 "Device tree Exynos bug fix for v3.16-rc7

  This bug fix has been brewing for a while.  I hate sending it to you
  so late, but I only got confirmation that it solves the problem this
  past weekend.  The diff looks big for a bug fix, but the majority of
  it is only executed in the Exynos quirk case.  Unfortunately it
  required splitting early_init_dt_scan() in two and adding quirk
  handling in the middle of it on ARM.

  Exynos has buggy firmware that puts bad data into the memory node.
  Commit 1c2f87c2 ("ARM: Get rid of meminfo") exposed the bug by
  dropping the artificial upper bound on the number of memory banks that
  can be added.  Exynos fails to boot after that commit.  This branch
  fixes it by splitting the early DT parse function and inserting a
  fixup hook.  Exynos uses the hook to correct the DT before parsing
  memory regions"

* tag 'devicetree-for-linus' of git://git.secretlab.ca/git/linux:
  arm: Add devicetree fixup machine function
  of: Add memory limiting function for flattened devicetrees
  of: Split early_init_dt_scan into two parts
parents acba648d 5a12a597
...@@ -50,6 +50,7 @@ struct machine_desc { ...@@ -50,6 +50,7 @@ struct machine_desc {
struct smp_operations *smp; /* SMP operations */ struct smp_operations *smp; /* SMP operations */
bool (*smp_init)(void); bool (*smp_init)(void);
void (*fixup)(struct tag *, char **); void (*fixup)(struct tag *, char **);
void (*dt_fixup)(void);
void (*init_meminfo)(void); void (*init_meminfo)(void);
void (*reserve)(void);/* reserve mem blocks */ void (*reserve)(void);/* reserve mem blocks */
void (*map_io)(void);/* IO mapping function */ void (*map_io)(void);/* IO mapping function */
......
...@@ -212,7 +212,7 @@ const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) ...@@ -212,7 +212,7 @@ const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
mdesc_best = &__mach_desc_GENERIC_DT; mdesc_best = &__mach_desc_GENERIC_DT;
#endif #endif
if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys))) if (!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys)))
return NULL; return NULL;
mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach); mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
...@@ -237,6 +237,12 @@ const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) ...@@ -237,6 +237,12 @@ const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
dump_machine_table(); /* does not return */ dump_machine_table(); /* does not return */
} }
/* We really don't want to do this, but sometimes firmware provides buggy data */
if (mdesc->dt_fixup)
mdesc->dt_fixup();
early_init_dt_scan_nodes();
/* Change machine number to match the mdesc we're using */ /* Change machine number to match the mdesc we're using */
__machine_arch_type = mdesc->nr; __machine_arch_type = mdesc->nr;
......
...@@ -335,6 +335,15 @@ static void __init exynos_reserve(void) ...@@ -335,6 +335,15 @@ static void __init exynos_reserve(void)
#endif #endif
} }
static void __init exynos_dt_fixup(void)
{
/*
* Some versions of uboot pass garbage entries in the memory node,
* use the old CONFIG_ARM_NR_BANKS
*/
of_fdt_limit_memory(8);
}
DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)") DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)")
/* Maintainer: Thomas Abraham <thomas.abraham@linaro.org> */ /* Maintainer: Thomas Abraham <thomas.abraham@linaro.org> */
/* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */ /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
...@@ -348,4 +357,5 @@ DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)") ...@@ -348,4 +357,5 @@ DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)")
.dt_compat = exynos_dt_compat, .dt_compat = exynos_dt_compat,
.restart = exynos_restart, .restart = exynos_restart,
.reserve = exynos_reserve, .reserve = exynos_reserve,
.dt_fixup = exynos_dt_fixup,
MACHINE_END MACHINE_END
...@@ -26,6 +26,54 @@ ...@@ -26,6 +26,54 @@
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */ #include <asm/setup.h> /* for COMMAND_LINE_SIZE */
#include <asm/page.h> #include <asm/page.h>
/*
* of_fdt_limit_memory - limit the number of regions in the /memory node
* @limit: maximum entries
*
* Adjust the flattened device tree to have at most 'limit' number of
* memory entries in the /memory node. This function may be called
* any time after initial_boot_param is set.
*/
void of_fdt_limit_memory(int limit)
{
int memory;
int len;
const void *val;
int nr_address_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
int nr_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
const uint32_t *addr_prop;
const uint32_t *size_prop;
int root_offset;
int cell_size;
root_offset = fdt_path_offset(initial_boot_params, "/");
if (root_offset < 0)
return;
addr_prop = fdt_getprop(initial_boot_params, root_offset,
"#address-cells", NULL);
if (addr_prop)
nr_address_cells = fdt32_to_cpu(*addr_prop);
size_prop = fdt_getprop(initial_boot_params, root_offset,
"#size-cells", NULL);
if (size_prop)
nr_size_cells = fdt32_to_cpu(*size_prop);
cell_size = sizeof(uint32_t)*(nr_address_cells + nr_size_cells);
memory = fdt_path_offset(initial_boot_params, "/memory");
if (memory > 0) {
val = fdt_getprop(initial_boot_params, memory, "reg", &len);
if (len > limit*cell_size) {
len = limit*cell_size;
pr_debug("Limiting number of entries to %d\n", limit);
fdt_setprop(initial_boot_params, memory, "reg", val,
len);
}
}
}
/** /**
* of_fdt_is_compatible - Return true if given node from the given blob has * of_fdt_is_compatible - Return true if given node from the given blob has
* compat in its compatible list * compat in its compatible list
...@@ -937,7 +985,7 @@ int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, ...@@ -937,7 +985,7 @@ int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
} }
#endif #endif
bool __init early_init_dt_scan(void *params) bool __init early_init_dt_verify(void *params)
{ {
if (!params) if (!params)
return false; return false;
...@@ -951,6 +999,12 @@ bool __init early_init_dt_scan(void *params) ...@@ -951,6 +999,12 @@ bool __init early_init_dt_scan(void *params)
return false; return false;
} }
return true;
}
void __init early_init_dt_scan_nodes(void)
{
/* Retrieve various information from the /chosen node */ /* Retrieve various information from the /chosen node */
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
...@@ -959,7 +1013,17 @@ bool __init early_init_dt_scan(void *params) ...@@ -959,7 +1013,17 @@ bool __init early_init_dt_scan(void *params)
/* Setup memory, calling early_init_dt_add_memory_arch */ /* Setup memory, calling early_init_dt_add_memory_arch */
of_scan_flat_dt(early_init_dt_scan_memory, NULL); of_scan_flat_dt(early_init_dt_scan_memory, NULL);
}
bool __init early_init_dt_scan(void *params)
{
bool status;
status = early_init_dt_verify(params);
if (!status)
return false;
early_init_dt_scan_nodes();
return true; return true;
} }
......
...@@ -73,6 +73,8 @@ extern int early_init_dt_scan_root(unsigned long node, const char *uname, ...@@ -73,6 +73,8 @@ extern int early_init_dt_scan_root(unsigned long node, const char *uname,
int depth, void *data); int depth, void *data);
extern bool early_init_dt_scan(void *params); extern bool early_init_dt_scan(void *params);
extern bool early_init_dt_verify(void *params);
extern void early_init_dt_scan_nodes(void);
extern const char *of_flat_dt_get_machine_name(void); extern const char *of_flat_dt_get_machine_name(void);
extern const void *of_flat_dt_match_machine(const void *default_match, extern const void *of_flat_dt_match_machine(const void *default_match,
...@@ -84,6 +86,7 @@ extern void unflatten_and_copy_device_tree(void); ...@@ -84,6 +86,7 @@ extern void unflatten_and_copy_device_tree(void);
extern void early_init_devtree(void *); extern void early_init_devtree(void *);
extern void early_get_first_memblock_info(void *, phys_addr_t *); extern void early_get_first_memblock_info(void *, phys_addr_t *);
extern u64 fdt_translate_address(const void *blob, int node_offset); extern u64 fdt_translate_address(const void *blob, int node_offset);
extern void of_fdt_limit_memory(int limit);
#else /* CONFIG_OF_FLATTREE */ #else /* CONFIG_OF_FLATTREE */
static inline void early_init_fdt_scan_reserved_mem(void) {} static inline void early_init_fdt_scan_reserved_mem(void) {}
static inline const char *of_flat_dt_get_machine_name(void) { return NULL; } static inline const char *of_flat_dt_get_machine_name(void) { return NULL; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment