diff options
Diffstat (limited to 'arch/powerpc/platforms/iseries')
-rw-r--r-- | arch/powerpc/platforms/iseries/Makefile | 6 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/call_pci.h | 19 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/dt.c | 615 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/iommu.c | 46 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/iommu.h | 35 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/irq.c | 7 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/irq.h | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/mf.c | 9 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/pci.c | 347 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/setup.c | 271 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/setup.h | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/vio.c | 131 |
12 files changed, 748 insertions, 742 deletions
diff --git a/arch/powerpc/platforms/iseries/Makefile b/arch/powerpc/platforms/iseries/Makefile index ce8c0b943fa0..dee4eb4d8bec 100644 --- a/arch/powerpc/platforms/iseries/Makefile +++ b/arch/powerpc/platforms/iseries/Makefile | |||
@@ -1,9 +1,11 @@ | |||
1 | EXTRA_CFLAGS += -mno-minimal-toc | 1 | EXTRA_CFLAGS += -mno-minimal-toc |
2 | 2 | ||
3 | obj-y += hvlog.o hvlpconfig.o lpardata.o setup.o mf.o lpevents.o \ | 3 | obj-y += hvlog.o hvlpconfig.o lpardata.o setup.o dt_mod.o mf.o lpevents.o \ |
4 | hvcall.o proc.o htab.o iommu.o misc.o irq.o | 4 | hvcall.o proc.o htab.o iommu.o misc.o irq.o |
5 | obj-$(CONFIG_PCI) += pci.o vpdinfo.o | 5 | obj-$(CONFIG_PCI) += pci.o vpdinfo.o |
6 | obj-$(CONFIG_IBMVIO) += vio.o | ||
7 | obj-$(CONFIG_SMP) += smp.o | 6 | obj-$(CONFIG_SMP) += smp.o |
8 | obj-$(CONFIG_VIOPATH) += viopath.o | 7 | obj-$(CONFIG_VIOPATH) += viopath.o |
9 | obj-$(CONFIG_MODULES) += ksyms.o | 8 | obj-$(CONFIG_MODULES) += ksyms.o |
9 | |||
10 | $(obj)/dt_mod.o: $(obj)/dt.o | ||
11 | @$(OBJCOPY) --rename-section .rodata.str1.8=.dt_strings $(obj)/dt.o $(obj)/dt_mod.o | ||
diff --git a/arch/powerpc/platforms/iseries/call_pci.h b/arch/powerpc/platforms/iseries/call_pci.h index 59d4e0ad5cf3..dbdf69850ed9 100644 --- a/arch/powerpc/platforms/iseries/call_pci.h +++ b/arch/powerpc/platforms/iseries/call_pci.h | |||
@@ -145,6 +145,25 @@ static inline u64 HvCallPci_configLoad16(u16 busNumber, u8 subBusNumber, | |||
145 | return retVal.rc; | 145 | return retVal.rc; |
146 | } | 146 | } |
147 | 147 | ||
148 | static inline u64 HvCallPci_configLoad32(u16 busNumber, u8 subBusNumber, | ||
149 | u8 deviceId, u32 offset, u32 *value) | ||
150 | { | ||
151 | struct HvCallPci_DsaAddr dsa; | ||
152 | struct HvCallPci_LoadReturn retVal; | ||
153 | |||
154 | *((u64*)&dsa) = 0; | ||
155 | |||
156 | dsa.busNumber = busNumber; | ||
157 | dsa.subBusNumber = subBusNumber; | ||
158 | dsa.deviceId = deviceId; | ||
159 | |||
160 | HvCall3Ret16(HvCallPciConfigLoad32, &retVal, *(u64 *)&dsa, offset, 0); | ||
161 | |||
162 | *value = retVal.value; | ||
163 | |||
164 | return retVal.rc; | ||
165 | } | ||
166 | |||
148 | static inline u64 HvCallPci_configStore8(u16 busNumber, u8 subBusNumber, | 167 | static inline u64 HvCallPci_configStore8(u16 busNumber, u8 subBusNumber, |
149 | u8 deviceId, u32 offset, u8 value) | 168 | u8 deviceId, u32 offset, u8 value) |
150 | { | 169 | { |
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 | } | ||
diff --git a/arch/powerpc/platforms/iseries/iommu.c b/arch/powerpc/platforms/iseries/iommu.c index bea0b703f409..e3bd2015f2c9 100644 --- a/arch/powerpc/platforms/iseries/iommu.c +++ b/arch/powerpc/platforms/iseries/iommu.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * Rewrite, cleanup: | 4 | * Rewrite, cleanup: |
5 | * | 5 | * |
6 | * Copyright (C) 2004 Olof Johansson <olof@lixom.net>, IBM Corporation | 6 | * Copyright (C) 2004 Olof Johansson <olof@lixom.net>, IBM Corporation |
7 | * Copyright (C) 2006 Olof Johansson <olof@lixom.net> | ||
7 | * | 8 | * |
8 | * Dynamic DMA mapping support, iSeries-specific parts. | 9 | * Dynamic DMA mapping support, iSeries-specific parts. |
9 | * | 10 | * |
@@ -31,42 +32,37 @@ | |||
31 | #include <asm/tce.h> | 32 | #include <asm/tce.h> |
32 | #include <asm/machdep.h> | 33 | #include <asm/machdep.h> |
33 | #include <asm/abs_addr.h> | 34 | #include <asm/abs_addr.h> |
35 | #include <asm/prom.h> | ||
34 | #include <asm/pci-bridge.h> | 36 | #include <asm/pci-bridge.h> |
35 | #include <asm/iseries/hv_call_xm.h> | 37 | #include <asm/iseries/hv_call_xm.h> |
36 | 38 | #include <asm/iseries/iommu.h> | |
37 | #include "iommu.h" | ||
38 | |||
39 | extern struct list_head iSeries_Global_Device_List; | ||
40 | |||
41 | 39 | ||
42 | static void tce_build_iSeries(struct iommu_table *tbl, long index, long npages, | 40 | static void tce_build_iSeries(struct iommu_table *tbl, long index, long npages, |
43 | unsigned long uaddr, enum dma_data_direction direction) | 41 | unsigned long uaddr, enum dma_data_direction direction) |
44 | { | 42 | { |
45 | u64 rc; | 43 | u64 rc; |
46 | union tce_entry tce; | 44 | u64 tce, rpn; |
47 | 45 | ||
48 | index <<= TCE_PAGE_FACTOR; | 46 | index <<= TCE_PAGE_FACTOR; |
49 | npages <<= TCE_PAGE_FACTOR; | 47 | npages <<= TCE_PAGE_FACTOR; |
50 | 48 | ||
51 | while (npages--) { | 49 | while (npages--) { |
52 | tce.te_word = 0; | 50 | rpn = virt_to_abs(uaddr) >> TCE_SHIFT; |
53 | tce.te_bits.tb_rpn = virt_to_abs(uaddr) >> TCE_SHIFT; | 51 | tce = (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; |
54 | 52 | ||
55 | if (tbl->it_type == TCE_VB) { | 53 | if (tbl->it_type == TCE_VB) { |
56 | /* Virtual Bus */ | 54 | /* Virtual Bus */ |
57 | tce.te_bits.tb_valid = 1; | 55 | tce |= TCE_VALID|TCE_ALLIO; |
58 | tce.te_bits.tb_allio = 1; | ||
59 | if (direction != DMA_TO_DEVICE) | 56 | if (direction != DMA_TO_DEVICE) |
60 | tce.te_bits.tb_rdwr = 1; | 57 | tce |= TCE_VB_WRITE; |
61 | } else { | 58 | } else { |
62 | /* PCI Bus */ | 59 | /* PCI Bus */ |
63 | tce.te_bits.tb_rdwr = 1; /* Read allowed */ | 60 | tce |= TCE_PCI_READ; /* Read allowed */ |
64 | if (direction != DMA_TO_DEVICE) | 61 | if (direction != DMA_TO_DEVICE) |
65 | tce.te_bits.tb_pciwr = 1; | 62 | tce |= TCE_PCI_WRITE; |
66 | } | 63 | } |
67 | 64 | ||
68 | rc = HvCallXm_setTce((u64)tbl->it_index, (u64)index, | 65 | rc = HvCallXm_setTce((u64)tbl->it_index, (u64)index, tce); |
69 | tce.te_word); | ||
70 | if (rc) | 66 | if (rc) |
71 | panic("PCI_DMA: HvCallXm_setTce failed, Rc: 0x%lx\n", | 67 | panic("PCI_DMA: HvCallXm_setTce failed, Rc: 0x%lx\n", |
72 | rc); | 68 | rc); |
@@ -124,7 +120,7 @@ void iommu_table_getparms_iSeries(unsigned long busno, | |||
124 | 120 | ||
125 | /* itc_size is in pages worth of table, it_size is in # of entries */ | 121 | /* itc_size is in pages worth of table, it_size is in # of entries */ |
126 | tbl->it_size = ((parms->itc_size * TCE_PAGE_SIZE) / | 122 | tbl->it_size = ((parms->itc_size * TCE_PAGE_SIZE) / |
127 | sizeof(union tce_entry)) >> TCE_PAGE_FACTOR; | 123 | TCE_ENTRY_SIZE) >> TCE_PAGE_FACTOR; |
128 | tbl->it_busno = parms->itc_busno; | 124 | tbl->it_busno = parms->itc_busno; |
129 | tbl->it_offset = parms->itc_offset >> TCE_PAGE_FACTOR; | 125 | tbl->it_offset = parms->itc_offset >> TCE_PAGE_FACTOR; |
130 | tbl->it_index = parms->itc_index; | 126 | tbl->it_index = parms->itc_index; |
@@ -142,10 +138,15 @@ void iommu_table_getparms_iSeries(unsigned long busno, | |||
142 | */ | 138 | */ |
143 | static struct iommu_table *iommu_table_find(struct iommu_table * tbl) | 139 | static struct iommu_table *iommu_table_find(struct iommu_table * tbl) |
144 | { | 140 | { |
145 | struct pci_dn *pdn; | 141 | struct device_node *node; |
146 | 142 | ||
147 | list_for_each_entry(pdn, &iSeries_Global_Device_List, Device_List) { | 143 | for (node = NULL; (node = of_find_all_nodes(node)); ) { |
148 | struct iommu_table *it = pdn->iommu_table; | 144 | struct pci_dn *pdn = PCI_DN(node); |
145 | struct iommu_table *it; | ||
146 | |||
147 | if (pdn == NULL) | ||
148 | continue; | ||
149 | it = pdn->iommu_table; | ||
149 | if ((it != NULL) && | 150 | if ((it != NULL) && |
150 | (it->it_type == TCE_PCI) && | 151 | (it->it_type == TCE_PCI) && |
151 | (it->it_offset == tbl->it_offset) && | 152 | (it->it_offset == tbl->it_offset) && |
@@ -161,15 +162,18 @@ void iommu_devnode_init_iSeries(struct device_node *dn) | |||
161 | { | 162 | { |
162 | struct iommu_table *tbl; | 163 | struct iommu_table *tbl; |
163 | struct pci_dn *pdn = PCI_DN(dn); | 164 | struct pci_dn *pdn = PCI_DN(dn); |
165 | u32 *lsn = (u32 *)get_property(dn, "linux,logical-slot-number", NULL); | ||
166 | |||
167 | BUG_ON(lsn == NULL); | ||
164 | 168 | ||
165 | tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL); | 169 | tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL); |
166 | 170 | ||
167 | iommu_table_getparms_iSeries(pdn->busno, pdn->LogicalSlot, 0, tbl); | 171 | iommu_table_getparms_iSeries(pdn->busno, *lsn, 0, tbl); |
168 | 172 | ||
169 | /* Look for existing tce table */ | 173 | /* Look for existing tce table */ |
170 | pdn->iommu_table = iommu_table_find(tbl); | 174 | pdn->iommu_table = iommu_table_find(tbl); |
171 | if (pdn->iommu_table == NULL) | 175 | if (pdn->iommu_table == NULL) |
172 | pdn->iommu_table = iommu_init_table(tbl); | 176 | pdn->iommu_table = iommu_init_table(tbl, -1); |
173 | else | 177 | else |
174 | kfree(tbl); | 178 | kfree(tbl); |
175 | } | 179 | } |
diff --git a/arch/powerpc/platforms/iseries/iommu.h b/arch/powerpc/platforms/iseries/iommu.h deleted file mode 100644 index cb5658fbe657..000000000000 --- a/arch/powerpc/platforms/iseries/iommu.h +++ /dev/null | |||
@@ -1,35 +0,0 @@ | |||
1 | #ifndef _PLATFORMS_ISERIES_IOMMU_H | ||
2 | #define _PLATFORMS_ISERIES_IOMMU_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 2005 Stephen Rothwell, IBM Corporation | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the: | ||
19 | * Free Software Foundation, Inc., | ||
20 | * 59 Temple Place, Suite 330, | ||
21 | * Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | struct device_node; | ||
25 | struct iommu_table; | ||
26 | |||
27 | /* Creates table for an individual device node */ | ||
28 | extern void iommu_devnode_init_iSeries(struct device_node *dn); | ||
29 | |||
30 | /* Get table parameters from HV */ | ||
31 | extern void iommu_table_getparms_iSeries(unsigned long busno, | ||
32 | unsigned char slotno, unsigned char virtbus, | ||
33 | struct iommu_table *tbl); | ||
34 | |||
35 | #endif /* _PLATFORMS_ISERIES_IOMMU_H */ | ||
diff --git a/arch/powerpc/platforms/iseries/irq.c b/arch/powerpc/platforms/iseries/irq.c index be3fbfc24e6c..62bbbcf5ded3 100644 --- a/arch/powerpc/platforms/iseries/irq.c +++ b/arch/powerpc/platforms/iseries/irq.c | |||
@@ -42,6 +42,7 @@ | |||
42 | #include <asm/iseries/it_lp_queue.h> | 42 | #include <asm/iseries/it_lp_queue.h> |
43 | 43 | ||
44 | #include "irq.h" | 44 | #include "irq.h" |
45 | #include "pci.h" | ||
45 | #include "call_pci.h" | 46 | #include "call_pci.h" |
46 | 47 | ||
47 | #if defined(CONFIG_SMP) | 48 | #if defined(CONFIG_SMP) |
@@ -312,12 +313,12 @@ static hw_irq_controller iSeries_IRQ_handler = { | |||
312 | * Note that sub_bus is always 0 (at the moment at least). | 313 | * Note that sub_bus is always 0 (at the moment at least). |
313 | */ | 314 | */ |
314 | int __init iSeries_allocate_IRQ(HvBusNumber bus, | 315 | int __init iSeries_allocate_IRQ(HvBusNumber bus, |
315 | HvSubBusNumber sub_bus, HvAgentId dev_id) | 316 | HvSubBusNumber sub_bus, u32 bsubbus) |
316 | { | 317 | { |
317 | int virtirq; | 318 | int virtirq; |
318 | unsigned int realirq; | 319 | unsigned int realirq; |
319 | u8 idsel = (dev_id >> 4); | 320 | u8 idsel = ISERIES_GET_DEVICE_FROM_SUBBUS(bsubbus); |
320 | u8 function = dev_id & 7; | 321 | u8 function = ISERIES_GET_FUNCTION_FROM_SUBBUS(bsubbus); |
321 | 322 | ||
322 | realirq = (((((sub_bus << 8) + (bus - 1)) << 3) + (idsel - 1)) << 3) | 323 | realirq = (((((sub_bus << 8) + (bus - 1)) << 3) + (idsel - 1)) << 3) |
323 | + function; | 324 | + function; |
diff --git a/arch/powerpc/platforms/iseries/irq.h b/arch/powerpc/platforms/iseries/irq.h index b9c801ba5a47..188aa808abd7 100644 --- a/arch/powerpc/platforms/iseries/irq.h +++ b/arch/powerpc/platforms/iseries/irq.h | |||
@@ -2,7 +2,7 @@ | |||
2 | #define _ISERIES_IRQ_H | 2 | #define _ISERIES_IRQ_H |
3 | 3 | ||
4 | extern void iSeries_init_IRQ(void); | 4 | extern void iSeries_init_IRQ(void); |
5 | extern int iSeries_allocate_IRQ(HvBusNumber, HvSubBusNumber, HvAgentId); | 5 | extern int iSeries_allocate_IRQ(HvBusNumber, HvSubBusNumber, u32); |
6 | extern void iSeries_activate_IRQs(void); | 6 | extern void iSeries_activate_IRQs(void); |
7 | extern int iSeries_get_irq(struct pt_regs *); | 7 | extern int iSeries_get_irq(struct pt_regs *); |
8 | 8 | ||
diff --git a/arch/powerpc/platforms/iseries/mf.c b/arch/powerpc/platforms/iseries/mf.c index d771b8ee857d..1a2c2a50f922 100644 --- a/arch/powerpc/platforms/iseries/mf.c +++ b/arch/powerpc/platforms/iseries/mf.c | |||
@@ -45,7 +45,6 @@ | |||
45 | 45 | ||
46 | #include "setup.h" | 46 | #include "setup.h" |
47 | 47 | ||
48 | extern int piranha_simulator; | ||
49 | static int mf_initialized; | 48 | static int mf_initialized; |
50 | 49 | ||
51 | /* | 50 | /* |
@@ -658,7 +657,7 @@ static void mf_clear_src(void) | |||
658 | 657 | ||
659 | void __init mf_display_progress(u16 value) | 658 | void __init mf_display_progress(u16 value) |
660 | { | 659 | { |
661 | if (piranha_simulator || !mf_initialized) | 660 | if (!mf_initialized) |
662 | return; | 661 | return; |
663 | 662 | ||
664 | if (0xFFFF == value) | 663 | if (0xFFFF == value) |
@@ -1295,9 +1294,6 @@ __initcall(mf_proc_init); | |||
1295 | */ | 1294 | */ |
1296 | void iSeries_get_rtc_time(struct rtc_time *rtc_tm) | 1295 | void iSeries_get_rtc_time(struct rtc_time *rtc_tm) |
1297 | { | 1296 | { |
1298 | if (piranha_simulator) | ||
1299 | return; | ||
1300 | |||
1301 | mf_get_rtc(rtc_tm); | 1297 | mf_get_rtc(rtc_tm); |
1302 | rtc_tm->tm_mon--; | 1298 | rtc_tm->tm_mon--; |
1303 | } | 1299 | } |
@@ -1316,9 +1312,6 @@ unsigned long iSeries_get_boot_time(void) | |||
1316 | { | 1312 | { |
1317 | struct rtc_time tm; | 1313 | struct rtc_time tm; |
1318 | 1314 | ||
1319 | if (piranha_simulator) | ||
1320 | return 0; | ||
1321 | |||
1322 | mf_get_boot_rtc(&tm); | 1315 | mf_get_boot_rtc(&tm); |
1323 | return mktime(tm.tm_year + 1900, tm.tm_mon, tm.tm_mday, | 1316 | return mktime(tm.tm_year + 1900, tm.tm_mon, tm.tm_mday, |
1324 | tm.tm_hour, tm.tm_min, tm.tm_sec); | 1317 | tm.tm_hour, tm.tm_min, tm.tm_sec); |
diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c index a19833b880e4..35bcc98111f5 100644 --- a/arch/powerpc/platforms/iseries/pci.c +++ b/arch/powerpc/platforms/iseries/pci.c | |||
@@ -37,36 +37,18 @@ | |||
37 | 37 | ||
38 | #include <asm/iseries/hv_call_xm.h> | 38 | #include <asm/iseries/hv_call_xm.h> |
39 | #include <asm/iseries/mf.h> | 39 | #include <asm/iseries/mf.h> |
40 | #include <asm/iseries/iommu.h> | ||
40 | 41 | ||
41 | #include <asm/ppc-pci.h> | 42 | #include <asm/ppc-pci.h> |
42 | 43 | ||
43 | #include "irq.h" | 44 | #include "irq.h" |
44 | #include "pci.h" | 45 | #include "pci.h" |
45 | #include "call_pci.h" | 46 | #include "call_pci.h" |
46 | #include "iommu.h" | ||
47 | |||
48 | extern unsigned long io_page_mask; | ||
49 | 47 | ||
50 | /* | 48 | /* |
51 | * Forward declares of prototypes. | 49 | * Forward declares of prototypes. |
52 | */ | 50 | */ |
53 | static struct device_node *find_Device_Node(int bus, int devfn); | 51 | static struct device_node *find_Device_Node(int bus, int devfn); |
54 | static void scan_PHB_slots(struct pci_controller *Phb); | ||
55 | static void scan_EADS_bridge(HvBusNumber Bus, HvSubBusNumber SubBus, int IdSel); | ||
56 | static int scan_bridge_slot(HvBusNumber Bus, struct HvCallPci_BridgeInfo *Info); | ||
57 | |||
58 | LIST_HEAD(iSeries_Global_Device_List); | ||
59 | |||
60 | static int DeviceCount; | ||
61 | |||
62 | /* Counters and control flags. */ | ||
63 | static long Pci_Io_Read_Count; | ||
64 | static long Pci_Io_Write_Count; | ||
65 | #if 0 | ||
66 | static long Pci_Cfg_Read_Count; | ||
67 | static long Pci_Cfg_Write_Count; | ||
68 | #endif | ||
69 | static long Pci_Error_Count; | ||
70 | 52 | ||
71 | static int Pci_Retry_Max = 3; /* Only retry 3 times */ | 53 | static int Pci_Retry_Max = 3; /* Only retry 3 times */ |
72 | static int Pci_Error_Flag = 1; /* Set Retry Error on. */ | 54 | static int Pci_Error_Flag = 1; /* Set Retry Error on. */ |
@@ -81,41 +63,19 @@ static struct pci_ops iSeries_pci_ops; | |||
81 | #define IOMM_TABLE_ENTRY_SIZE 0x0000000000400000UL | 63 | #define IOMM_TABLE_ENTRY_SIZE 0x0000000000400000UL |
82 | #define BASE_IO_MEMORY 0xE000000000000000UL | 64 | #define BASE_IO_MEMORY 0xE000000000000000UL |
83 | 65 | ||
84 | static unsigned long max_io_memory = 0xE000000000000000UL; | 66 | static unsigned long max_io_memory = BASE_IO_MEMORY; |
85 | static long current_iomm_table_entry; | 67 | static long current_iomm_table_entry; |
86 | 68 | ||
87 | /* | 69 | /* |
88 | * Lookup Tables. | 70 | * Lookup Tables. |
89 | */ | 71 | */ |
90 | static struct device_node **iomm_table; | 72 | static struct device_node *iomm_table[IOMM_TABLE_MAX_ENTRIES]; |
91 | static u8 *iobar_table; | 73 | static u8 iobar_table[IOMM_TABLE_MAX_ENTRIES]; |
92 | 74 | ||
93 | /* | 75 | static const char pci_io_text[] = "iSeries PCI I/O"; |
94 | * Static and Global variables | ||
95 | */ | ||
96 | static char *pci_io_text = "iSeries PCI I/O"; | ||
97 | static DEFINE_SPINLOCK(iomm_table_lock); | 76 | static DEFINE_SPINLOCK(iomm_table_lock); |
98 | 77 | ||
99 | /* | 78 | /* |
100 | * iomm_table_initialize | ||
101 | * | ||
102 | * Allocates and initalizes the Address Translation Table and Bar | ||
103 | * Tables to get them ready for use. Must be called before any | ||
104 | * I/O space is handed out to the device BARs. | ||
105 | */ | ||
106 | static void iomm_table_initialize(void) | ||
107 | { | ||
108 | spin_lock(&iomm_table_lock); | ||
109 | iomm_table = kmalloc(sizeof(*iomm_table) * IOMM_TABLE_MAX_ENTRIES, | ||
110 | GFP_KERNEL); | ||
111 | iobar_table = kmalloc(sizeof(*iobar_table) * IOMM_TABLE_MAX_ENTRIES, | ||
112 | GFP_KERNEL); | ||
113 | spin_unlock(&iomm_table_lock); | ||
114 | if ((iomm_table == NULL) || (iobar_table == NULL)) | ||
115 | panic("PCI: I/O tables allocation failed.\n"); | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * iomm_table_allocate_entry | 79 | * iomm_table_allocate_entry |
120 | * | 80 | * |
121 | * Adds pci_dev entry in address translation table | 81 | * Adds pci_dev entry in address translation table |
@@ -142,9 +102,8 @@ static void iomm_table_allocate_entry(struct pci_dev *dev, int bar_num) | |||
142 | */ | 102 | */ |
143 | spin_lock(&iomm_table_lock); | 103 | spin_lock(&iomm_table_lock); |
144 | bar_res->name = pci_io_text; | 104 | bar_res->name = pci_io_text; |
145 | bar_res->start = | 105 | bar_res->start = BASE_IO_MEMORY + |
146 | IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry; | 106 | IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry; |
147 | bar_res->start += BASE_IO_MEMORY; | ||
148 | bar_res->end = bar_res->start + bar_size - 1; | 107 | bar_res->end = bar_res->start + bar_size - 1; |
149 | /* | 108 | /* |
150 | * Allocate the number of table entries needed for BAR. | 109 | * Allocate the number of table entries needed for BAR. |
@@ -156,7 +115,7 @@ static void iomm_table_allocate_entry(struct pci_dev *dev, int bar_num) | |||
156 | ++current_iomm_table_entry; | 115 | ++current_iomm_table_entry; |
157 | } | 116 | } |
158 | max_io_memory = BASE_IO_MEMORY + | 117 | max_io_memory = BASE_IO_MEMORY + |
159 | (IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry); | 118 | IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry; |
160 | spin_unlock(&iomm_table_lock); | 119 | spin_unlock(&iomm_table_lock); |
161 | } | 120 | } |
162 | 121 | ||
@@ -173,13 +132,10 @@ static void iomm_table_allocate_entry(struct pci_dev *dev, int bar_num) | |||
173 | */ | 132 | */ |
174 | static void allocate_device_bars(struct pci_dev *dev) | 133 | static void allocate_device_bars(struct pci_dev *dev) |
175 | { | 134 | { |
176 | struct resource *bar_res; | ||
177 | int bar_num; | 135 | int bar_num; |
178 | 136 | ||
179 | for (bar_num = 0; bar_num <= PCI_ROM_RESOURCE; ++bar_num) { | 137 | for (bar_num = 0; bar_num <= PCI_ROM_RESOURCE; ++bar_num) |
180 | bar_res = &dev->resource[bar_num]; | ||
181 | iomm_table_allocate_entry(dev, bar_num); | 138 | iomm_table_allocate_entry(dev, bar_num); |
182 | } | ||
183 | } | 139 | } |
184 | 140 | ||
185 | /* | 141 | /* |
@@ -199,34 +155,7 @@ static void pci_Log_Error(char *Error_Text, int Bus, int SubBus, | |||
199 | } | 155 | } |
200 | 156 | ||
201 | /* | 157 | /* |
202 | * build_device_node(u16 Bus, int SubBus, u8 DevFn) | 158 | * iSeries_pcibios_init |
203 | */ | ||
204 | static struct device_node *build_device_node(HvBusNumber Bus, | ||
205 | HvSubBusNumber SubBus, int AgentId, int Function) | ||
206 | { | ||
207 | struct device_node *node; | ||
208 | struct pci_dn *pdn; | ||
209 | |||
210 | node = kmalloc(sizeof(struct device_node), GFP_KERNEL); | ||
211 | if (node == NULL) | ||
212 | return NULL; | ||
213 | memset(node, 0, sizeof(struct device_node)); | ||
214 | pdn = kzalloc(sizeof(*pdn), GFP_KERNEL); | ||
215 | if (pdn == NULL) { | ||
216 | kfree(node); | ||
217 | return NULL; | ||
218 | } | ||
219 | node->data = pdn; | ||
220 | pdn->node = node; | ||
221 | list_add_tail(&pdn->Device_List, &iSeries_Global_Device_List); | ||
222 | pdn->busno = Bus; | ||
223 | pdn->bussubno = SubBus; | ||
224 | pdn->devfn = PCI_DEVFN(ISERIES_ENCODE_DEVICE(AgentId), Function); | ||
225 | return node; | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * unsigned long __init find_and_init_phbs(void) | ||
230 | * | 159 | * |
231 | * Description: | 160 | * Description: |
232 | * This function checks for all possible system PCI host bridges that connect | 161 | * This function checks for all possible system PCI host bridges that connect |
@@ -234,50 +163,42 @@ static struct device_node *build_device_node(HvBusNumber Bus, | |||
234 | * ownership status. A pci_controller is built for any bus which is partially | 163 | * ownership status. A pci_controller is built for any bus which is partially |
235 | * owned or fully owned by this guest partition. | 164 | * owned or fully owned by this guest partition. |
236 | */ | 165 | */ |
237 | unsigned long __init find_and_init_phbs(void) | 166 | void iSeries_pcibios_init(void) |
238 | { | 167 | { |
239 | struct pci_controller *phb; | 168 | struct pci_controller *phb; |
240 | HvBusNumber bus; | 169 | struct device_node *root = of_find_node_by_path("/"); |
241 | 170 | struct device_node *node = NULL; | |
242 | /* Check all possible buses. */ | ||
243 | for (bus = 0; bus < 256; bus++) { | ||
244 | int ret = HvCallXm_testBus(bus); | ||
245 | if (ret == 0) { | ||
246 | printk("bus %d appears to exist\n", bus); | ||
247 | 171 | ||
248 | phb = pcibios_alloc_controller(NULL); | 172 | if (root == NULL) { |
249 | if (phb == NULL) | 173 | printk(KERN_CRIT "iSeries_pcibios_init: can't find root " |
250 | return -ENOMEM; | 174 | "of device tree\n"); |
251 | 175 | return; | |
252 | phb->pci_mem_offset = phb->local_number = bus; | 176 | } |
253 | phb->first_busno = bus; | 177 | while ((node = of_get_next_child(root, node)) != NULL) { |
254 | phb->last_busno = bus; | 178 | HvBusNumber bus; |
255 | phb->ops = &iSeries_pci_ops; | 179 | u32 *busp; |
256 | 180 | ||
257 | /* Find and connect the devices. */ | 181 | if ((node->type == NULL) || (strcmp(node->type, "pci") != 0)) |
258 | scan_PHB_slots(phb); | 182 | continue; |
259 | } | 183 | |
260 | /* | 184 | busp = (u32 *)get_property(node, "bus-range", NULL); |
261 | * Check for Unexpected Return code, a clue that something | 185 | if (busp == NULL) |
262 | * has gone wrong. | 186 | continue; |
263 | */ | 187 | bus = *busp; |
264 | else if (ret != 0x0301) | 188 | printk("bus %d appears to exist\n", bus); |
265 | printk(KERN_ERR "Unexpected Return on Probe(0x%04X): 0x%04X", | 189 | phb = pcibios_alloc_controller(node); |
266 | bus, ret); | 190 | if (phb == NULL) |
191 | continue; | ||
192 | |||
193 | phb->pci_mem_offset = phb->local_number = bus; | ||
194 | phb->first_busno = bus; | ||
195 | phb->last_busno = bus; | ||
196 | phb->ops = &iSeries_pci_ops; | ||
267 | } | 197 | } |
268 | return 0; | ||
269 | } | ||
270 | 198 | ||
271 | /* | 199 | of_node_put(root); |
272 | * iSeries_pcibios_init | 200 | |
273 | * | 201 | pci_devs_phb_init(); |
274 | * Chance to initialize and structures or variable before PCI Bus walk. | ||
275 | */ | ||
276 | void iSeries_pcibios_init(void) | ||
277 | { | ||
278 | iomm_table_initialize(); | ||
279 | find_and_init_phbs(); | ||
280 | io_page_mask = -1; | ||
281 | } | 202 | } |
282 | 203 | ||
283 | /* | 204 | /* |
@@ -299,6 +220,34 @@ void __init iSeries_pci_final_fixup(void) | |||
299 | pdev->bus->number, pdev->devfn, node); | 220 | pdev->bus->number, pdev->devfn, node); |
300 | 221 | ||
301 | if (node != NULL) { | 222 | if (node != NULL) { |
223 | struct pci_dn *pdn = PCI_DN(node); | ||
224 | u32 *agent; | ||
225 | |||
226 | agent = (u32 *)get_property(node, "linux,agent-id", | ||
227 | NULL); | ||
228 | if ((pdn != NULL) && (agent != NULL)) { | ||
229 | u8 irq = iSeries_allocate_IRQ(pdn->busno, 0, | ||
230 | pdn->bussubno); | ||
231 | int err; | ||
232 | |||
233 | err = HvCallXm_connectBusUnit(pdn->busno, pdn->bussubno, | ||
234 | *agent, irq); | ||
235 | if (err) | ||
236 | pci_Log_Error("Connect Bus Unit", | ||
237 | pdn->busno, pdn->bussubno, *agent, err); | ||
238 | else { | ||
239 | err = HvCallPci_configStore8(pdn->busno, pdn->bussubno, | ||
240 | *agent, | ||
241 | PCI_INTERRUPT_LINE, | ||
242 | irq); | ||
243 | if (err) | ||
244 | pci_Log_Error("PciCfgStore Irq Failed!", | ||
245 | pdn->busno, pdn->bussubno, *agent, err); | ||
246 | } | ||
247 | if (!err) | ||
248 | pdev->irq = irq; | ||
249 | } | ||
250 | |||
302 | ++DeviceCount; | 251 | ++DeviceCount; |
303 | pdev->sysdata = (void *)node; | 252 | pdev->sysdata = (void *)node; |
304 | PCI_DN(node)->pcidev = pdev; | 253 | PCI_DN(node)->pcidev = pdev; |
@@ -308,7 +257,6 @@ void __init iSeries_pci_final_fixup(void) | |||
308 | } else | 257 | } else |
309 | printk("PCI: Device Tree not found for 0x%016lX\n", | 258 | printk("PCI: Device Tree not found for 0x%016lX\n", |
310 | (unsigned long)pdev); | 259 | (unsigned long)pdev); |
311 | pdev->irq = PCI_DN(node)->Irq; | ||
312 | } | 260 | } |
313 | iSeries_activate_IRQs(); | 261 | iSeries_activate_IRQs(); |
314 | mf_display_src(0xC9000200); | 262 | mf_display_src(0xC9000200); |
@@ -323,148 +271,6 @@ void pcibios_fixup_resources(struct pci_dev *pdev) | |||
323 | } | 271 | } |
324 | 272 | ||
325 | /* | 273 | /* |
326 | * Loop through each node function to find usable EADs bridges. | ||
327 | */ | ||
328 | static void scan_PHB_slots(struct pci_controller *Phb) | ||
329 | { | ||
330 | struct HvCallPci_DeviceInfo *DevInfo; | ||
331 | HvBusNumber bus = Phb->local_number; /* System Bus */ | ||
332 | const HvSubBusNumber SubBus = 0; /* EADs is always 0. */ | ||
333 | int HvRc = 0; | ||
334 | int IdSel; | ||
335 | const int MaxAgents = 8; | ||
336 | |||
337 | DevInfo = (struct HvCallPci_DeviceInfo*) | ||
338 | kmalloc(sizeof(struct HvCallPci_DeviceInfo), GFP_KERNEL); | ||
339 | if (DevInfo == NULL) | ||
340 | return; | ||
341 | |||
342 | /* | ||
343 | * Probe for EADs Bridges | ||
344 | */ | ||
345 | for (IdSel = 1; IdSel < MaxAgents; ++IdSel) { | ||
346 | HvRc = HvCallPci_getDeviceInfo(bus, SubBus, IdSel, | ||
347 | iseries_hv_addr(DevInfo), | ||
348 | sizeof(struct HvCallPci_DeviceInfo)); | ||
349 | if (HvRc == 0) { | ||
350 | if (DevInfo->deviceType == HvCallPci_NodeDevice) | ||
351 | scan_EADS_bridge(bus, SubBus, IdSel); | ||
352 | else | ||
353 | printk("PCI: Invalid System Configuration(0x%02X)" | ||
354 | " for bus 0x%02x id 0x%02x.\n", | ||
355 | DevInfo->deviceType, bus, IdSel); | ||
356 | } | ||
357 | else | ||
358 | pci_Log_Error("getDeviceInfo", bus, SubBus, IdSel, HvRc); | ||
359 | } | ||
360 | kfree(DevInfo); | ||
361 | } | ||
362 | |||
363 | static void scan_EADS_bridge(HvBusNumber bus, HvSubBusNumber SubBus, | ||
364 | int IdSel) | ||
365 | { | ||
366 | struct HvCallPci_BridgeInfo *BridgeInfo; | ||
367 | HvAgentId AgentId; | ||
368 | int Function; | ||
369 | int HvRc; | ||
370 | |||
371 | BridgeInfo = (struct HvCallPci_BridgeInfo *) | ||
372 | kmalloc(sizeof(struct HvCallPci_BridgeInfo), GFP_KERNEL); | ||
373 | if (BridgeInfo == NULL) | ||
374 | return; | ||
375 | |||
376 | /* Note: hvSubBus and irq is always be 0 at this level! */ | ||
377 | for (Function = 0; Function < 8; ++Function) { | ||
378 | AgentId = ISERIES_PCI_AGENTID(IdSel, Function); | ||
379 | HvRc = HvCallXm_connectBusUnit(bus, SubBus, AgentId, 0); | ||
380 | if (HvRc == 0) { | ||
381 | printk("found device at bus %d idsel %d func %d (AgentId %x)\n", | ||
382 | bus, IdSel, Function, AgentId); | ||
383 | /* Connect EADs: 0x18.00.12 = 0x00 */ | ||
384 | HvRc = HvCallPci_getBusUnitInfo(bus, SubBus, AgentId, | ||
385 | iseries_hv_addr(BridgeInfo), | ||
386 | sizeof(struct HvCallPci_BridgeInfo)); | ||
387 | if (HvRc == 0) { | ||
388 | printk("bridge info: type %x subbus %x maxAgents %x maxsubbus %x logslot %x\n", | ||
389 | BridgeInfo->busUnitInfo.deviceType, | ||
390 | BridgeInfo->subBusNumber, | ||
391 | BridgeInfo->maxAgents, | ||
392 | BridgeInfo->maxSubBusNumber, | ||
393 | BridgeInfo->logicalSlotNumber); | ||
394 | if (BridgeInfo->busUnitInfo.deviceType == | ||
395 | HvCallPci_BridgeDevice) { | ||
396 | /* Scan_Bridge_Slot...: 0x18.00.12 */ | ||
397 | scan_bridge_slot(bus, BridgeInfo); | ||
398 | } else | ||
399 | printk("PCI: Invalid Bridge Configuration(0x%02X)", | ||
400 | BridgeInfo->busUnitInfo.deviceType); | ||
401 | } | ||
402 | } else if (HvRc != 0x000B) | ||
403 | pci_Log_Error("EADs Connect", | ||
404 | bus, SubBus, AgentId, HvRc); | ||
405 | } | ||
406 | kfree(BridgeInfo); | ||
407 | } | ||
408 | |||
409 | /* | ||
410 | * This assumes that the node slot is always on the primary bus! | ||
411 | */ | ||
412 | static int scan_bridge_slot(HvBusNumber Bus, | ||
413 | struct HvCallPci_BridgeInfo *BridgeInfo) | ||
414 | { | ||
415 | struct device_node *node; | ||
416 | HvSubBusNumber SubBus = BridgeInfo->subBusNumber; | ||
417 | u16 VendorId = 0; | ||
418 | int HvRc = 0; | ||
419 | u8 Irq = 0; | ||
420 | int IdSel = ISERIES_GET_DEVICE_FROM_SUBBUS(SubBus); | ||
421 | int Function = ISERIES_GET_FUNCTION_FROM_SUBBUS(SubBus); | ||
422 | HvAgentId EADsIdSel = ISERIES_PCI_AGENTID(IdSel, Function); | ||
423 | |||
424 | /* iSeries_allocate_IRQ.: 0x18.00.12(0xA3) */ | ||
425 | Irq = iSeries_allocate_IRQ(Bus, 0, EADsIdSel); | ||
426 | |||
427 | /* | ||
428 | * Connect all functions of any device found. | ||
429 | */ | ||
430 | for (IdSel = 1; IdSel <= BridgeInfo->maxAgents; ++IdSel) { | ||
431 | for (Function = 0; Function < 8; ++Function) { | ||
432 | HvAgentId AgentId = ISERIES_PCI_AGENTID(IdSel, Function); | ||
433 | HvRc = HvCallXm_connectBusUnit(Bus, SubBus, | ||
434 | AgentId, Irq); | ||
435 | if (HvRc != 0) { | ||
436 | pci_Log_Error("Connect Bus Unit", | ||
437 | Bus, SubBus, AgentId, HvRc); | ||
438 | continue; | ||
439 | } | ||
440 | |||
441 | HvRc = HvCallPci_configLoad16(Bus, SubBus, AgentId, | ||
442 | PCI_VENDOR_ID, &VendorId); | ||
443 | if (HvRc != 0) { | ||
444 | pci_Log_Error("Read Vendor", | ||
445 | Bus, SubBus, AgentId, HvRc); | ||
446 | continue; | ||
447 | } | ||
448 | printk("read vendor ID: %x\n", VendorId); | ||
449 | |||
450 | /* FoundDevice: 0x18.28.10 = 0x12AE */ | ||
451 | HvRc = HvCallPci_configStore8(Bus, SubBus, AgentId, | ||
452 | PCI_INTERRUPT_LINE, Irq); | ||
453 | if (HvRc != 0) | ||
454 | pci_Log_Error("PciCfgStore Irq Failed!", | ||
455 | Bus, SubBus, AgentId, HvRc); | ||
456 | |||
457 | ++DeviceCount; | ||
458 | node = build_device_node(Bus, SubBus, EADsIdSel, Function); | ||
459 | PCI_DN(node)->Irq = Irq; | ||
460 | PCI_DN(node)->LogicalSlot = BridgeInfo->logicalSlotNumber; | ||
461 | |||
462 | } /* for (Function = 0; Function < 8; ++Function) */ | ||
463 | } /* for (IdSel = 1; IdSel <= MaxAgents; ++IdSel) */ | ||
464 | return HvRc; | ||
465 | } | ||
466 | |||
467 | /* | ||
468 | * I/0 Memory copy MUST use mmio commands on iSeries | 274 | * I/0 Memory copy MUST use mmio commands on iSeries |
469 | * To do; For performance, include the hv call directly | 275 | * To do; For performance, include the hv call directly |
470 | */ | 276 | */ |
@@ -509,11 +315,13 @@ EXPORT_SYMBOL(iSeries_memcpy_fromio); | |||
509 | */ | 315 | */ |
510 | static struct device_node *find_Device_Node(int bus, int devfn) | 316 | static struct device_node *find_Device_Node(int bus, int devfn) |
511 | { | 317 | { |
512 | struct pci_dn *pdn; | 318 | struct device_node *node; |
319 | |||
320 | for (node = NULL; (node = of_find_all_nodes(node)); ) { | ||
321 | struct pci_dn *pdn = PCI_DN(node); | ||
513 | 322 | ||
514 | list_for_each_entry(pdn, &iSeries_Global_Device_List, Device_List) { | 323 | if (pdn && (bus == pdn->busno) && (devfn == pdn->devfn)) |
515 | if ((bus == pdn->busno) && (devfn == pdn->devfn)) | 324 | return node; |
516 | return pdn->node; | ||
517 | } | 325 | } |
518 | return NULL; | 326 | return NULL; |
519 | } | 327 | } |
@@ -625,7 +433,6 @@ static int CheckReturnCode(char *TextHdr, struct device_node *DevNode, | |||
625 | if (ret != 0) { | 433 | if (ret != 0) { |
626 | struct pci_dn *pdn = PCI_DN(DevNode); | 434 | struct pci_dn *pdn = PCI_DN(DevNode); |
627 | 435 | ||
628 | ++Pci_Error_Count; | ||
629 | (*retry)++; | 436 | (*retry)++; |
630 | printk("PCI: %s: Device 0x%04X:%02X I/O Error(%2d): 0x%04X\n", | 437 | printk("PCI: %s: Device 0x%04X:%02X I/O Error(%2d): 0x%04X\n", |
631 | TextHdr, pdn->busno, pdn->devfn, | 438 | TextHdr, pdn->busno, pdn->devfn, |
@@ -707,7 +514,6 @@ u8 iSeries_Read_Byte(const volatile void __iomem *IoAddress) | |||
707 | return 0xff; | 514 | return 0xff; |
708 | } | 515 | } |
709 | do { | 516 | do { |
710 | ++Pci_Io_Read_Count; | ||
711 | HvCall3Ret16(HvCallPciBarLoad8, &ret, dsa, BarOffset, 0); | 517 | HvCall3Ret16(HvCallPciBarLoad8, &ret, dsa, BarOffset, 0); |
712 | } while (CheckReturnCode("RDB", DevNode, &retry, ret.rc) != 0); | 518 | } while (CheckReturnCode("RDB", DevNode, &retry, ret.rc) != 0); |
713 | 519 | ||
@@ -737,7 +543,6 @@ u16 iSeries_Read_Word(const volatile void __iomem *IoAddress) | |||
737 | return 0xffff; | 543 | return 0xffff; |
738 | } | 544 | } |
739 | do { | 545 | do { |
740 | ++Pci_Io_Read_Count; | ||
741 | HvCall3Ret16(HvCallPciBarLoad16, &ret, dsa, | 546 | HvCall3Ret16(HvCallPciBarLoad16, &ret, dsa, |
742 | BarOffset, 0); | 547 | BarOffset, 0); |
743 | } while (CheckReturnCode("RDW", DevNode, &retry, ret.rc) != 0); | 548 | } while (CheckReturnCode("RDW", DevNode, &retry, ret.rc) != 0); |
@@ -768,7 +573,6 @@ u32 iSeries_Read_Long(const volatile void __iomem *IoAddress) | |||
768 | return 0xffffffff; | 573 | return 0xffffffff; |
769 | } | 574 | } |
770 | do { | 575 | do { |
771 | ++Pci_Io_Read_Count; | ||
772 | HvCall3Ret16(HvCallPciBarLoad32, &ret, dsa, | 576 | HvCall3Ret16(HvCallPciBarLoad32, &ret, dsa, |
773 | BarOffset, 0); | 577 | BarOffset, 0); |
774 | } while (CheckReturnCode("RDL", DevNode, &retry, ret.rc) != 0); | 578 | } while (CheckReturnCode("RDL", DevNode, &retry, ret.rc) != 0); |
@@ -806,7 +610,6 @@ void iSeries_Write_Byte(u8 data, volatile void __iomem *IoAddress) | |||
806 | return; | 610 | return; |
807 | } | 611 | } |
808 | do { | 612 | do { |
809 | ++Pci_Io_Write_Count; | ||
810 | rc = HvCall4(HvCallPciBarStore8, dsa, BarOffset, data, 0); | 613 | rc = HvCall4(HvCallPciBarStore8, dsa, BarOffset, data, 0); |
811 | } while (CheckReturnCode("WWB", DevNode, &retry, rc) != 0); | 614 | } while (CheckReturnCode("WWB", DevNode, &retry, rc) != 0); |
812 | } | 615 | } |
@@ -834,7 +637,6 @@ void iSeries_Write_Word(u16 data, volatile void __iomem *IoAddress) | |||
834 | return; | 637 | return; |
835 | } | 638 | } |
836 | do { | 639 | do { |
837 | ++Pci_Io_Write_Count; | ||
838 | rc = HvCall4(HvCallPciBarStore16, dsa, BarOffset, swab16(data), 0); | 640 | rc = HvCall4(HvCallPciBarStore16, dsa, BarOffset, swab16(data), 0); |
839 | } while (CheckReturnCode("WWW", DevNode, &retry, rc) != 0); | 641 | } while (CheckReturnCode("WWW", DevNode, &retry, rc) != 0); |
840 | } | 642 | } |
@@ -862,7 +664,6 @@ void iSeries_Write_Long(u32 data, volatile void __iomem *IoAddress) | |||
862 | return; | 664 | return; |
863 | } | 665 | } |
864 | do { | 666 | do { |
865 | ++Pci_Io_Write_Count; | ||
866 | rc = HvCall4(HvCallPciBarStore32, dsa, BarOffset, swab32(data), 0); | 667 | rc = HvCall4(HvCallPciBarStore32, dsa, BarOffset, swab32(data), 0); |
867 | } while (CheckReturnCode("WWL", DevNode, &retry, rc) != 0); | 668 | } while (CheckReturnCode("WWL", DevNode, &retry, rc) != 0); |
868 | } | 669 | } |
diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index a6fd9bedb074..617c724c4590 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c | |||
@@ -50,7 +50,6 @@ | |||
50 | #include <asm/iseries/hv_call_xm.h> | 50 | #include <asm/iseries/hv_call_xm.h> |
51 | #include <asm/iseries/it_lp_queue.h> | 51 | #include <asm/iseries/it_lp_queue.h> |
52 | #include <asm/iseries/mf.h> | 52 | #include <asm/iseries/mf.h> |
53 | #include <asm/iseries/it_exp_vpd_panel.h> | ||
54 | #include <asm/iseries/hv_lp_event.h> | 53 | #include <asm/iseries/hv_lp_event.h> |
55 | #include <asm/iseries/lpar_map.h> | 54 | #include <asm/iseries/lpar_map.h> |
56 | #include <asm/udbg.h> | 55 | #include <asm/udbg.h> |
@@ -81,9 +80,6 @@ extern void iSeries_pci_final_fixup(void); | |||
81 | static void iSeries_pci_final_fixup(void) { } | 80 | static void iSeries_pci_final_fixup(void) { } |
82 | #endif | 81 | #endif |
83 | 82 | ||
84 | /* Global Variables */ | ||
85 | int piranha_simulator; | ||
86 | |||
87 | extern int rd_size; /* Defined in drivers/block/rd.c */ | 83 | extern int rd_size; /* Defined in drivers/block/rd.c */ |
88 | extern unsigned long embedded_sysmap_start; | 84 | extern unsigned long embedded_sysmap_start; |
89 | extern unsigned long embedded_sysmap_end; | 85 | extern unsigned long embedded_sysmap_end; |
@@ -91,8 +87,6 @@ extern unsigned long embedded_sysmap_end; | |||
91 | extern unsigned long iSeries_recal_tb; | 87 | extern unsigned long iSeries_recal_tb; |
92 | extern unsigned long iSeries_recal_titan; | 88 | extern unsigned long iSeries_recal_titan; |
93 | 89 | ||
94 | static unsigned long cmd_mem_limit; | ||
95 | |||
96 | struct MemoryBlock { | 90 | struct MemoryBlock { |
97 | unsigned long absStart; | 91 | unsigned long absStart; |
98 | unsigned long absEnd; | 92 | unsigned long absEnd; |
@@ -340,8 +334,6 @@ static void __init iSeries_init_early(void) | |||
340 | #ifdef CONFIG_SMP | 334 | #ifdef CONFIG_SMP |
341 | smp_init_iSeries(); | 335 | smp_init_iSeries(); |
342 | #endif | 336 | #endif |
343 | if (itLpNaca.xPirEnvironMode == 0) | ||
344 | piranha_simulator = 1; | ||
345 | 337 | ||
346 | /* Associate Lp Event Queue 0 with processor 0 */ | 338 | /* Associate Lp Event Queue 0 with processor 0 */ |
347 | HvCallEvent_setLpEventQueueInterruptProc(0, 0); | 339 | HvCallEvent_setLpEventQueueInterruptProc(0, 0); |
@@ -536,10 +528,10 @@ static void __init iSeries_setup_arch(void) | |||
536 | { | 528 | { |
537 | if (get_lppaca()->shared_proc) { | 529 | if (get_lppaca()->shared_proc) { |
538 | ppc_md.idle_loop = iseries_shared_idle; | 530 | ppc_md.idle_loop = iseries_shared_idle; |
539 | printk(KERN_INFO "Using shared processor idle loop\n"); | 531 | printk(KERN_DEBUG "Using shared processor idle loop\n"); |
540 | } else { | 532 | } else { |
541 | ppc_md.idle_loop = iseries_dedicated_idle; | 533 | ppc_md.idle_loop = iseries_dedicated_idle; |
542 | printk(KERN_INFO "Using dedicated idle loop\n"); | 534 | printk(KERN_DEBUG "Using dedicated idle loop\n"); |
543 | } | 535 | } |
544 | 536 | ||
545 | /* Setup the Lp Event Queue */ | 537 | /* Setup the Lp Event Queue */ |
@@ -714,243 +706,6 @@ define_machine(iseries) { | |||
714 | /* XXX Implement enable_pmcs for iSeries */ | 706 | /* XXX Implement enable_pmcs for iSeries */ |
715 | }; | 707 | }; |
716 | 708 | ||
717 | struct blob { | ||
718 | unsigned char data[PAGE_SIZE]; | ||
719 | unsigned long next; | ||
720 | }; | ||
721 | |||
722 | struct iseries_flat_dt { | ||
723 | struct boot_param_header header; | ||
724 | u64 reserve_map[2]; | ||
725 | struct blob dt; | ||
726 | struct blob strings; | ||
727 | }; | ||
728 | |||
729 | struct iseries_flat_dt iseries_dt; | ||
730 | |||
731 | void dt_init(struct iseries_flat_dt *dt) | ||
732 | { | ||
733 | dt->header.off_mem_rsvmap = | ||
734 | offsetof(struct iseries_flat_dt, reserve_map); | ||
735 | dt->header.off_dt_struct = offsetof(struct iseries_flat_dt, dt); | ||
736 | dt->header.off_dt_strings = offsetof(struct iseries_flat_dt, strings); | ||
737 | dt->header.totalsize = sizeof(struct iseries_flat_dt); | ||
738 | dt->header.dt_strings_size = sizeof(struct blob); | ||
739 | |||
740 | /* There is no notion of hardware cpu id on iSeries */ | ||
741 | dt->header.boot_cpuid_phys = smp_processor_id(); | ||
742 | |||
743 | dt->dt.next = (unsigned long)&dt->dt.data; | ||
744 | dt->strings.next = (unsigned long)&dt->strings.data; | ||
745 | |||
746 | dt->header.magic = OF_DT_HEADER; | ||
747 | dt->header.version = 0x10; | ||
748 | dt->header.last_comp_version = 0x10; | ||
749 | |||
750 | dt->reserve_map[0] = 0; | ||
751 | dt->reserve_map[1] = 0; | ||
752 | } | ||
753 | |||
754 | void dt_check_blob(struct blob *b) | ||
755 | { | ||
756 | if (b->next >= (unsigned long)&b->next) { | ||
757 | DBG("Ran out of space in flat device tree blob!\n"); | ||
758 | BUG(); | ||
759 | } | ||
760 | } | ||
761 | |||
762 | void dt_push_u32(struct iseries_flat_dt *dt, u32 value) | ||
763 | { | ||
764 | *((u32*)dt->dt.next) = value; | ||
765 | dt->dt.next += sizeof(u32); | ||
766 | |||
767 | dt_check_blob(&dt->dt); | ||
768 | } | ||
769 | |||
770 | void dt_push_u64(struct iseries_flat_dt *dt, u64 value) | ||
771 | { | ||
772 | *((u64*)dt->dt.next) = value; | ||
773 | dt->dt.next += sizeof(u64); | ||
774 | |||
775 | dt_check_blob(&dt->dt); | ||
776 | } | ||
777 | |||
778 | unsigned long dt_push_bytes(struct blob *blob, char *data, int len) | ||
779 | { | ||
780 | unsigned long start = blob->next - (unsigned long)blob->data; | ||
781 | |||
782 | memcpy((char *)blob->next, data, len); | ||
783 | blob->next = _ALIGN(blob->next + len, 4); | ||
784 | |||
785 | dt_check_blob(blob); | ||
786 | |||
787 | return start; | ||
788 | } | ||
789 | |||
790 | void dt_start_node(struct iseries_flat_dt *dt, char *name) | ||
791 | { | ||
792 | dt_push_u32(dt, OF_DT_BEGIN_NODE); | ||
793 | dt_push_bytes(&dt->dt, name, strlen(name) + 1); | ||
794 | } | ||
795 | |||
796 | #define dt_end_node(dt) dt_push_u32(dt, OF_DT_END_NODE) | ||
797 | |||
798 | void dt_prop(struct iseries_flat_dt *dt, char *name, char *data, int len) | ||
799 | { | ||
800 | unsigned long offset; | ||
801 | |||
802 | dt_push_u32(dt, OF_DT_PROP); | ||
803 | |||
804 | /* Length of the data */ | ||
805 | dt_push_u32(dt, len); | ||
806 | |||
807 | /* Put the property name in the string blob. */ | ||
808 | offset = dt_push_bytes(&dt->strings, name, strlen(name) + 1); | ||
809 | |||
810 | /* The offset of the properties name in the string blob. */ | ||
811 | dt_push_u32(dt, (u32)offset); | ||
812 | |||
813 | /* The actual data. */ | ||
814 | dt_push_bytes(&dt->dt, data, len); | ||
815 | } | ||
816 | |||
817 | void dt_prop_str(struct iseries_flat_dt *dt, char *name, char *data) | ||
818 | { | ||
819 | dt_prop(dt, name, data, strlen(data) + 1); /* + 1 for NULL */ | ||
820 | } | ||
821 | |||
822 | void dt_prop_u32(struct iseries_flat_dt *dt, char *name, u32 data) | ||
823 | { | ||
824 | dt_prop(dt, name, (char *)&data, sizeof(u32)); | ||
825 | } | ||
826 | |||
827 | void dt_prop_u64(struct iseries_flat_dt *dt, char *name, u64 data) | ||
828 | { | ||
829 | dt_prop(dt, name, (char *)&data, sizeof(u64)); | ||
830 | } | ||
831 | |||
832 | void dt_prop_u64_list(struct iseries_flat_dt *dt, char *name, u64 *data, int n) | ||
833 | { | ||
834 | dt_prop(dt, name, (char *)data, sizeof(u64) * n); | ||
835 | } | ||
836 | |||
837 | void dt_prop_u32_list(struct iseries_flat_dt *dt, char *name, u32 *data, int n) | ||
838 | { | ||
839 | dt_prop(dt, name, (char *)data, sizeof(u32) * n); | ||
840 | } | ||
841 | |||
842 | void dt_prop_empty(struct iseries_flat_dt *dt, char *name) | ||
843 | { | ||
844 | dt_prop(dt, name, NULL, 0); | ||
845 | } | ||
846 | |||
847 | void dt_cpus(struct iseries_flat_dt *dt) | ||
848 | { | ||
849 | unsigned char buf[32]; | ||
850 | unsigned char *p; | ||
851 | unsigned int i, index; | ||
852 | struct IoHriProcessorVpd *d; | ||
853 | u32 pft_size[2]; | ||
854 | |||
855 | /* yuck */ | ||
856 | snprintf(buf, 32, "PowerPC,%s", cur_cpu_spec->cpu_name); | ||
857 | p = strchr(buf, ' '); | ||
858 | if (!p) p = buf + strlen(buf); | ||
859 | |||
860 | dt_start_node(dt, "cpus"); | ||
861 | dt_prop_u32(dt, "#address-cells", 1); | ||
862 | dt_prop_u32(dt, "#size-cells", 0); | ||
863 | |||
864 | pft_size[0] = 0; /* NUMA CEC cookie, 0 for non NUMA */ | ||
865 | pft_size[1] = __ilog2(HvCallHpt_getHptPages() * HW_PAGE_SIZE); | ||
866 | |||
867 | for (i = 0; i < NR_CPUS; i++) { | ||
868 | if (lppaca[i].dyn_proc_status >= 2) | ||
869 | continue; | ||
870 | |||
871 | snprintf(p, 32 - (p - buf), "@%d", i); | ||
872 | dt_start_node(dt, buf); | ||
873 | |||
874 | dt_prop_str(dt, "device_type", "cpu"); | ||
875 | |||
876 | index = lppaca[i].dyn_hv_phys_proc_index; | ||
877 | d = &xIoHriProcessorVpd[index]; | ||
878 | |||
879 | dt_prop_u32(dt, "i-cache-size", d->xInstCacheSize * 1024); | ||
880 | dt_prop_u32(dt, "i-cache-line-size", d->xInstCacheOperandSize); | ||
881 | |||
882 | dt_prop_u32(dt, "d-cache-size", d->xDataL1CacheSizeKB * 1024); | ||
883 | dt_prop_u32(dt, "d-cache-line-size", d->xDataCacheOperandSize); | ||
884 | |||
885 | /* magic conversions to Hz copied from old code */ | ||
886 | dt_prop_u32(dt, "clock-frequency", | ||
887 | ((1UL << 34) * 1000000) / d->xProcFreq); | ||
888 | dt_prop_u32(dt, "timebase-frequency", | ||
889 | ((1UL << 32) * 1000000) / d->xTimeBaseFreq); | ||
890 | |||
891 | dt_prop_u32(dt, "reg", i); | ||
892 | |||
893 | dt_prop_u32_list(dt, "ibm,pft-size", pft_size, 2); | ||
894 | |||
895 | dt_end_node(dt); | ||
896 | } | ||
897 | |||
898 | dt_end_node(dt); | ||
899 | } | ||
900 | |||
901 | void dt_model(struct iseries_flat_dt *dt) | ||
902 | { | ||
903 | char buf[16] = "IBM,"; | ||
904 | |||
905 | /* "IBM," + mfgId[2:3] + systemSerial[1:5] */ | ||
906 | strne2a(buf + 4, xItExtVpdPanel.mfgID + 2, 2); | ||
907 | strne2a(buf + 6, xItExtVpdPanel.systemSerial + 1, 5); | ||
908 | buf[11] = '\0'; | ||
909 | dt_prop_str(dt, "system-id", buf); | ||
910 | |||
911 | /* "IBM," + machineType[0:4] */ | ||
912 | strne2a(buf + 4, xItExtVpdPanel.machineType, 4); | ||
913 | buf[8] = '\0'; | ||
914 | dt_prop_str(dt, "model", buf); | ||
915 | |||
916 | dt_prop_str(dt, "compatible", "IBM,iSeries"); | ||
917 | } | ||
918 | |||
919 | void build_flat_dt(struct iseries_flat_dt *dt, unsigned long phys_mem_size) | ||
920 | { | ||
921 | u64 tmp[2]; | ||
922 | |||
923 | dt_init(dt); | ||
924 | |||
925 | dt_start_node(dt, ""); | ||
926 | |||
927 | dt_prop_u32(dt, "#address-cells", 2); | ||
928 | dt_prop_u32(dt, "#size-cells", 2); | ||
929 | dt_model(dt); | ||
930 | |||
931 | /* /memory */ | ||
932 | dt_start_node(dt, "memory@0"); | ||
933 | dt_prop_str(dt, "name", "memory"); | ||
934 | dt_prop_str(dt, "device_type", "memory"); | ||
935 | tmp[0] = 0; | ||
936 | tmp[1] = phys_mem_size; | ||
937 | dt_prop_u64_list(dt, "reg", tmp, 2); | ||
938 | dt_end_node(dt); | ||
939 | |||
940 | /* /chosen */ | ||
941 | dt_start_node(dt, "chosen"); | ||
942 | dt_prop_str(dt, "bootargs", cmd_line); | ||
943 | if (cmd_mem_limit) | ||
944 | dt_prop_u64(dt, "linux,memory-limit", cmd_mem_limit); | ||
945 | dt_end_node(dt); | ||
946 | |||
947 | dt_cpus(dt); | ||
948 | |||
949 | dt_end_node(dt); | ||
950 | |||
951 | dt_push_u32(dt, OF_DT_END); | ||
952 | } | ||
953 | |||
954 | void * __init iSeries_early_setup(void) | 709 | void * __init iSeries_early_setup(void) |
955 | { | 710 | { |
956 | unsigned long phys_mem_size; | 711 | unsigned long phys_mem_size; |
@@ -965,28 +720,8 @@ void * __init iSeries_early_setup(void) | |||
965 | 720 | ||
966 | iSeries_get_cmdline(); | 721 | iSeries_get_cmdline(); |
967 | 722 | ||
968 | /* Save unparsed command line copy for /proc/cmdline */ | 723 | return (void *) __pa(build_flat_dt(phys_mem_size)); |
969 | strlcpy(saved_command_line, cmd_line, COMMAND_LINE_SIZE); | ||
970 | |||
971 | /* Parse early parameters, in particular mem=x */ | ||
972 | parse_early_param(); | ||
973 | |||
974 | build_flat_dt(&iseries_dt, phys_mem_size); | ||
975 | |||
976 | return (void *) __pa(&iseries_dt); | ||
977 | } | ||
978 | |||
979 | /* | ||
980 | * On iSeries we just parse the mem=X option from the command line. | ||
981 | * On pSeries it's a bit more complicated, see prom_init_mem() | ||
982 | */ | ||
983 | static int __init early_parsemem(char *p) | ||
984 | { | ||
985 | if (p) | ||
986 | cmd_mem_limit = ALIGN(memparse(p, &p), PAGE_SIZE); | ||
987 | return 0; | ||
988 | } | 724 | } |
989 | early_param("mem", early_parsemem); | ||
990 | 725 | ||
991 | static void hvputc(char c) | 726 | static void hvputc(char c) |
992 | { | 727 | { |
diff --git a/arch/powerpc/platforms/iseries/setup.h b/arch/powerpc/platforms/iseries/setup.h index 5213044ec411..0a47ac53c959 100644 --- a/arch/powerpc/platforms/iseries/setup.h +++ b/arch/powerpc/platforms/iseries/setup.h | |||
@@ -21,4 +21,6 @@ extern unsigned long iSeries_get_boot_time(void); | |||
21 | extern int iSeries_set_rtc_time(struct rtc_time *tm); | 21 | extern int iSeries_set_rtc_time(struct rtc_time *tm); |
22 | extern void iSeries_get_rtc_time(struct rtc_time *tm); | 22 | extern void iSeries_get_rtc_time(struct rtc_time *tm); |
23 | 23 | ||
24 | extern void *build_flat_dt(unsigned long phys_mem_size); | ||
25 | |||
24 | #endif /* __ISERIES_SETUP_H__ */ | 26 | #endif /* __ISERIES_SETUP_H__ */ |
diff --git a/arch/powerpc/platforms/iseries/vio.c b/arch/powerpc/platforms/iseries/vio.c deleted file mode 100644 index ad36ab0639f0..000000000000 --- a/arch/powerpc/platforms/iseries/vio.c +++ /dev/null | |||
@@ -1,131 +0,0 @@ | |||
1 | /* | ||
2 | * IBM PowerPC iSeries Virtual I/O Infrastructure Support. | ||
3 | * | ||
4 | * Copyright (c) 2005 Stephen Rothwell, IBM Corp. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | #include <linux/types.h> | ||
12 | #include <linux/device.h> | ||
13 | #include <linux/init.h> | ||
14 | |||
15 | #include <asm/vio.h> | ||
16 | #include <asm/iommu.h> | ||
17 | #include <asm/tce.h> | ||
18 | #include <asm/abs_addr.h> | ||
19 | #include <asm/page.h> | ||
20 | #include <asm/iseries/vio.h> | ||
21 | #include <asm/iseries/hv_types.h> | ||
22 | #include <asm/iseries/hv_lp_config.h> | ||
23 | #include <asm/iseries/hv_call_xm.h> | ||
24 | |||
25 | #include "iommu.h" | ||
26 | |||
27 | struct device *iSeries_vio_dev = &vio_bus_device.dev; | ||
28 | EXPORT_SYMBOL(iSeries_vio_dev); | ||
29 | |||
30 | static struct iommu_table veth_iommu_table; | ||
31 | static struct iommu_table vio_iommu_table; | ||
32 | |||
33 | static void __init iommu_vio_init(void) | ||
34 | { | ||
35 | iommu_table_getparms_iSeries(255, 0, 0xff, &veth_iommu_table); | ||
36 | veth_iommu_table.it_size /= 2; | ||
37 | vio_iommu_table = veth_iommu_table; | ||
38 | vio_iommu_table.it_offset += veth_iommu_table.it_size; | ||
39 | |||
40 | if (!iommu_init_table(&veth_iommu_table)) | ||
41 | printk("Virtual Bus VETH TCE table failed.\n"); | ||
42 | if (!iommu_init_table(&vio_iommu_table)) | ||
43 | printk("Virtual Bus VIO TCE table failed.\n"); | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * vio_register_device_iseries: - Register a new iSeries vio device. | ||
48 | * @voidev: The device to register. | ||
49 | */ | ||
50 | static struct vio_dev *__init vio_register_device_iseries(char *type, | ||
51 | uint32_t unit_num) | ||
52 | { | ||
53 | struct vio_dev *viodev; | ||
54 | |||
55 | /* allocate a vio_dev for this device */ | ||
56 | viodev = kmalloc(sizeof(struct vio_dev), GFP_KERNEL); | ||
57 | if (!viodev) | ||
58 | return NULL; | ||
59 | memset(viodev, 0, sizeof(struct vio_dev)); | ||
60 | |||
61 | snprintf(viodev->dev.bus_id, BUS_ID_SIZE, "%s%d", type, unit_num); | ||
62 | |||
63 | viodev->name = viodev->dev.bus_id; | ||
64 | viodev->type = type; | ||
65 | viodev->unit_address = unit_num; | ||
66 | viodev->iommu_table = &vio_iommu_table; | ||
67 | if (vio_register_device(viodev) == NULL) { | ||
68 | kfree(viodev); | ||
69 | return NULL; | ||
70 | } | ||
71 | return viodev; | ||
72 | } | ||
73 | |||
74 | void __init probe_bus_iseries(void) | ||
75 | { | ||
76 | HvLpIndexMap vlan_map; | ||
77 | struct vio_dev *viodev; | ||
78 | int i; | ||
79 | |||
80 | /* there is only one of each of these */ | ||
81 | vio_register_device_iseries("viocons", 0); | ||
82 | vio_register_device_iseries("vscsi", 0); | ||
83 | |||
84 | vlan_map = HvLpConfig_getVirtualLanIndexMap(); | ||
85 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) { | ||
86 | if ((vlan_map & (0x8000 >> i)) == 0) | ||
87 | continue; | ||
88 | viodev = vio_register_device_iseries("vlan", i); | ||
89 | /* veth is special and has it own iommu_table */ | ||
90 | viodev->iommu_table = &veth_iommu_table; | ||
91 | } | ||
92 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALDISKS; i++) | ||
93 | vio_register_device_iseries("viodasd", i); | ||
94 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALCDROMS; i++) | ||
95 | vio_register_device_iseries("viocd", i); | ||
96 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALTAPES; i++) | ||
97 | vio_register_device_iseries("viotape", i); | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * vio_match_device_iseries: - Tell if a iSeries VIO device matches a | ||
102 | * vio_device_id | ||
103 | */ | ||
104 | static int vio_match_device_iseries(const struct vio_device_id *id, | ||
105 | const struct vio_dev *dev) | ||
106 | { | ||
107 | return strncmp(dev->type, id->type, strlen(id->type)) == 0; | ||
108 | } | ||
109 | |||
110 | static struct vio_bus_ops vio_bus_ops_iseries = { | ||
111 | .match = vio_match_device_iseries, | ||
112 | }; | ||
113 | |||
114 | /** | ||
115 | * vio_bus_init_iseries: - Initialize the iSeries virtual IO bus | ||
116 | */ | ||
117 | static int __init vio_bus_init_iseries(void) | ||
118 | { | ||
119 | int err; | ||
120 | |||
121 | err = vio_bus_init(&vio_bus_ops_iseries); | ||
122 | if (err == 0) { | ||
123 | iommu_vio_init(); | ||
124 | vio_bus_device.iommu_table = &vio_iommu_table; | ||
125 | iSeries_vio_dev = &vio_bus_device.dev; | ||
126 | probe_bus_iseries(); | ||
127 | } | ||
128 | return err; | ||
129 | } | ||
130 | |||
131 | __initcall(vio_bus_init_iseries); | ||