diff options
Diffstat (limited to 'arch/powerpc/platforms/iseries/pci.c')
-rw-r--r-- | arch/powerpc/platforms/iseries/pci.c | 273 |
1 files changed, 77 insertions, 196 deletions
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 | */ |