devtree.c 6.77 KB
Newer Older
Grant Likely's avatar
Grant Likely committed
1 2 3 4 5 6 7 8 9 10 11
/*
 *  linux/arch/arm/kernel/devtree.c
 *
 *  Copyright (C) 2009 Canonical Ltd. <jeremy.kerr@canonical.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
12
#include <linux/export.h>
Grant Likely's avatar
Grant Likely committed
13 14 15 16 17 18 19 20
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/bootmem.h>
#include <linux/memblock.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
21
#include <linux/smp.h>
Grant Likely's avatar
Grant Likely committed
22

23
#include <asm/cputype.h>
Grant Likely's avatar
Grant Likely committed
24 25
#include <asm/setup.h>
#include <asm/page.h>
26
#include <asm/smp_plat.h>
27 28
#include <asm/mach/arch.h>
#include <asm/mach-types.h>
Grant Likely's avatar
Grant Likely committed
29 30 31

void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
{
32
	return memblock_virt_alloc(size, align);
Grant Likely's avatar
Grant Likely committed
33 34
}

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
void __init arm_dt_memblock_reserve(void)
{
	u64 *reserve_map, base, size;

	if (!initial_boot_params)
		return;

	/* Reserve the dtb region */
	memblock_reserve(virt_to_phys(initial_boot_params),
			 be32_to_cpu(initial_boot_params->totalsize));

	/*
	 * Process the reserve map.  This will probably overlap the initrd
	 * and dtb locations which are already reserved, but overlaping
	 * doesn't hurt anything
	 */
	reserve_map = ((void*)initial_boot_params) +
			be32_to_cpu(initial_boot_params->off_mem_rsvmap);
	while (1) {
		base = be64_to_cpup(reserve_map++);
		size = be64_to_cpup(reserve_map++);
		if (!size)
			break;
		memblock_reserve(base, size);
	}
}

62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
#ifdef CONFIG_SMP
extern struct of_cpu_method __cpu_method_of_table_begin[];
extern struct of_cpu_method __cpu_method_of_table_end[];

static int __init set_smp_ops_by_method(struct device_node *node)
{
	const char *method;
	struct of_cpu_method *m = __cpu_method_of_table_begin;

	if (of_property_read_string(node, "enable-method", &method))
		return 0;

	for (; m < __cpu_method_of_table_end; m++)
		if (!strcmp(m->method, method)) {
			smp_set_ops(m->ops);
			return 1;
		}

	return 0;
}
#else
static inline int set_smp_ops_by_method(struct device_node *node)
{
	return 1;
}
#endif


90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
/*
 * arm_dt_init_cpu_maps - Function retrieves cpu nodes from the device tree
 * and builds the cpu logical map array containing MPIDR values related to
 * logical cpus
 *
 * Updates the cpu possible mask with the number of parsed cpu nodes
 */
