diff options
Diffstat (limited to 'arch/powerpc/platforms/iseries')
-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/pci.c | 273 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/setup.c | 204 |
4 files changed, 286 insertions, 200 deletions
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/pci.c b/arch/powerpc/platforms/iseries/pci.c index 91a94747eda9..9d571e749098 100644 --- a/arch/powerpc/platforms/iseries/pci.c +++ b/arch/powerpc/platforms/iseries/pci.c | |||
@@ -49,14 +49,9 @@ | |||
49 | * Forward declares of prototypes. | 49 | * Forward declares of prototypes. |
50 | */ | 50 | */ |
51 | static struct device_node *find_Device_Node(int bus, int devfn); | 51 | static struct device_node *find_Device_Node(int bus, int devfn); |
52 | static void scan_PHB_slots(struct pci_controller *Phb); | ||
53 | static void scan_EADS_bridge(HvBusNumber Bus, HvSubBusNumber SubBus, int IdSel); | ||
54 | static int scan_bridge_slot(HvBusNumber Bus, struct HvCallPci_BridgeInfo *Info); | ||
55 | 52 | ||
56 | LIST_HEAD(iSeries_Global_Device_List); | 53 | LIST_HEAD(iSeries_Global_Device_List); |
57 | 54 | ||
58 | static int DeviceCount; | ||
59 | |||
60 | static int Pci_Retry_Max = 3; /* Only retry 3 times */ | 55 | static int Pci_Retry_Max = 3; /* Only retry 3 times */ |
61 | static int Pci_Error_Flag = 1; /* Set Retry Error on. */ | 56 | static int Pci_Error_Flag = 1; /* Set Retry Error on. */ |
62 | 57 | ||
@@ -162,32 +157,6 @@ static void pci_Log_Error(char *Error_Text, int Bus, int SubBus, | |||
162 | } | 157 | } |
163 | 158 | ||
164 | /* | 159 | /* |
165 | * build_device_node(u16 Bus, int SubBus, u8 DevFn) | ||
166 | */ | ||
167 | static struct device_node *build_device_node(HvBusNumber Bus, | ||
168 | HvSubBusNumber SubBus, int AgentId, int Function) | ||
169 | { | ||
170 | struct device_node *node; | ||
171 | struct pci_dn *pdn; | ||
172 | |||
173 | node = kzalloc(sizeof(struct device_node), GFP_KERNEL); | ||
174 | if (node == NULL) | ||
175 | return NULL; | ||
176 | pdn = kzalloc(sizeof(*pdn), GFP_KERNEL); | ||
177 | if (pdn == NULL) { | ||
178 | kfree(node); | ||
179 | return NULL; | ||
180 | } | ||
181 | node->data = pdn; | ||
182 | pdn->node = node; | ||
183 | list_add_tail(&pdn->Device_List, &iSeries_Global_Device_List); | ||
184 | pdn->busno = Bus; | ||
185 | pdn->bussubno = SubBus; | ||
186 | pdn->devfn = PCI_DEVFN(ISERIES_ENCODE_DEVICE(AgentId), Function); | ||
187 | return node; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * iSeries_pcibios_init | 160 | * iSeries_pcibios_init |
192 | * | 161 | * |
193 | * Description: | 162 | * Description: |
@@ -199,33 +168,86 @@ static struct device_node *build_device_node(HvBusNumber Bus, | |||
199 | void iSeries_pcibios_init(void) | 168 | void iSeries_pcibios_init(void) |
200 | { | 169 | { |
201 | struct pci_controller *phb; | 170 | struct pci_controller *phb; |
202 | HvBusNumber bus; | 171 | struct device_node *node; |
203 | 172 | struct device_node *dn; | |
204 | /* Check all possible buses. */ | 173 | |
205 | for (bus = 0; bus < 256; bus++) { | 174 | for_each_node_by_type(node, "pci") { |
206 | int ret = HvCallXm_testBus(bus); | 175 | HvBusNumber bus; |
207 | if (ret == 0) { | 176 | u32 *busp; |
208 | printk("bus %d appears to exist\n", bus); | 177 | |
209 | 178 | busp = (u32 *)get_property(node, "bus-range", NULL); | |
210 | phb = pcibios_alloc_controller(NULL); | 179 | if (busp == NULL) |
211 | if (phb == NULL) | 180 | continue; |
212 | return -ENOMEM; | 181 | bus = *busp; |
182 | printk("bus %d appears to exist\n", bus); | ||
183 | phb = pcibios_alloc_controller(node); | ||
184 | if (phb == NULL) | ||
185 | continue; | ||
186 | |||
187 | phb->pci_mem_offset = phb->local_number = bus; | ||
188 | phb->first_busno = bus; | ||
189 | phb->last_busno = bus; | ||
190 | phb->ops = &iSeries_pci_ops; | ||
191 | |||
192 | /* Find and connect the devices. */ | ||
193 | for (dn = NULL; (dn = of_get_next_child(node, dn)) != NULL;) { | ||
194 | struct pci_dn *pdn; | ||
195 | u8 irq; | ||
196 | int err; | ||
197 | u32 *agent; | ||
198 | u32 *reg; | ||
199 | u32 *lsn; | ||
200 | |||
201 | reg = (u32 *)get_property(dn, "reg", NULL); | ||
202 | if (reg == NULL) { | ||
203 | printk(KERN_DEBUG "no reg property!\n"); | ||
204 | continue; | ||
205 | } | ||
206 | busp = (u32 *)get_property(dn, "linux,subbus", NULL); | ||
207 | if (busp == NULL) { | ||
208 | printk(KERN_DEBUG "no subbus property!\n"); | ||
209 | continue; | ||
210 | } | ||
211 | agent = (u32 *)get_property(dn, "linux,agent-id", NULL); | ||
212 | if (agent == NULL) { | ||
213 | printk(KERN_DEBUG "no agent-id\n"); | ||
214 | continue; | ||
215 | } | ||
216 | lsn = (u32 *)get_property(dn, | ||
217 | "linux,logical-slot-number", NULL); | ||
218 | if (lsn == NULL) { | ||
219 | printk(KERN_DEBUG "no logical-slot-number\n"); | ||
220 | continue; | ||
221 | } | ||
213 | 222 | ||
214 | phb->pci_mem_offset = phb->local_number = bus; | 223 | irq = iSeries_allocate_IRQ(bus, 0, *busp); |
215 | phb->first_busno = bus; | 224 | err = HvCallXm_connectBusUnit(bus, *busp, *agent, irq); |
216 | phb->last_busno = bus; | 225 | if (err) { |
217 | phb->ops = &iSeries_pci_ops; | 226 | pci_Log_Error("Connect Bus Unit", |
227 | bus, *busp, *agent, err); | ||
228 | continue; | ||
229 | } | ||
230 | err = HvCallPci_configStore8(bus, *busp, *agent, | ||
231 | PCI_INTERRUPT_LINE, irq); | ||
232 | if (err) { | ||
233 | pci_Log_Error("PciCfgStore Irq Failed!", | ||
234 | bus, *busp, *agent, err); | ||
235 | continue; | ||
236 | } | ||
218 | 237 | ||
219 | /* Find and connect the devices. */ | 238 | pdn = kzalloc(sizeof(*pdn), GFP_KERNEL); |
220 | scan_PHB_slots(phb); | 239 | if (pdn == NULL) |
240 | return; | ||
241 | dn->data = pdn; | ||
242 | pdn->node = dn; | ||
243 | pdn->busno = bus; | ||
244 | pdn->devfn = (reg[0] >> 8) & 0xff; | ||
245 | pdn->bussubno = *busp; | ||
246 | pdn->Irq = irq; | ||
247 | pdn->LogicalSlot = *lsn; | ||
248 | list_add_tail(&pdn->Device_List, | ||
249 | &iSeries_Global_Device_List); | ||
221 | } | 250 | } |
222 | /* | ||
223 | * Check for Unexpected Return code, a clue that something | ||
224 | * has gone wrong. | ||
225 | */ | ||
226 | else if (ret != 0x0301) | ||
227 | printk(KERN_ERR "Unexpected Return on Probe(0x%04X): 0x%04X", | ||
228 | bus, ret); | ||
229 | } | 251 | } |
230 | } | 252 | } |
231 | 253 | ||
@@ -272,147 +294,6 @@ void pcibios_fixup_resources(struct pci_dev *pdev) | |||
272 | } | 294 | } |
273 | 295 | ||
274 | /* | 296 | /* |
275 | * Loop through each node function to find usable EADs bridges. | ||
276 | */ | ||
277 | static void scan_PHB_slots(struct pci_controller *Phb) | ||
278 | { | ||
279 | struct HvCallPci_DeviceInfo *DevInfo; | ||
280 | HvBusNumber bus = Phb->local_number; /* System Bus */ | ||
281 | const HvSubBusNumber SubBus = 0; /* EADs is always 0. */ | ||
282 | int HvRc = 0; | ||
283 | int IdSel; | ||
284 | const int MaxAgents = 8; | ||
285 | |||
286 | DevInfo = kmalloc(sizeof(struct HvCallPci_DeviceInfo), GFP_KERNEL); | ||
287 | if (DevInfo == NULL) | ||
288 | return; | ||
289 | |||
290 | /* | ||
291 | * Probe for EADs Bridges | ||
292 | */ | ||
293 | for (IdSel = 1; IdSel < MaxAgents; ++IdSel) { | ||
294 | HvRc = HvCallPci_getDeviceInfo(bus, SubBus, IdSel, | ||
295 | iseries_hv_addr(DevInfo), | ||
296 | sizeof(struct HvCallPci_DeviceInfo)); | ||
297 | if (HvRc == 0) { | ||
298 | if (DevInfo->deviceType == HvCallPci_NodeDevice) | ||
299 | scan_EADS_bridge(bus, SubBus, IdSel); | ||
300 | else | ||
301 | printk("PCI: Invalid System Configuration(0x%02X)" | ||
302 | " for bus 0x%02x id 0x%02x.\n", | ||
303 | DevInfo->deviceType, bus, IdSel); | ||
304 | } | ||
305 | else | ||
306 | pci_Log_Error("getDeviceInfo", bus, SubBus, IdSel, HvRc); | ||
307 | } | ||
308 | kfree(DevInfo); | ||
309 | } | ||
310 | |||
311 | static void scan_EADS_bridge(HvBusNumber bus, HvSubBusNumber SubBus, | ||
312 | int IdSel) | ||
313 | { | ||
314 | struct HvCallPci_BridgeInfo *BridgeInfo; | ||
315 | HvAgentId AgentId; | ||
316 | int Function; | ||
317 | int HvRc; | ||
318 | |||
319 | BridgeInfo = (struct HvCallPci_BridgeInfo *) | ||
320 | kmalloc(sizeof(struct HvCallPci_BridgeInfo), GFP_KERNEL); | ||
321 | if (BridgeInfo == NULL) | ||
322 | return; | ||
323 | |||
324 | /* Note: hvSubBus and irq is always be 0 at this level! */ | ||
325 | for (Function = 0; Function < 8; ++Function) { | ||
326 | AgentId = ISERIES_PCI_AGENTID(IdSel, Function); | ||
327 | HvRc = HvCallXm_connectBusUnit(bus, SubBus, AgentId, 0); | ||
328 | if (HvRc == 0) { | ||
329 | printk("found device at bus %d idsel %d func %d (AgentId %x)\n", | ||
330 | bus, IdSel, Function, AgentId); | ||
331 | /* Connect EADs: 0x18.00.12 = 0x00 */ | ||
332 | HvRc = HvCallPci_getBusUnitInfo(bus, SubBus, AgentId, | ||
333 | iseries_hv_addr(BridgeInfo), | ||
334 | sizeof(struct HvCallPci_BridgeInfo)); | ||
335 | if (HvRc == 0) { | ||
336 | printk("bridge info: type %x subbus %x maxAgents %x maxsubbus %x logslot %x\n", | ||
337 | BridgeInfo->busUnitInfo.deviceType, | ||
338 | BridgeInfo->subBusNumber, | ||
339 | BridgeInfo->maxAgents, | ||
340 | BridgeInfo->maxSubBusNumber, | ||
341 | BridgeInfo->logicalSlotNumber); | ||
342 | if (BridgeInfo->busUnitInfo.deviceType == | ||
343 | HvCallPci_BridgeDevice) { | ||
344 | /* Scan_Bridge_Slot...: 0x18.00.12 */ | ||
345 | scan_bridge_slot(bus, BridgeInfo); | ||
346 | } else | ||
347 | printk("PCI: Invalid Bridge Configuration(0x%02X)", | ||
348 | BridgeInfo->busUnitInfo.deviceType); | ||
349 | } | ||
350 | } else if (HvRc != 0x000B) | ||
351 | pci_Log_Error("EADs Connect", | ||
352 | bus, SubBus, AgentId, HvRc); | ||
353 | } | ||
354 | kfree(BridgeInfo); | ||
355 | } | ||
356 | |||
357 | /* | ||
358 | * This assumes that the node slot is always on the primary bus! | ||
359 | */ | ||
360 | static int scan_bridge_slot(HvBusNumber Bus, | ||
361 | struct HvCallPci_BridgeInfo *BridgeInfo) | ||
362 | { | ||
363 | struct device_node *node; | ||
364 | HvSubBusNumber SubBus = BridgeInfo->subBusNumber; | ||
365 | u16 VendorId = 0; | ||
366 | int HvRc = 0; | ||
367 | u8 Irq = 0; | ||
368 | int IdSel = ISERIES_GET_DEVICE_FROM_SUBBUS(SubBus); | ||
369 | int Function = ISERIES_GET_FUNCTION_FROM_SUBBUS(SubBus); | ||
370 | HvAgentId EADsIdSel = ISERIES_PCI_AGENTID(IdSel, Function); | ||
371 | |||
372 | /* iSeries_allocate_IRQ.: 0x18.00.12(0xA3) */ | ||
373 | Irq = iSeries_allocate_IRQ(Bus, 0, EADsIdSel); | ||
374 | |||
375 | /* | ||
376 | * Connect all functions of any device found. | ||
377 | */ | ||
378 | for (IdSel = 1; IdSel <= BridgeInfo->maxAgents; ++IdSel) { | ||
379 | for (Function = 0; Function < 8; ++Function) { | ||
380 | HvAgentId AgentId = ISERIES_PCI_AGENTID(IdSel, Function); | ||
381 | HvRc = HvCallXm_connectBusUnit(Bus, SubBus, | ||
382 | AgentId, Irq); | ||
383 | if (HvRc != 0) { | ||
384 | pci_Log_Error("Connect Bus Unit", | ||
385 | Bus, SubBus, AgentId, HvRc); | ||
386 | continue; | ||
387 | } | ||
388 | |||
389 | HvRc = HvCallPci_configLoad16(Bus, SubBus, AgentId, | ||
390 | PCI_VENDOR_ID, &VendorId); | ||
391 | if (HvRc != 0) { | ||
392 | pci_Log_Error("Read Vendor", | ||
393 | Bus, SubBus, AgentId, HvRc); | ||
394 | continue; | ||
395 | } | ||
396 | printk("read vendor ID: %x\n", VendorId); | ||
397 | |||
398 | /* FoundDevice: 0x18.28.10 = 0x12AE */ | ||
399 | HvRc = HvCallPci_configStore8(Bus, SubBus, AgentId, | ||
400 | PCI_INTERRUPT_LINE, Irq); | ||
401 | if (HvRc != 0) | ||
402 | pci_Log_Error("PciCfgStore Irq Failed!", | ||
403 | Bus, SubBus, AgentId, HvRc); | ||
404 | |||
405 | ++DeviceCount; | ||
406 | node = build_device_node(Bus, SubBus, EADsIdSel, Function); | ||
407 | PCI_DN(node)->Irq = Irq; | ||
408 | PCI_DN(node)->LogicalSlot = BridgeInfo->logicalSlotNumber; | ||
409 | |||
410 | } /* for (Function = 0; Function < 8; ++Function) */ | ||
411 | } /* for (IdSel = 1; IdSel <= MaxAgents; ++IdSel) */ | ||
412 | return HvRc; | ||
413 | } | ||
414 | |||
415 | /* | ||
416 | * I/0 Memory copy MUST use mmio commands on iSeries | 297 | * I/0 Memory copy MUST use mmio commands on iSeries |
417 | * To do; For performance, include the hv call directly | 298 | * To do; For performance, include the hv call directly |
418 | */ | 299 | */ |
diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index fd6d0ebe8ddd..d83f5ed4ec1f 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c | |||
@@ -66,6 +66,8 @@ | |||
66 | #include "main_store.h" | 66 | #include "main_store.h" |
67 | #include "call_sm.h" | 67 | #include "call_sm.h" |
68 | #include "call_hpt.h" | 68 | #include "call_hpt.h" |
69 | #include "call_pci.h" | ||
70 | #include "pci.h" | ||
69 | 71 | ||
70 | #ifdef DEBUG | 72 | #ifdef DEBUG |
71 | #define DBG(fmt...) udbg_printf(fmt) | 73 | #define DBG(fmt...) udbg_printf(fmt) |
@@ -1000,6 +1002,207 @@ void dt_vdevices(struct iseries_flat_dt *dt) | |||
1000 | dt_end_node(dt); | 1002 | dt_end_node(dt); |
1001 | } | 1003 | } |
1002 | 1004 | ||
1005 | /* | ||
1006 | * This assumes that the node slot is always on the primary bus! | ||
1007 | */ | ||
1008 | static void scan_bridge_slot(struct iseries_flat_dt *dt, HvBusNumber bus, | ||
1009 | struct HvCallPci_BridgeInfo *bridge_info) | ||
1010 | { | ||
1011 | HvSubBusNumber sub_bus = bridge_info->subBusNumber; | ||
1012 | u16 vendor_id; | ||
1013 | u16 device_id; | ||
1014 | u32 class_id; | ||
1015 | int err; | ||
1016 | char buf[32]; | ||
1017 | u32 reg[5]; | ||
1018 | int id_sel = ISERIES_GET_DEVICE_FROM_SUBBUS(sub_bus); | ||
1019 | int function = ISERIES_GET_FUNCTION_FROM_SUBBUS(sub_bus); | ||
1020 | HvAgentId eads_id_sel = ISERIES_PCI_AGENTID(id_sel, function); | ||
1021 | |||
1022 | /* | ||
1023 | * Connect all functions of any device found. | ||
1024 | */ | ||
1025 | for (id_sel = 1; id_sel <= bridge_info->maxAgents; id_sel++) { | ||
1026 | for (function = 0; function < 8; function++) { | ||
1027 | u8 devfn; | ||
1028 | |||
1029 | HvAgentId agent_id = ISERIES_PCI_AGENTID(id_sel, | ||
1030 | function); | ||
1031 | err = HvCallXm_connectBusUnit(bus, sub_bus, | ||
1032 | agent_id, 0); | ||
1033 | if (err) { | ||
1034 | if (err != 0x302) | ||
1035 | printk(KERN_DEBUG | ||
1036 | "connectBusUnit(%x, %x, %x) " | ||
1037 | "== %x\n", | ||
1038 | bus, sub_bus, agent_id, err); | ||
1039 | continue; | ||
1040 | } | ||
1041 | |||
1042 | err = HvCallPci_configLoad16(bus, sub_bus, agent_id, | ||
1043 | PCI_VENDOR_ID, &vendor_id); | ||
1044 | if (err) { | ||
1045 | printk(KERN_DEBUG | ||
1046 | "ReadVendor(%x, %x, %x) == %x\n", | ||
1047 | bus, sub_bus, agent_id, err); | ||
1048 | continue; | ||
1049 | } | ||
1050 | err = HvCallPci_configLoad16(bus, sub_bus, agent_id, | ||
1051 | PCI_DEVICE_ID, &device_id); | ||
1052 | if (err) { | ||
1053 | printk(KERN_DEBUG | ||
1054 | "ReadDevice(%x, %x, %x) == %x\n", | ||
1055 | bus, sub_bus, agent_id, err); | ||
1056 | continue; | ||
1057 | } | ||
1058 | err = HvCallPci_configLoad32(bus, sub_bus, agent_id, | ||
1059 | PCI_CLASS_REVISION , &class_id); | ||
1060 | if (err) { | ||
1061 | printk(KERN_DEBUG | ||
1062 | "ReadClass(%x, %x, %x) == %x\n", | ||
1063 | bus, sub_bus, agent_id, err); | ||
1064 | continue; | ||
1065 | } | ||
1066 | |||
1067 | devfn = PCI_DEVFN(ISERIES_ENCODE_DEVICE(eads_id_sel), | ||
1068 | function); | ||
1069 | if (function == 0) | ||
1070 | snprintf(buf, sizeof(buf), "pci@%x", | ||
1071 | PCI_SLOT(devfn)); | ||
1072 | else | ||
1073 | snprintf(buf, sizeof(buf), "pci@%x,%d", | ||
1074 | PCI_SLOT(devfn), function); | ||
1075 | dt_start_node(dt, buf); | ||
1076 | reg[0] = (bus << 18) | (devfn << 8); | ||
1077 | reg[1] = 0; | ||
1078 | reg[2] = 0; | ||
1079 | reg[3] = 0; | ||
1080 | reg[4] = 0; | ||
1081 | dt_prop_u32_list(dt, "reg", reg, 5); | ||
1082 | dt_prop_u32(dt, "vendor-id", vendor_id); | ||
1083 | dt_prop_u32(dt, "device-id", device_id); | ||
1084 | dt_prop_u32(dt, "class-code", class_id >> 8); | ||
1085 | dt_prop_u32(dt, "revision-id", class_id & 0xff); | ||
1086 | dt_prop_u32(dt, "linux,subbus", sub_bus); | ||
1087 | dt_prop_u32(dt, "linux,agent-id", agent_id); | ||
1088 | dt_prop_u32(dt, "linux,logical-slot-number", | ||
1089 | bridge_info->logicalSlotNumber); | ||
1090 | dt_end_node(dt); | ||
1091 | |||
1092 | } | ||
1093 | } | ||
1094 | } | ||
1095 | |||
1096 | static void scan_bridge(struct iseries_flat_dt *dt, HvBusNumber bus, | ||
1097 | HvSubBusNumber sub_bus, int id_sel) | ||
1098 | { | ||
1099 | struct HvCallPci_BridgeInfo bridge_info; | ||
1100 | HvAgentId agent_id; | ||
1101 | int function; | ||
1102 | int ret; | ||
1103 | |||
1104 | /* Note: hvSubBus and irq is always be 0 at this level! */ | ||
1105 | for (function = 0; function < 8; ++function) { | ||
1106 | agent_id = ISERIES_PCI_AGENTID(id_sel, function); | ||
1107 | ret = HvCallXm_connectBusUnit(bus, sub_bus, agent_id, 0); | ||
1108 | if (ret != 0) { | ||
1109 | if (ret != 0xb) | ||
1110 | printk(KERN_DEBUG "connectBusUnit(%x, %x, %x) " | ||
1111 | "== %x\n", | ||
1112 | bus, sub_bus, agent_id, ret); | ||
1113 | continue; | ||
1114 | } | ||
1115 | printk("found device at bus %d idsel %d func %d (AgentId %x)\n", | ||
1116 | bus, id_sel, function, agent_id); | ||
1117 | ret = HvCallPci_getBusUnitInfo(bus, sub_bus, agent_id, | ||
1118 | iseries_hv_addr(&bridge_info), | ||
1119 | sizeof(struct HvCallPci_BridgeInfo)); | ||
1120 | if (ret != 0) | ||
1121 | continue; | ||
1122 | printk("bridge info: type %x subbus %x " | ||
1123 | "maxAgents %x maxsubbus %x logslot %x\n", | ||
1124 | bridge_info.busUnitInfo.deviceType, | ||
1125 | bridge_info.subBusNumber, | ||
1126 | bridge_info.maxAgents, | ||
1127 | bridge_info.maxSubBusNumber, | ||
1128 | bridge_info.logicalSlotNumber); | ||
1129 | if (bridge_info.busUnitInfo.deviceType == | ||
1130 | HvCallPci_BridgeDevice) | ||
1131 | scan_bridge_slot(dt, bus, &bridge_info); | ||
1132 | else | ||
1133 | printk("PCI: Invalid Bridge Configuration(0x%02X)", | ||
1134 | bridge_info.busUnitInfo.deviceType); | ||
1135 | } | ||
1136 | } | ||
1137 | |||
1138 | static void scan_phb(struct iseries_flat_dt *dt, HvBusNumber bus) | ||
1139 | { | ||
1140 | struct HvCallPci_DeviceInfo dev_info; | ||
1141 | const HvSubBusNumber sub_bus = 0; /* EADs is always 0. */ | ||
1142 | int err; | ||
1143 | int id_sel; | ||
1144 | const int max_agents = 8; | ||
1145 | |||
1146 | /* | ||
1147 | * Probe for EADs Bridges | ||
1148 | */ | ||
1149 | for (id_sel = 1; id_sel < max_agents; ++id_sel) { | ||
1150 | err = HvCallPci_getDeviceInfo(bus, sub_bus, id_sel, | ||
1151 | iseries_hv_addr(&dev_info), | ||
1152 | sizeof(struct HvCallPci_DeviceInfo)); | ||
1153 | if (err) { | ||
1154 | if (err != 0x302) | ||
1155 | printk(KERN_DEBUG "getDeviceInfo(%x, %x, %x) " | ||
1156 | "== %x\n", | ||
1157 | bus, sub_bus, id_sel, err); | ||
1158 | continue; | ||
1159 | } | ||
1160 | if (dev_info.deviceType != HvCallPci_NodeDevice) { | ||
1161 | printk(KERN_DEBUG "PCI: Invalid System Configuration" | ||
1162 | "(0x%02X) for bus 0x%02x id 0x%02x.\n", | ||
1163 | dev_info.deviceType, bus, id_sel); | ||
1164 | continue; | ||
1165 | } | ||
1166 | scan_bridge(dt, bus, sub_bus, id_sel); | ||
1167 | } | ||
1168 | } | ||
1169 | |||
1170 | static void dt_pci_devices(struct iseries_flat_dt *dt) | ||
1171 | { | ||
1172 | HvBusNumber bus; | ||
1173 | char buf[32]; | ||
1174 | u32 buses[2]; | ||
1175 | int phb_num = 0; | ||
1176 | |||
1177 | /* Check all possible buses. */ | ||
1178 | for (bus = 0; bus < 256; bus++) { | ||
1179 | int err = HvCallXm_testBus(bus); | ||
1180 | |||
1181 | if (err) { | ||
1182 | /* | ||
1183 | * Check for Unexpected Return code, a clue that | ||
1184 | * something has gone wrong. | ||
1185 | */ | ||
1186 | if (err != 0x0301) | ||
1187 | printk(KERN_ERR "Unexpected Return on Probe" | ||
1188 | "(0x%02X): 0x%04X", bus, err); | ||
1189 | continue; | ||
1190 | } | ||
1191 | printk("bus %d appears to exist\n", bus); | ||
1192 | snprintf(buf, 32, "pci@%d", phb_num); | ||
1193 | dt_start_node(dt, buf); | ||
1194 | dt_prop_str(dt, "device_type", "pci"); | ||
1195 | dt_prop_str(dt, "compatible", "IBM,iSeries-Logical-PHB"); | ||
1196 | dt_prop_u32(dt, "#address-cells", 3); | ||
1197 | dt_prop_u32(dt, "#size-cells", 2); | ||
1198 | buses[0] = buses[1] = bus; | ||
1199 | dt_prop_u32_list(dt, "bus-range", buses, 2); | ||
1200 | scan_phb(dt, bus); | ||
1201 | dt_end_node(dt); | ||
1202 | phb_num++; | ||
1203 | } | ||
1204 | } | ||
1205 | |||
1003 | void build_flat_dt(struct iseries_flat_dt *dt, unsigned long phys_mem_size) | 1206 | void build_flat_dt(struct iseries_flat_dt *dt, unsigned long phys_mem_size) |
1004 | { | 1207 | { |
1005 | u64 tmp[2]; | 1208 | u64 tmp[2]; |
@@ -1029,6 +1232,7 @@ void build_flat_dt(struct iseries_flat_dt *dt, unsigned long phys_mem_size) | |||
1029 | dt_cpus(dt); | 1232 | dt_cpus(dt); |
1030 | 1233 | ||
1031 | dt_vdevices(dt); | 1234 | dt_vdevices(dt); |
1235 | dt_pci_devices(dt); | ||
1032 | 1236 | ||
1033 | dt_end_node(dt); | 1237 | dt_end_node(dt); |
1034 | 1238 | ||