Commit 26bcd8b72563b4c54892c4c2a409f6656fb8ae8b

Authored by 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 1c2f87c22566 ("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

Showing 5 changed files Side-by-side Diff

arch/arm/include/asm/mach/arch.h
... ... @@ -50,6 +50,7 @@
50 50 struct smp_operations *smp; /* SMP operations */
51 51 bool (*smp_init)(void);
52 52 void (*fixup)(struct tag *, char **);
  53 + void (*dt_fixup)(void);
53 54 void (*init_meminfo)(void);
54 55 void (*reserve)(void);/* reserve mem blocks */
55 56 void (*map_io)(void);/* IO mapping function */
arch/arm/kernel/devtree.c
... ... @@ -212,7 +212,7 @@
212 212 mdesc_best = &__mach_desc_GENERIC_DT;
213 213 #endif
214 214  
215   - if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys)))
  215 + if (!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys)))
216 216 return NULL;
217 217  
218 218 mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
... ... @@ -236,6 +236,12 @@
236 236  
237 237 dump_machine_table(); /* does not return */
238 238 }
  239 +
  240 + /* We really don't want to do this, but sometimes firmware provides buggy data */
  241 + if (mdesc->dt_fixup)
  242 + mdesc->dt_fixup();
  243 +
  244 + early_init_dt_scan_nodes();
239 245  
240 246 /* Change machine number to match the mdesc we're using */
241 247 __machine_arch_type = mdesc->nr;
arch/arm/mach-exynos/exynos.c
... ... @@ -335,6 +335,15 @@
335 335 #endif
336 336 }
337 337  
  338 +static void __init exynos_dt_fixup(void)
  339 +{
  340 + /*
  341 + * Some versions of uboot pass garbage entries in the memory node,
  342 + * use the old CONFIG_ARM_NR_BANKS
  343 + */
  344 + of_fdt_limit_memory(8);
  345 +}
  346 +
338 347 DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)")
339 348 /* Maintainer: Thomas Abraham <thomas.abraham@linaro.org> */
340 349 /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
... ... @@ -348,5 +357,6 @@
348 357 .dt_compat = exynos_dt_compat,
349 358 .restart = exynos_restart,
350 359 .reserve = exynos_reserve,
  360 + .dt_fixup = exynos_dt_fixup,
351 361 MACHINE_END
... ... @@ -26,6 +26,54 @@
26 26 #include <asm/setup.h> /* for COMMAND_LINE_SIZE */
27 27 #include <asm/page.h>
28 28  
  29 +/*
  30 + * of_fdt_limit_memory - limit the number of regions in the /memory node
  31 + * @limit: maximum entries
  32 + *
  33 + * Adjust the flattened device tree to have at most 'limit' number of
  34 + * memory entries in the /memory node. This function may be called
  35 + * any time after initial_boot_param is set.
  36 + */
  37 +void of_fdt_limit_memory(int limit)
  38 +{
  39 + int memory;
  40 + int len;
  41 + const void *val;
  42 + int nr_address_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
  43 + int nr_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
  44 + const uint32_t *addr_prop;
  45 + const uint32_t *size_prop;
  46 + int root_offset;
  47 + int cell_size;
  48 +
  49 + root_offset = fdt_path_offset(initial_boot_params, "/");
  50 + if (root_offset < 0)
  51 + return;
  52 +
  53 + addr_prop = fdt_getprop(initial_boot_params, root_offset,
  54 + "#address-cells", NULL);
  55 + if (addr_prop)
  56 + nr_address_cells = fdt32_to_cpu(*addr_prop);
  57 +
  58 + size_prop = fdt_getprop(initial_boot_params, root_offset,
  59 + "#size-cells", NULL);
  60 + if (size_prop)
  61 + nr_size_cells = fdt32_to_cpu(*size_prop);
  62 +
  63 + cell_size = sizeof(uint32_t)*(nr_address_cells + nr_size_cells);
  64 +
  65 + memory = fdt_path_offset(initial_boot_params, "/memory");
  66 + if (memory > 0) {
  67 + val = fdt_getprop(initial_boot_params, memory, "reg", &len);
  68 + if (len > limit*cell_size) {
  69 + len = limit*cell_size;
  70 + pr_debug("Limiting number of entries to %d\n", limit);
  71 + fdt_setprop(initial_boot_params, memory, "reg", val,
  72 + len);
  73 + }
  74 + }
  75 +}
  76 +
29 77 /**
30 78 * of_fdt_is_compatible - Return true if given node from the given blob has
31 79 * compat in its compatible list
... ... @@ -937,7 +985,7 @@
937 985 }
938 986 #endif
939 987  
940   -bool __init early_init_dt_scan(void *params)
  988 +bool __init early_init_dt_verify(void *params)
941 989 {
942 990 if (!params)
943 991 return false;
... ... @@ -951,6 +999,12 @@
951 999 return false;
952 1000 }
953 1001  
  1002 + return true;
  1003 +}
  1004 +
  1005 +
  1006 +void __init early_init_dt_scan_nodes(void)
  1007 +{
954 1008 /* Retrieve various information from the /chosen node */
955 1009 of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
956 1010  
957 1011  
... ... @@ -959,7 +1013,17 @@
959 1013  
960 1014 /* Setup memory, calling early_init_dt_add_memory_arch */
961 1015 of_scan_flat_dt(early_init_dt_scan_memory, NULL);
  1016 +}
962 1017  
  1018 +bool __init early_init_dt_scan(void *params)
  1019 +{
  1020 + bool status;
  1021 +
  1022 + status = early_init_dt_verify(params);
  1023 + if (!status)
  1024 + return false;
  1025 +
  1026 + early_init_dt_scan_nodes();
963 1027 return true;
964 1028 }
965 1029  
include/linux/of_fdt.h
... ... @@ -73,6 +73,8 @@
73 73 int depth, void *data);
74 74  
75 75 extern bool early_init_dt_scan(void *params);
  76 +extern bool early_init_dt_verify(void *params);
  77 +extern void early_init_dt_scan_nodes(void);
76 78  
77 79 extern const char *of_flat_dt_get_machine_name(void);
78 80 extern const void *of_flat_dt_match_machine(const void *default_match,
... ... @@ -84,6 +86,7 @@
84 86 extern void early_init_devtree(void *);
85 87 extern void early_get_first_memblock_info(void *, phys_addr_t *);
86 88 extern u64 fdt_translate_address(const void *blob, int node_offset);
  89 +extern void of_fdt_limit_memory(int limit);
87 90 #else /* CONFIG_OF_FLATTREE */
88 91 static inline void early_init_fdt_scan_reserved_mem(void) {}
89 92 static inline const char *of_flat_dt_get_machine_name(void) { return NULL; }