void __init arm_dt_init_cpu_maps(void)
{
	/*
	 * Temp logical map is initialized with UINT_MAX values that are
	 * considered invalid logical map entries since the logical map must
	 * contain a list of MPIDR[23:0] values where MPIDR[31:24] must
	 * read as 0.
	 */
	struct device_node *cpu, *cpus;
106
	int found_method = 0;
107 108 109
	u32 i, j, cpuidx = 1;
	u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0;

110
	u32 tmp_map[NR_CPUS] = { [0 ... NR_CPUS-1] = MPIDR_INVALID };
111 112 113 114 115 116 117 118 119
	bool bootcpu_valid = false;
	cpus = of_find_node_by_path("/cpus");

	if (!cpus)
		return;

	for_each_child_of_node(cpus, cpu) {
		u32 hwid;

120 121 122
		if (of_node_cmp(cpu->type, "cpu"))
			continue;

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
		pr_debug(" * %s...\n", cpu->full_name);
		/*
		 * A device tree containing CPU nodes with missing "reg"
		 * properties is considered invalid to build the
		 * cpu_logical_map.
		 */
		if (of_property_read_u32(cpu, "reg", &hwid)) {
			pr_debug(" * %s missing reg property\n",
				     cpu->full_name);
			return;
		}

		/*
		 * 8 MSBs must be set to 0 in the DT since the reg property
		 * defines the MPIDR[23:0].
		 */
		if (hwid & ~MPIDR_HWID_BITMASK)
			return;

		/*
		 * Duplicate MPIDRs are a recipe for disaster.
		 * Scan all initialized entries and check for
		 * duplicates. If any is found just bail out.
		 * temp values were initialized to UINT_MAX
		 * to avoid matching valid MPIDR[23:0] values.
		 */
		for (j = 0; j < cpuidx; j++)
			if (WARN(tmp_map[j] == hwid, "Duplicate /cpu reg "
						     "properties in the DT\n"))
				return;

		/*
		 * Build a stashed array of MPIDR values. Numbering scheme
		 * requires that if detected the boot CPU must be assigned
		 * logical id 0. Other CPUs get sequential indexes starting
		 * from 1. If a CPU node with a reg property matching the
		 * boot CPU MPIDR is detected, this is recorded so that the
		 * logical map built from DT is validated and can be used
		 * to override the map created in smp_setup_processor_id().
		 */
		if (hwid == mpidr) {
			i = 0;
			bootcpu_valid = true;
		} else {
			i = cpuidx++;
		}

170 171 172 173
		if (WARN(cpuidx > nr_cpu_ids, "DT /cpu %u nodes greater than "
					       "max cores %u, capping them\n",
					       cpuidx, nr_cpu_ids)) {
			cpuidx = nr_cpu_ids;
174
			break;
175 176 177
		}

		tmp_map[i] = hwid;
178 179 180

		if (!found_method)
			found_method = set_smp_ops_by_method(cpu);
181 182
	}

183 184 185 186 187 188 189
	/*
	 * Fallback to an enable-method in the cpus node if nothing found in
	 * a cpu node.
	 */
	if (!found_method)
		set_smp_ops_by_method(cpus);

190 191
	if (!bootcpu_valid) {
		pr_warn("DT missing boot CPU MPIDR[23:0], fall back to default cpu_logical_map\n");
192
		return;
193
	}
194 195 196 197 198 199 200 201 202 203 204 205 206

	/*
	 * Since the boot CPU node contains proper data, and all nodes have
	 * a reg property, the DT CPU list can be considered valid and the
	 * logical map created in smp_setup_processor_id() can be overridden
	 */
	for (i = 0; i < cpuidx; i++) {
		set_cpu_possible(i, true);
		cpu_logical_map(i) = tmp_map[i];
		pr_debug("cpu logical map 0x%x\n", cpu_logical_map(i));
	}
}

207 208
bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
{
209
	return phys_id == cpu_logical_map(cpu);
210 211
}

212 213 214 215 216 217 218 219 220 221 222 223 224
static const void * __init arch_get_next_mach(const char *const **match)
{
	static const struct machine_desc *mdesc = __arch_info_begin;
	const struct machine_desc *m = mdesc;

	if (m >= __arch_info_end)
		return NULL;

	mdesc++;
	*match = m->dt_compat;
	return m;
}

225 226 227 228 229 230 231
/**
 * setup_machine_fdt - Machine setup when an dtb was passed to the kernel
 * @dt_phys: physical address of dt blob
 *
 * If a dtb was passed to the kernel in r2, then use it to choose the
 * correct machine_desc and to setup the system.
 */
232
const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
233
{
234
	const struct machine_desc *mdesc, *mdesc_best = NULL;
235

236 237 238 239
#ifdef CONFIG_ARCH_MULTIPLATFORM
	DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
	MACHINE_END

240
	mdesc_best = &__mach_desc_GENERIC_DT;
241 242
#endif

Rob Herring's avatar
Rob Herring committed
243
	if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys)))
244 245
		return NULL;

246
	mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
247

248
	if (!mdesc) {
249 250
		const char *prop;
		long size;
251
		unsigned long dt_root;
252 253 254 255

		early_print("\nError: unrecognized/unsupported "
			    "device tree compatible list:\n[ ");

256
		dt_root = of_get_flat_dt_root();
257 258 259 260 261 262 263 264 265 266 267 268
		prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
		while (size > 0) {
			early_print("'%s' ", prop);
			size -= strlen(prop) + 1;
			prop += strlen(prop) + 1;
		}
		early_print("]\n\n");

		dump_machine_table(); /* does not return */
	}

	/* Change machine number to match the mdesc we're using */
269
	__machine_arch_type = mdesc->nr;
270

271
	return mdesc;
272
}