1// SPDX-License-Identifier: GPL-2.0
2/*
3 * OF NUMA Parsing support.
4 *
5 * Copyright (C) 2015 - 2016 Cavium Inc.
6 */
7
8#define pr_fmt(fmt) "OF: NUMA: " fmt
9
10#include <linux/of.h>
11#include <linux/of_address.h>
12#include <linux/nodemask.h>
13
14#include <asm/numa.h>
15
16/* define default numa node to 0 */
17#define DEFAULT_NODE 0
18
19/*
20 * Even though we connect cpus to numa domains later in SMP
21 * init, we need to know the node ids now for all cpus.
22*/
23static void __init of_numa_parse_cpu_nodes(void)
24{
25	u32 nid;
26	int r;
27	struct device_node *np;
28
29	for_each_of_cpu_node(np) {
30		r = of_property_read_u32(np, "numa-node-id", &nid);
31		if (r)
32			continue;
33
34		pr_debug("CPU on %u\n", nid);
35		if (nid >= MAX_NUMNODES)
36			pr_warn("Node id %u exceeds maximum value\n", nid);
37		else
38			node_set(nid, numa_nodes_parsed);
39	}
40}
41
42static int __init of_numa_parse_memory_nodes(void)
43{
44	struct device_node *np = NULL;
45	struct resource rsrc;
46	u32 nid;
47	int i, r;
48
49	for_each_node_by_type(np, "memory") {
50		r = of_property_read_u32(np, "numa-node-id", &nid);
51		if (r == -EINVAL)
52			/*
53			 * property doesn't exist if -EINVAL, continue
54			 * looking for more memory nodes with
55			 * "numa-node-id" property
56			 */
57			continue;
58
59		if (nid >= MAX_NUMNODES) {
60			pr_warn("Node id %u exceeds maximum value\n", nid);
61			r = -EINVAL;
62		}
63
64		for (i = 0; !r && !of_address_to_resource(np, i, &rsrc); i++)
65			r = numa_add_memblk(nid, rsrc.start, rsrc.end + 1);
66
67		if (!i || r) {
68			of_node_put(np);
69			pr_err("bad property in memory node\n");
70			return r ? : -EINVAL;
71		}
72	}
73
74	return 0;
75}
76
77static int __init of_numa_parse_distance_map_v1(struct device_node *map)
78{
79	const __be32 *matrix;
80	int entry_count;
81	int i;
82
83	pr_info("parsing numa-distance-map-v1\n");
84
85	matrix = of_get_property(map, "distance-matrix", NULL);
86	if (!matrix) {
87		pr_err("No distance-matrix property in distance-map\n");
88		return -EINVAL;
89	}
90
91	entry_count = of_property_count_u32_elems(map, "distance-matrix");
92	if (entry_count <= 0) {
93		pr_err("Invalid distance-matrix\n");
94		return -EINVAL;
95	}
96
97	for (i = 0; i + 2 < entry_count; i += 3) {
98		u32 nodea, nodeb, distance;
99
100		nodea = of_read_number(matrix, 1);
101		matrix++;
102		nodeb = of_read_number(matrix, 1);
103		matrix++;
104		distance = of_read_number(matrix, 1);
105		matrix++;
106
107		if ((nodea == nodeb && distance != LOCAL_DISTANCE) ||
108		    (nodea != nodeb && distance <= LOCAL_DISTANCE)) {
109			pr_err("Invalid distance[node%d -> node%d] = %d\n",
110			       nodea, nodeb, distance);
111			return -EINVAL;
112		}
113
114		node_set(nodea, numa_nodes_parsed);
115
116		numa_set_distance(nodea, nodeb, distance);
117
118		/* Set default distance of node B->A same as A->B */
119		if (nodeb > nodea)
120			numa_set_distance(nodeb, nodea, distance);
121	}
122
123	return 0;
124}
125
126static int __init of_numa_parse_distance_map(void)
127{
128	int ret = 0;
129	struct device_node *np;
130
131	np = of_find_compatible_node(NULL, NULL,
132				     "numa-distance-map-v1");
133	if (np)
134		ret = of_numa_parse_distance_map_v1(np);
135
136	of_node_put(np);
137	return ret;
138}
139
140int of_node_to_nid(struct device_node *device)
141{
142	struct device_node *np;
143	u32 nid;
144	int r = -ENODATA;
145
146	np = of_node_get(device);
147
148	while (np) {
149		r = of_property_read_u32(np, "numa-node-id", &nid);
150		/*
151		 * -EINVAL indicates the property was not found, and
152		 *  we walk up the tree trying to find a parent with a
153		 *  "numa-node-id".  Any other type of error indicates
154		 *  a bad device tree and we give up.
155		 */
156		if (r != -EINVAL)
157			break;
158
159		np = of_get_next_parent(np);
160	}
161	if (np && r)
162		pr_warn("Invalid \"numa-node-id\" property in node %pOFn\n",
163			np);
164	of_node_put(np);
165
166	/*
167	 * If numa=off passed on command line, or with a defective
168	 * device tree, the nid may not be in the set of possible
169	 * nodes.  Check for this case and return NUMA_NO_NODE.
170	 */
171	if (!r && nid < MAX_NUMNODES && node_possible(nid))
172		return nid;
173
174	return NUMA_NO_NODE;
175}
176
177int __init of_numa_init(void)
178{
179	int r;
180
181	of_numa_parse_cpu_nodes();
182	r = of_numa_parse_memory_nodes();
183	if (r)
184		return r;
185	return of_numa_parse_distance_map();
186}
187