diff options
Diffstat (limited to 'arch/powerpc/platforms/iseries/dt.c')
-rw-r--r-- | arch/powerpc/platforms/iseries/dt.c | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/iseries/dt.c b/arch/powerpc/platforms/iseries/dt.c new file mode 100644 index 000000000000..d3444aabe76e --- /dev/null +++ b/arch/powerpc/platforms/iseries/dt.c | |||
@@ -0,0 +1,615 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2005-2006 Michael Ellerman, IBM Corporation | ||
3 | * | ||
4 | * Description: | ||
5 | * This file contains all the routines to build a flattened device | ||
6 | * tree for a legacy iSeries machine. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * as published by the Free Software Foundation; either version | ||
11 | * 2 of the License, or (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #undef DEBUG | ||
15 | |||
16 | #include <linux/types.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/pci.h> | ||
19 | #include <linux/pci_regs.h> | ||
20 | #include <linux/pci_ids.h> | ||
21 | #include <linux/threads.h> | ||
22 | #include <linux/bitops.h> | ||
23 | #include <linux/string.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/if_ether.h> /* ETH_ALEN */ | ||
26 | |||
27 | #include <asm/machdep.h> | ||
28 | #include <asm/prom.h> | ||
29 | #include <asm/lppaca.h> | ||
30 | #include <asm/cputable.h> | ||
31 | #include <asm/abs_addr.h> | ||
32 | #include <asm/system.h> | ||
33 | #include <asm/iseries/hv_types.h> | ||
34 | #include <asm/iseries/hv_lp_config.h> | ||
35 | #include <asm/iseries/hv_call_xm.h> | ||
36 | #include <asm/iseries/it_exp_vpd_panel.h> | ||
37 | #include <asm/udbg.h> | ||
38 | |||
39 | #include "processor_vpd.h" | ||
40 | #include "call_hpt.h" | ||
41 | #include "call_pci.h" | ||
42 | #include "pci.h" | ||
43 | |||
44 | #ifdef DEBUG | ||
45 | #define DBG(fmt...) udbg_printf(fmt) | ||
46 | #else | ||
47 | #define DBG(fmt...) | ||
48 | #endif | ||
49 | |||
50 | /* | ||
51 | * These are created by the linker script at the start and end | ||
52 | * of the section containing all the strings from this file. | ||
53 | */ | ||
54 | extern char __dt_strings_start[]; | ||
55 | extern char __dt_strings_end[]; | ||
56 | |||
57 | struct iseries_flat_dt { | ||
58 | struct boot_param_header header; | ||
59 | u64 reserve_map[2]; | ||
60 | }; | ||
61 | |||
62 | static void * __initdata dt_data; | ||
63 | |||
64 | /* | ||
65 | * Putting these strings here keeps them out of the section | ||
66 | * that we rename to .dt_strings using objcopy and capture | ||
67 | * for the strings blob of the flattened device tree. | ||
68 | */ | ||
69 | static char __initdata device_type_cpu[] = "cpu"; | ||
70 | static char __initdata device_type_memory[] = "memory"; | ||
71 | static char __initdata device_type_serial[] = "serial"; | ||
72 | static char __initdata device_type_network[] = "network"; | ||
73 | static char __initdata device_type_block[] = "block"; | ||
74 | static char __initdata device_type_byte[] = "byte"; | ||
75 | static char __initdata device_type_pci[] = "pci"; | ||
76 | static char __initdata device_type_vdevice[] = "vdevice"; | ||
77 | static char __initdata device_type_vscsi[] = "vscsi"; | ||
78 | |||
79 | static struct iseries_flat_dt * __init dt_init(void) | ||
80 | { | ||
81 | struct iseries_flat_dt *dt; | ||
82 | unsigned long str_len; | ||
83 | |||
84 | str_len = __dt_strings_end - __dt_strings_start; | ||
85 | dt = (struct iseries_flat_dt *)ALIGN(klimit, 8); | ||
86 | dt->header.off_mem_rsvmap = | ||
87 | offsetof(struct iseries_flat_dt, reserve_map); | ||
88 | dt->header.off_dt_strings = ALIGN(sizeof(*dt), 8); | ||
89 | dt->header.off_dt_struct = dt->header.off_dt_strings | ||
90 | + ALIGN(str_len, 8); | ||
91 | dt_data = (void *)((unsigned long)dt + dt->header.off_dt_struct); | ||
92 | dt->header.dt_strings_size = str_len; | ||
93 | |||
94 | /* There is no notion of hardware cpu id on iSeries */ | ||
95 | dt->header.boot_cpuid_phys = smp_processor_id(); | ||
96 | |||
97 | memcpy((char *)dt + dt->header.off_dt_strings, __dt_strings_start, | ||
98 | str_len); | ||
99 | |||
100 | dt->header.magic = OF_DT_HEADER; | ||
101 | dt->header.version = 0x10; | ||
102 | dt->header.last_comp_version = 0x10; | ||
103 | |||
104 | dt->reserve_map[0] = 0; | ||
105 | dt->reserve_map[1] = 0; | ||
106 | |||
107 | return dt; | ||
108 | } | ||
109 | |||
110 | static void __init dt_push_u32(struct iseries_flat_dt *dt, u32 value) | ||
111 | { | ||
112 | *((u32 *)dt_data) = value; | ||
113 | dt_data += sizeof(u32); | ||
114 | } | ||
115 | |||
116 | #ifdef notyet | ||
117 | static void __init dt_push_u64(struct iseries_flat_dt *dt, u64 value) | ||
118 | { | ||
119 | *((u64 *)dt_data) = value; | ||
120 | dt_data += sizeof(u64); | ||
121 | } | ||
122 | #endif | ||
123 | |||
124 | static void __init dt_push_bytes(struct iseries_flat_dt *dt, const char *data, | ||
125 | int len) | ||
126 | { | ||
127 | memcpy(dt_data, data, len); | ||
128 | dt_data += ALIGN(len, 4); | ||
129 | } | ||
130 | |||
131 | static void __init dt_start_node(struct iseries_flat_dt *dt, const char *name) | ||
132 | { | ||
133 | dt_push_u32(dt, OF_DT_BEGIN_NODE); | ||
134 | dt_push_bytes(dt, name, strlen(name) + 1); | ||
135 | } | ||
136 | |||
137 | #define dt_end_node(dt) dt_push_u32(dt, OF_DT_END_NODE) | ||
138 | |||
139 | static void __init dt_prop(struct iseries_flat_dt *dt, const char *name, | ||
140 | const void *data, int len) | ||
141 | { | ||
142 | unsigned long offset; | ||
143 | |||
144 | dt_push_u32(dt, OF_DT_PROP); | ||
145 | |||
146 | /* Length of the data */ | ||
147 | dt_push_u32(dt, len); | ||
148 | |||
149 | offset = name - __dt_strings_start; | ||
150 | |||
151 | /* The offset of the properties name in the string blob. */ | ||
152 | dt_push_u32(dt, (u32)offset); | ||
153 | |||
154 | /* The actual data. */ | ||
155 | dt_push_bytes(dt, data, len); | ||
156 | } | ||
157 | |||
158 | static void __init dt_prop_str(struct iseries_flat_dt *dt, const char *name, | ||
159 | const char *data) | ||
160 | { | ||
161 | dt_prop(dt, name, data, strlen(data) + 1); /* + 1 for NULL */ | ||
162 | } | ||
163 | |||
164 | static void __init dt_prop_u32(struct iseries_flat_dt *dt, const char *name, | ||
165 | u32 data) | ||
166 | { | ||
167 | dt_prop(dt, name, &data, sizeof(u32)); | ||
168 | } | ||
169 | |||
170 | #ifdef notyet | ||
171 | static void __init dt_prop_u64(struct iseries_flat_dt *dt, const char *name, | ||
172 | u64 data) | ||
173 | { | ||
174 | dt_prop(dt, name, &data, sizeof(u64)); | ||
175 | } | ||
176 | #endif | ||
177 | |||
178 | static void __init dt_prop_u64_list(struct iseries_flat_dt *dt, | ||
179 | const char *name, u64 *data, int n) | ||
180 | { | ||
181 | dt_prop(dt, name, data, sizeof(u64) * n); | ||
182 | } | ||
183 | |||
184 | static void __init dt_prop_u32_list(struct iseries_flat_dt *dt, | ||
185 | const char *name, u32 *data, int n) | ||
186 | { | ||
187 | dt_prop(dt, name, data, sizeof(u32) * n); | ||
188 | } | ||
189 | |||
190 | #ifdef notyet | ||
191 | static void __init dt_prop_empty(struct iseries_flat_dt *dt, const char *name) | ||
192 | { | ||
193 | dt_prop(dt, name, NULL, 0); | ||
194 | } | ||
195 | #endif | ||
196 | |||
197 | static void __init dt_cpus(struct iseries_flat_dt *dt) | ||
198 | { | ||
199 | unsigned char buf[32]; | ||
200 | unsigned char *p; | ||
201 | unsigned int i, index; | ||
202 | struct IoHriProcessorVpd *d; | ||
203 | u32 pft_size[2]; | ||
204 | |||
205 | /* yuck */ | ||
206 | snprintf(buf, 32, "PowerPC,%s", cur_cpu_spec->cpu_name); | ||
207 | p = strchr(buf, ' '); | ||
208 | if (!p) p = buf + strlen(buf); | ||
209 | |||
210 | dt_start_node(dt, "cpus"); | ||
211 | dt_prop_u32(dt, "#address-cells", 1); | ||
212 | dt_prop_u32(dt, "#size-cells", 0); | ||
213 | |||
214 | pft_size[0] = 0; /* NUMA CEC cookie, 0 for non NUMA */ | ||
215 | pft_size[1] = __ilog2(HvCallHpt_getHptPages() * HW_PAGE_SIZE); | ||
216 | |||
217 | for (i = 0; i < NR_CPUS; i++) { | ||
218 | if (lppaca[i].dyn_proc_status >= 2) | ||
219 | continue; | ||
220 | |||
221 | snprintf(p, 32 - (p - buf), "@%d", i); | ||
222 | dt_start_node(dt, buf); | ||
223 | |||
224 | dt_prop_str(dt, "device_type", device_type_cpu); | ||
225 | |||
226 | index = lppaca[i].dyn_hv_phys_proc_index; | ||
227 | d = &xIoHriProcessorVpd[index]; | ||
228 | |||
229 | dt_prop_u32(dt, "i-cache-size", d->xInstCacheSize * 1024); | ||
230 | dt_prop_u32(dt, "i-cache-line-size", d->xInstCacheOperandSize); | ||
231 | |||
232 | dt_prop_u32(dt, "d-cache-size", d->xDataL1CacheSizeKB * 1024); | ||
233 | dt_prop_u32(dt, "d-cache-line-size", d->xDataCacheOperandSize); | ||
234 | |||
235 | /* magic conversions to Hz copied from old code */ | ||
236 | dt_prop_u32(dt, "clock-frequency", | ||
237 | ((1UL << 34) * 1000000) / d->xProcFreq); | ||
238 | dt_prop_u32(dt, "timebase-frequency", | ||
239 | ((1UL << 32) * 1000000) / d->xTimeBaseFreq); | ||
240 | |||
241 | dt_prop_u32(dt, "reg", i); | ||
242 | |||
243 | dt_prop_u32_list(dt, "ibm,pft-size", pft_size, 2); | ||
244 | |||
245 | dt_end_node(dt); | ||
246 | } | ||
247 | |||
248 | dt_end_node(dt); | ||
249 | } | ||
250 | |||
251 | static void __init dt_model(struct iseries_flat_dt *dt) | ||
252 | { | ||
253 | char buf[16] = "IBM,"; | ||
254 | |||
255 | /* "IBM," + mfgId[2:3] + systemSerial[1:5] */ | ||
256 | strne2a(buf + 4, xItExtVpdPanel.mfgID + 2, 2); | ||
257 | strne2a(buf + 6, xItExtVpdPanel.systemSerial + 1, 5); | ||
258 | buf[11] = '\0'; | ||
259 | dt_prop_str(dt, "system-id", buf); | ||
260 | |||
261 | /* "IBM," + machineType[0:4] */ | ||
262 | strne2a(buf + 4, xItExtVpdPanel.machineType, 4); | ||
263 | buf[8] = '\0'; | ||
264 | dt_prop_str(dt, "model", buf); | ||
265 | |||
266 | dt_prop_str(dt, "compatible", "IBM,iSeries"); | ||
267 | } | ||
268 | |||
269 | static void __init dt_do_vdevice(struct iseries_flat_dt *dt, | ||
270 | const char *name, u32 reg, int unit, | ||
271 | const char *type, const char *compat, int end) | ||
272 | { | ||
273 | char buf[32]; | ||
274 | |||
275 | snprintf(buf, 32, "%s@%08x", name, reg + ((unit >= 0) ? unit : 0)); | ||
276 | dt_start_node(dt, buf); | ||
277 | dt_prop_str(dt, "device_type", type); | ||
278 | if (compat) | ||
279 | dt_prop_str(dt, "compatible", compat); | ||
280 | dt_prop_u32(dt, "reg", reg + ((unit >= 0) ? unit : 0)); | ||
281 | if (unit >= 0) | ||
282 | dt_prop_u32(dt, "linux,unit_address", unit); | ||
283 | if (end) | ||
284 | dt_end_node(dt); | ||
285 | } | ||
286 | |||
287 | static void __init dt_vdevices(struct iseries_flat_dt *dt) | ||
288 | { | ||
289 | u32 reg = 0; | ||
290 | HvLpIndexMap vlan_map; | ||
291 | int i; | ||
292 | |||
293 | dt_start_node(dt, "vdevice"); | ||
294 | dt_prop_str(dt, "device_type", device_type_vdevice); | ||
295 | dt_prop_str(dt, "compatible", "IBM,iSeries-vdevice"); | ||
296 | dt_prop_u32(dt, "#address-cells", 1); | ||
297 | dt_prop_u32(dt, "#size-cells", 0); | ||
298 | |||
299 | dt_do_vdevice(dt, "vty", reg, -1, device_type_serial, NULL, 1); | ||
300 | reg++; | ||
301 | |||
302 | dt_do_vdevice(dt, "v-scsi", reg, -1, device_type_vscsi, | ||
303 | "IBM,v-scsi", 1); | ||
304 | reg++; | ||
305 | |||
306 | vlan_map = HvLpConfig_getVirtualLanIndexMap(); | ||
307 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) { | ||
308 | unsigned char mac_addr[ETH_ALEN]; | ||
309 | |||
310 | if ((vlan_map & (0x8000 >> i)) == 0) | ||
311 | continue; | ||
312 | dt_do_vdevice(dt, "l-lan", reg, i, device_type_network, | ||
313 | "IBM,iSeries-l-lan", 0); | ||
314 | mac_addr[0] = 0x02; | ||
315 | mac_addr[1] = 0x01; | ||
316 | mac_addr[2] = 0xff; | ||
317 | mac_addr[3] = i; | ||
318 | mac_addr[4] = 0xff; | ||
319 | mac_addr[5] = HvLpConfig_getLpIndex_outline(); | ||
320 | dt_prop(dt, "local-mac-address", (char *)mac_addr, ETH_ALEN); | ||
321 | dt_prop(dt, "mac-address", (char *)mac_addr, ETH_ALEN); | ||
322 | dt_prop_u32(dt, "max-frame-size", 9000); | ||
323 | dt_prop_u32(dt, "address-bits", 48); | ||
324 | |||
325 | dt_end_node(dt); | ||
326 | } | ||
327 | reg += HVMAXARCHITECTEDVIRTUALLANS; | ||
328 | |||
329 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALDISKS; i++) | ||
330 | dt_do_vdevice(dt, "viodasd", reg, i, device_type_block, | ||
331 | "IBM,iSeries-viodasd", 1); | ||
332 | reg += HVMAXARCHITECTEDVIRTUALDISKS; | ||
333 | |||
334 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALCDROMS; i++) | ||
335 | dt_do_vdevice(dt, "viocd", reg, i, device_type_block, | ||
336 | "IBM,iSeries-viocd", 1); | ||
337 | reg += HVMAXARCHITECTEDVIRTUALCDROMS; | ||
338 | |||
339 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALTAPES; i++) | ||
340 | dt_do_vdevice(dt, "viotape", reg, i, device_type_byte, | ||
341 | "IBM,iSeries-viotape", 1); | ||
342 | |||
343 | dt_end_node(dt); | ||
344 | } | ||
345 | |||
346 | struct pci_class_name { | ||
347 | u16 code; | ||
348 | const char *name; | ||
349 | const char *type; | ||
350 | }; | ||
351 | |||
352 | static struct pci_class_name __initdata pci_class_name[] = { | ||
353 | { PCI_CLASS_NETWORK_ETHERNET, "ethernet", device_type_network }, | ||
354 | }; | ||
355 | |||
356 | static struct pci_class_name * __init dt_find_pci_class_name(u16 class_code) | ||
357 | { | ||
358 | struct pci_class_name *cp; | ||
359 | |||
360 | for (cp = pci_class_name; | ||
361 | cp < &pci_class_name[ARRAY_SIZE(pci_class_name)]; cp++) | ||
362 | if (cp->code == class_code) | ||
363 | return cp; | ||
364 | return NULL; | ||
365 | } | ||
366 | |||
367 | /* | ||
368 | * This assumes that the node slot is always on the primary bus! | ||
369 | */ | ||
370 | static void __init scan_bridge_slot(struct iseries_flat_dt *dt, | ||
371 | HvBusNumber bus, struct HvCallPci_BridgeInfo *bridge_info) | ||
372 | { | ||
373 | HvSubBusNumber sub_bus = bridge_info->subBusNumber; | ||
374 | u16 vendor_id; | ||
375 | u16 device_id; | ||
376 | u32 class_id; | ||
377 | int err; | ||
378 | char buf[32]; | ||
379 | u32 reg[5]; | ||
380 | int id_sel = ISERIES_GET_DEVICE_FROM_SUBBUS(sub_bus); | ||
381 | int function = ISERIES_GET_FUNCTION_FROM_SUBBUS(sub_bus); | ||
382 | HvAgentId eads_id_sel = ISERIES_PCI_AGENTID(id_sel, function); | ||
383 | u8 devfn; | ||
384 | struct pci_class_name *cp; | ||
385 | |||
386 | /* | ||
387 | * Connect all functions of any device found. | ||
388 | */ | ||
389 | for (id_sel = 1; id_sel <= bridge_info->maxAgents; id_sel++) { | ||
390 | for (function = 0; function < 8; function++) { | ||
391 | HvAgentId agent_id = ISERIES_PCI_AGENTID(id_sel, | ||
392 | function); | ||
393 | err = HvCallXm_connectBusUnit(bus, sub_bus, | ||
394 | agent_id, 0); | ||
395 | if (err) { | ||
396 | if (err != 0x302) | ||
397 | DBG("connectBusUnit(%x, %x, %x) %x\n", | ||
398 | bus, sub_bus, agent_id, err); | ||
399 | continue; | ||
400 | } | ||
401 | |||
402 | err = HvCallPci_configLoad16(bus, sub_bus, agent_id, | ||
403 | PCI_VENDOR_ID, &vendor_id); | ||
404 | if (err) { | ||
405 | DBG("ReadVendor(%x, %x, %x) %x\n", | ||
406 | bus, sub_bus, agent_id, err); | ||
407 | continue; | ||
408 | } | ||
409 | err = HvCallPci_configLoad16(bus, sub_bus, agent_id, | ||
410 | PCI_DEVICE_ID, &device_id); | ||
411 | if (err) { | ||
412 | DBG("ReadDevice(%x, %x, %x) %x\n", | ||
413 | bus, sub_bus, agent_id, err); | ||
414 | continue; | ||
415 | } | ||
416 | err = HvCallPci_configLoad32(bus, sub_bus, agent_id, | ||
417 | PCI_CLASS_REVISION , &class_id); | ||
418 | if (err) { | ||
419 | DBG("ReadClass(%x, %x, %x) %x\n", | ||
420 | bus, sub_bus, agent_id, err); | ||
421 | continue; | ||
422 | } | ||
423 | |||
424 | devfn = PCI_DEVFN(ISERIES_ENCODE_DEVICE(eads_id_sel), | ||
425 | function); | ||
426 | cp = dt_find_pci_class_name(class_id >> 16); | ||
427 | if (cp && cp->name) | ||
428 | strncpy(buf, cp->name, sizeof(buf) - 1); | ||
429 | else | ||
430 | snprintf(buf, sizeof(buf), "pci%x,%x", | ||
431 | vendor_id, device_id); | ||
432 | buf[sizeof(buf) - 1] = '\0'; | ||
433 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
434 | "@%x", PCI_SLOT(devfn)); | ||
435 | buf[sizeof(buf) - 1] = '\0'; | ||
436 | if (function != 0) | ||
437 | snprintf(buf + strlen(buf), | ||
438 | sizeof(buf) - strlen(buf), | ||
439 | ",%x", function); | ||
440 | dt_start_node(dt, buf); | ||
441 | reg[0] = (bus << 16) | (devfn << 8); | ||
442 | reg[1] = 0; | ||
443 | reg[2] = 0; | ||
444 | reg[3] = 0; | ||
445 | reg[4] = 0; | ||
446 | dt_prop_u32_list(dt, "reg", reg, 5); | ||
447 | if (cp && (cp->type || cp->name)) | ||
448 | dt_prop_str(dt, "device_type", | ||
449 | cp->type ? cp->type : cp->name); | ||
450 | dt_prop_u32(dt, "vendor-id", vendor_id); | ||
451 | dt_prop_u32(dt, "device-id", device_id); | ||
452 | dt_prop_u32(dt, "class-code", class_id >> 8); | ||
453 | dt_prop_u32(dt, "revision-id", class_id & 0xff); | ||
454 | dt_prop_u32(dt, "linux,subbus", sub_bus); | ||
455 | dt_prop_u32(dt, "linux,agent-id", agent_id); | ||
456 | dt_prop_u32(dt, "linux,logical-slot-number", | ||
457 | bridge_info->logicalSlotNumber); | ||
458 | dt_end_node(dt); | ||
459 | |||
460 | } | ||
461 | } | ||
462 | } | ||
463 | |||
464 | static void __init scan_bridge(struct iseries_flat_dt *dt, HvBusNumber bus, | ||
465 | HvSubBusNumber sub_bus, int id_sel) | ||
466 | { | ||
467 | struct HvCallPci_BridgeInfo bridge_info; | ||
468 | HvAgentId agent_id; | ||
469 | int function; | ||
470 | int ret; | ||
471 | |||
472 | /* Note: hvSubBus and irq is always be 0 at this level! */ | ||
473 | for (function = 0; function < 8; ++function) { | ||
474 | agent_id = ISERIES_PCI_AGENTID(id_sel, function); | ||
475 | ret = HvCallXm_connectBusUnit(bus, sub_bus, agent_id, 0); | ||
476 | if (ret != 0) { | ||
477 | if (ret != 0xb) | ||
478 | DBG("connectBusUnit(%x, %x, %x) %x\n", | ||
479 | bus, sub_bus, agent_id, ret); | ||
480 | continue; | ||
481 | } | ||
482 | DBG("found device at bus %d idsel %d func %d (AgentId %x)\n", | ||
483 | bus, id_sel, function, agent_id); | ||
484 | ret = HvCallPci_getBusUnitInfo(bus, sub_bus, agent_id, | ||
485 | iseries_hv_addr(&bridge_info), | ||
486 | sizeof(struct HvCallPci_BridgeInfo)); | ||
487 | if (ret != 0) | ||
488 | continue; | ||
489 | DBG("bridge info: type %x subbus %x " | ||
490 | "maxAgents %x maxsubbus %x logslot %x\n", | ||
491 | bridge_info.busUnitInfo.deviceType, | ||
492 | bridge_info.subBusNumber, | ||
493 | bridge_info.maxAgents, | ||
494 | bridge_info.maxSubBusNumber, | ||
495 | bridge_info.logicalSlotNumber); | ||
496 | if (bridge_info.busUnitInfo.deviceType == | ||
497 | HvCallPci_BridgeDevice) | ||
498 | scan_bridge_slot(dt, bus, &bridge_info); | ||
499 | else | ||
500 | DBG("PCI: Invalid Bridge Configuration(0x%02X)", | ||
501 | bridge_info.busUnitInfo.deviceType); | ||
502 | } | ||
503 | } | ||
504 | |||
505 | static void __init scan_phb(struct iseries_flat_dt *dt, HvBusNumber bus) | ||
506 | { | ||
507 | struct HvCallPci_DeviceInfo dev_info; | ||
508 | const HvSubBusNumber sub_bus = 0; /* EADs is always 0. */ | ||
509 | int err; | ||
510 | int id_sel; | ||
511 | const int max_agents = 8; | ||
512 | |||
513 | /* | ||
514 | * Probe for EADs Bridges | ||
515 | */ | ||
516 | for (id_sel = 1; id_sel < max_agents; ++id_sel) { | ||
517 | err = HvCallPci_getDeviceInfo(bus, sub_bus, id_sel, | ||
518 | iseries_hv_addr(&dev_info), | ||
519 | sizeof(struct HvCallPci_DeviceInfo)); | ||
520 | if (err) { | ||
521 | if (err != 0x302) | ||
522 | DBG("getDeviceInfo(%x, %x, %x) %x\n", | ||
523 | bus, sub_bus, id_sel, err); | ||
524 | continue; | ||
525 | } | ||
526 | if (dev_info.deviceType != HvCallPci_NodeDevice) { | ||
527 | DBG("PCI: Invalid System Configuration" | ||
528 | "(0x%02X) for bus 0x%02x id 0x%02x.\n", | ||
529 | dev_info.deviceType, bus, id_sel); | ||
530 | continue; | ||
531 | } | ||
532 | scan_bridge(dt, bus, sub_bus, id_sel); | ||
533 | } | ||
534 | } | ||
535 | |||
536 | static void __init dt_pci_devices(struct iseries_flat_dt *dt) | ||
537 | { | ||
538 | HvBusNumber bus; | ||
539 | char buf[32]; | ||
540 | u32 buses[2]; | ||
541 | int phb_num = 0; | ||
542 | |||
543 | /* Check all possible buses. */ | ||
544 | for (bus = 0; bus < 256; bus++) { | ||
545 | int err = HvCallXm_testBus(bus); | ||
546 | |||
547 | if (err) { | ||
548 | /* | ||
549 | * Check for Unexpected Return code, a clue that | ||
550 | * something has gone wrong. | ||
551 | */ | ||
552 | if (err != 0x0301) | ||
553 | DBG("Unexpected Return on Probe(0x%02X) " | ||
554 | "0x%04X\n", bus, err); | ||
555 | continue; | ||
556 | } | ||
557 | DBG("bus %d appears to exist\n", bus); | ||
558 | snprintf(buf, 32, "pci@%d", phb_num); | ||
559 | dt_start_node(dt, buf); | ||
560 | dt_prop_str(dt, "device_type", device_type_pci); | ||
561 | dt_prop_str(dt, "compatible", "IBM,iSeries-Logical-PHB"); | ||
562 | dt_prop_u32(dt, "#address-cells", 3); | ||
563 | dt_prop_u32(dt, "#size-cells", 2); | ||
564 | buses[0] = buses[1] = bus; | ||
565 | dt_prop_u32_list(dt, "bus-range", buses, 2); | ||
566 | scan_phb(dt, bus); | ||
567 | dt_end_node(dt); | ||
568 | phb_num++; | ||
569 | } | ||
570 | } | ||
571 | |||
572 | static void dt_finish(struct iseries_flat_dt *dt) | ||
573 | { | ||
574 | dt_push_u32(dt, OF_DT_END); | ||
575 | dt->header.totalsize = (unsigned long)dt_data - (unsigned long)dt; | ||
576 | klimit = ALIGN((unsigned long)dt_data, 8); | ||
577 | } | ||
578 | |||
579 | void * __init build_flat_dt(unsigned long phys_mem_size) | ||
580 | { | ||
581 | struct iseries_flat_dt *iseries_dt; | ||
582 | u64 tmp[2]; | ||
583 | |||
584 | iseries_dt = dt_init(); | ||
585 | |||
586 | dt_start_node(iseries_dt, ""); | ||
587 | |||
588 | dt_prop_u32(iseries_dt, "#address-cells", 2); | ||
589 | dt_prop_u32(iseries_dt, "#size-cells", 2); | ||
590 | dt_model(iseries_dt); | ||
591 | |||
592 | /* /memory */ | ||
593 | dt_start_node(iseries_dt, "memory@0"); | ||
594 | dt_prop_str(iseries_dt, "device_type", device_type_memory); | ||
595 | tmp[0] = 0; | ||
596 | tmp[1] = phys_mem_size; | ||
597 | dt_prop_u64_list(iseries_dt, "reg", tmp, 2); | ||
598 | dt_end_node(iseries_dt); | ||
599 | |||
600 | /* /chosen */ | ||
601 | dt_start_node(iseries_dt, "chosen"); | ||
602 | dt_prop_str(iseries_dt, "bootargs", cmd_line); | ||
603 | dt_end_node(iseries_dt); | ||
604 | |||
605 | dt_cpus(iseries_dt); | ||
606 | |||
607 | dt_vdevices(iseries_dt); | ||
608 | dt_pci_devices(iseries_dt); | ||
609 | |||
610 | dt_end_node(iseries_dt); | ||
611 | |||
612 | dt_finish(iseries_dt); | ||
613 | |||
614 | return iseries_dt; | ||
615 | } | ||