1 /* Copyright 2013-2014 IBM Corp.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 * implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <skiboot.h>
18 #include "spira.h"
19 #include <cpu.h>
20 #include <fsp.h>
21 #include <opal.h>
22 #include <ccan/str/str.h>
23 #include <device.h>
24 #include <types.h>
25
26 #include "hdata.h"
27
28 #define PACA_MAX_THREADS 4
29
paca_index(const struct HDIF_common_hdr * paca)30 static unsigned int paca_index(const struct HDIF_common_hdr *paca)
31 {
32 void *start = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG);
33 return ((void *)paca - start)
34 / be32_to_cpu(spira.ntuples.paca.alloc_len);
35 }
36
add_cpu_node(struct dt_node * cpus,const struct HDIF_common_hdr * paca,const struct sppaca_cpu_id * id,bool okay)37 static struct dt_node *add_cpu_node(struct dt_node *cpus,
38 const struct HDIF_common_hdr *paca,
39 const struct sppaca_cpu_id *id,
40 bool okay)
41 {
42 const struct sppaca_cpu_timebase *timebase;
43 const struct sppaca_cpu_cache *cache;
44 const struct sppaca_cpu_attr *attr;
45 struct dt_node *cpu;
46 u32 no, size, ve_flags, l2_phandle, chip_id;
47
48 /* We use the process_interrupt_line as the res id */
49 no = be32_to_cpu(id->process_interrupt_line);
50
51 ve_flags = be32_to_cpu(id->verify_exists_flags);
52 prlog(PR_INFO, "CPU[%i]: PIR=%i RES=%i %s %s(%u threads)\n",
53 paca_index(paca), be32_to_cpu(id->pir), no,
54 ve_flags & CPU_ID_PACA_RESERVED
55 ? "**RESERVED**" : cpu_state(ve_flags),
56 ve_flags & CPU_ID_SECONDARY_THREAD
57 ? "[secondary] " :
58 (be32_to_cpu(id->pir) == boot_cpu->pir ? "[boot] " : ""),
59 ((ve_flags & CPU_ID_NUM_SECONDARY_THREAD_MASK)
60 >> CPU_ID_NUM_SECONDARY_THREAD_SHIFT) + 1);
61
62 timebase = HDIF_get_idata(paca, SPPACA_IDATA_TIMEBASE, &size);
63 if (!timebase || size < sizeof(*timebase)) {
64 prerror("CPU[%i]: bad timebase size %u @ %p\n",
65 paca_index(paca), size, timebase);
66 return NULL;
67 }
68
69 cache = HDIF_get_idata(paca, SPPACA_IDATA_CACHE_SIZE, &size);
70 if (!cache || size < sizeof(*cache)) {
71 prerror("CPU[%i]: bad cache size %u @ %p\n",
72 paca_index(paca), size, cache);
73 return NULL;
74 }
75
76 cpu = add_core_common(cpus, cache, timebase, no, okay);
77
78 /* Core attributes */
79 attr = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ATTR, &size);
80 if (attr)
81 add_core_attr(cpu, be32_to_cpu(attr->attr));
82
83 /* Add cache info */
84 l2_phandle = add_core_cache_info(cpus, cache, no, okay);
85 dt_add_property_cells(cpu, "l2-cache", l2_phandle);
86
87 /* We append the secondary cpus in __cpu_parse */
88 dt_add_property_cells(cpu, "ibm,ppc-interrupt-server#s", no);
89
90 dt_add_property_cells(cpu, DT_PRIVATE "hw_proc_id",
91 be32_to_cpu(id->hardware_proc_id));
92 dt_add_property_cells(cpu, "ibm,pir", be32_to_cpu(id->pir));
93
94 chip_id = pcid_to_chip_id(be32_to_cpu(id->processor_chip_id));
95 dt_add_property_cells(cpu, "ibm,chip-id", chip_id);
96
97 return cpu;
98 }
99
find_cpu_by_hardware_proc_id(struct dt_node * root,u32 hw_proc_id)100 static struct dt_node *find_cpu_by_hardware_proc_id(struct dt_node *root,
101 u32 hw_proc_id)
102 {
103 struct dt_node *i;
104
105 dt_for_each_node(root, i) {
106 const struct dt_property *prop;
107
108 if (!dt_has_node_property(i, "device_type", "cpu"))
109 continue;
110
111 prop = dt_find_property(i, DT_PRIVATE "hw_proc_id");
112 if (!prop)
113 return NULL;
114
115 if (be32_to_cpu(*(be32 *)prop->prop) == hw_proc_id)
116 return i;
117 }
118 return NULL;
119 }
120
121 /* Note that numbers are small. */
add_be32_sorted(__be32 arr[],__be32 new,unsigned num)122 static void add_be32_sorted(__be32 arr[], __be32 new, unsigned num)
123 {
124 unsigned int i;
125
126 /* Walk until we find where we belong (insertion sort). */
127 for (i = 0; i < num; i++) {
128 if (be32_to_cpu(new) < be32_to_cpu(arr[i])) {
129 __be32 tmp = arr[i];
130 arr[i] = new;
131 new = tmp;
132 }
133 }
134 arr[i] = new;
135 }
136
add_xics_icps(void)137 static void add_xics_icps(void)
138 {
139 struct dt_node *cpu;
140 unsigned int i;
141 u64 reg[PACA_MAX_THREADS * 2];
142 struct dt_node *icp;
143
144 dt_for_each_node(dt_root, cpu) {
145 u32 irange[2], size, pir;
146 const struct dt_property *intsrv;
147 const struct HDIF_common_hdr *paca;
148 u64 ibase;
149 unsigned int num_threads;
150 bool found = false;
151
152 if (!dt_has_node_property(cpu, "device_type", "cpu"))
153 continue;
154
155 intsrv = dt_find_property(cpu, "ibm,ppc-interrupt-server#s");
156 if (!intsrv)
157 continue;
158
159 pir = dt_prop_get_u32(cpu, "ibm,pir");
160
161 /* Get ibase address */
162 paca = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG);
163 for_each_paca(paca) {
164 const struct sppaca_cpu_id *id;
165 id = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ID, &size);
166
167 if (!CHECK_SPPTR(id))
168 continue;
169
170 if (pir != be32_to_cpu(id->pir))
171 continue;
172 ibase = cleanup_addr(be64_to_cpu(id->ibase));
173 found = true;
174 break;
175 }
176 if (!found)
177 return;
178
179 num_threads = intsrv->len / sizeof(u32);
180 assert(num_threads <= PACA_MAX_THREADS);
181
182 icp = dt_new_addr(dt_root, "interrupt-controller", ibase);
183 if (!icp)
184 continue;
185
186 dt_add_property_strings(icp, "compatible",
187 "IBM,ppc-xicp",
188 "IBM,power7-xicp");
189
190 irange[0] = dt_property_get_cell(intsrv, 0); /* Index */
191 irange[1] = num_threads; /* num servers */
192 dt_add_property(icp, "ibm,interrupt-server-ranges",
193 irange, sizeof(irange));
194 dt_add_property(icp, "interrupt-controller", NULL, 0);
195 dt_add_property_cells(icp, "#address-cells", 0);
196 dt_add_property_string(icp, "device_type",
197 "PowerPC-External-Interrupt-Presentation");
198 for (i = 0; i < num_threads*2; i += 2) {
199 reg[i] = ibase;
200 /* One page is enough for a handful of regs. */
201 reg[i+1] = 4096;
202 ibase += reg[i+1];
203 }
204 dt_add_property(icp, "reg", reg, sizeof(reg));
205 }
206 }
207
__paca_parse(void)208 static bool __paca_parse(void)
209 {
210 const struct HDIF_common_hdr *paca;
211 struct dt_node *cpus;
212
213 paca = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG);
214 if (!paca) {
215 prerror("Invalid PACA (PCIA = %p)\n",
216 ntuple_addr(&spira.ntuples.pcia));
217 return false;
218 }
219
220 if (be32_to_cpu(spira.ntuples.paca.act_len) < sizeof(*paca)) {
221 prerror("PACA: invalid size %u\n",
222 be32_to_cpu(spira.ntuples.paca.act_len));
223 return false;
224 }
225
226 cpus = dt_new(dt_root, "cpus");
227 dt_add_property_cells(cpus, "#address-cells", 1);
228 dt_add_property_cells(cpus, "#size-cells", 0);
229
230 for_each_paca(paca) {
231 const struct sppaca_cpu_id *id;
232 u32 size, ve_flags;
233 bool okay;
234
235 id = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ID, &size);
236
237 /* The ID structure on Blade314 is only 0x54 long. We can
238 * cope with it as we don't use all the additional fields.
239 * The minimum size we support is 0x40
240 */
241 if (!id || size < SPIRA_CPU_ID_MIN_SIZE) {
242 prerror("CPU[%i]: bad id size %u @ %p\n",
243 paca_index(paca), size, id);
244 return false;
245 }
246
247 ve_flags = be32_to_cpu(id->verify_exists_flags);
248 switch ((ve_flags&CPU_ID_VERIFY_MASK) >> CPU_ID_VERIFY_SHIFT) {
249 case CPU_ID_VERIFY_USABLE_NO_FAILURES:
250 case CPU_ID_VERIFY_USABLE_FAILURES:
251 okay = true;
252 break;
253 default:
254 okay = false;
255 }
256
257 prlog(PR_INFO, "CPU[%i]: PIR=%i RES=%i %s\n",
258 paca_index(paca), be32_to_cpu(id->pir),
259 be32_to_cpu(id->process_interrupt_line),
260 okay ? "OK" : "UNAVAILABLE");
261
262 /* Secondary threads don't get their own node. */
263 if (ve_flags & CPU_ID_SECONDARY_THREAD)
264 continue;
265
266 if (!add_cpu_node(cpus, paca, id, okay))
267 return false;
268 }
269
270 /* Now account for secondaries. */
271 for_each_paca(paca) {
272 const struct dt_property *prop;
273 const struct sppaca_cpu_id *id;
274 u32 size, state, num, ve_flags;
275 struct dt_node *cpu;
276 __be32 *new_prop;
277
278 id = HDIF_get_idata(paca, 2, &size);
279 if (!CHECK_SPPTR(id))
280 continue;
281
282 ve_flags = be32_to_cpu(id->verify_exists_flags);
283 state = (ve_flags & CPU_ID_VERIFY_MASK) >> CPU_ID_VERIFY_SHIFT;
284 switch (state) {
285 case CPU_ID_VERIFY_USABLE_NO_FAILURES:
286 case CPU_ID_VERIFY_USABLE_FAILURES:
287 break;
288 default:
289 continue;
290 }
291
292 /* Only interested in secondary threads. */
293 if (!(ve_flags & CPU_ID_SECONDARY_THREAD))
294 continue;
295
296 cpu = find_cpu_by_hardware_proc_id(cpus,
297 be32_to_cpu(id->hardware_proc_id));
298 if (!cpu) {
299 prerror("CPU[%i]: could not find primary hwid %i\n",
300 paca_index(paca),
301 be32_to_cpu(id->hardware_proc_id));
302 return false;
303 }
304
305 /* Add the cpu #. */
306 prop = dt_find_property(cpu, "ibm,ppc-interrupt-server#s");
307 if (!prop) {
308 prerror("CPU[%i]: could not find mapping information\n",
309 paca_index(paca));
310 return false;
311 }
312 num = prop->len / sizeof(u32);
313 new_prop = malloc((num + 1) * sizeof(u32));
314 if (!new_prop) {
315 prerror("Property allocation length %zu failed\n",
316 (num + 1) * sizeof(u32));
317 return false;
318 }
319 memcpy(new_prop, prop->prop, prop->len);
320 add_be32_sorted(new_prop, id->process_interrupt_line, num);
321 dt_del_property(cpu, (struct dt_property *)prop);
322 dt_add_property(cpu, "ibm,ppc-interrupt-server#s",
323 new_prop, (num + 1) * sizeof(__be32));
324 free(new_prop);
325 }
326
327 /*
328 * P7 and P8 use the XICS interrupt controller which has a per-core
329 * interrupt controller node.
330 */
331 if (proc_gen <= proc_gen_p8)
332 add_xics_icps();
333
334 return true;
335 }
336
paca_parse(void)337 int paca_parse(void)
338 {
339 if (!__paca_parse()) {
340 prerror("CPU: Initial CPU parsing failed\n");
341 return -1;
342 }
343 return 0;
344 }
345