diff options
Diffstat (limited to 'arch/powerpc/platforms/powernv/pci.c')
-rw-r--r-- | arch/powerpc/platforms/powernv/pci.c | 139 |
1 files changed, 103 insertions, 36 deletions
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 277343cc6a3d..a28d3b5e6393 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/irq.h> | 20 | #include <linux/irq.h> |
21 | #include <linux/io.h> | 21 | #include <linux/io.h> |
22 | #include <linux/msi.h> | 22 | #include <linux/msi.h> |
23 | #include <linux/iommu.h> | ||
23 | 24 | ||
24 | #include <asm/sections.h> | 25 | #include <asm/sections.h> |
25 | #include <asm/io.h> | 26 | #include <asm/io.h> |
@@ -32,6 +33,8 @@ | |||
32 | #include <asm/iommu.h> | 33 | #include <asm/iommu.h> |
33 | #include <asm/tce.h> | 34 | #include <asm/tce.h> |
34 | #include <asm/firmware.h> | 35 | #include <asm/firmware.h> |
36 | #include <asm/eeh_event.h> | ||
37 | #include <asm/eeh.h> | ||
35 | 38 | ||
36 | #include "powernv.h" | 39 | #include "powernv.h" |
37 | #include "pci.h" | 40 | #include "pci.h" |
@@ -202,7 +205,8 @@ static void pnv_pci_handle_eeh_config(struct pnv_phb *phb, u32 pe_no) | |||
202 | 205 | ||
203 | spin_lock_irqsave(&phb->lock, flags); | 206 | spin_lock_irqsave(&phb->lock, flags); |
204 | 207 | ||
205 | rc = opal_pci_get_phb_diag_data(phb->opal_id, phb->diag.blob, PNV_PCI_DIAG_BUF_SIZE); | 208 | rc = opal_pci_get_phb_diag_data2(phb->opal_id, phb->diag.blob, |
209 | PNV_PCI_DIAG_BUF_SIZE); | ||
206 | has_diag = (rc == OPAL_SUCCESS); | 210 | has_diag = (rc == OPAL_SUCCESS); |
207 | 211 | ||
208 | rc = opal_pci_eeh_freeze_clear(phb->opal_id, pe_no, | 212 | rc = opal_pci_eeh_freeze_clear(phb->opal_id, pe_no, |
@@ -227,43 +231,50 @@ static void pnv_pci_handle_eeh_config(struct pnv_phb *phb, u32 pe_no) | |||
227 | spin_unlock_irqrestore(&phb->lock, flags); | 231 | spin_unlock_irqrestore(&phb->lock, flags); |
228 | } | 232 | } |
229 | 233 | ||
230 | static void pnv_pci_config_check_eeh(struct pnv_phb *phb, struct pci_bus *bus, | 234 | static void pnv_pci_config_check_eeh(struct pnv_phb *phb, |
231 | u32 bdfn) | 235 | struct device_node *dn) |
232 | { | 236 | { |
233 | s64 rc; | 237 | s64 rc; |
234 | u8 fstate; | 238 | u8 fstate; |
235 | u16 pcierr; | 239 | u16 pcierr; |
236 | u32 pe_no; | 240 | u32 pe_no; |
237 | 241 | ||
238 | /* Get PE# if we support IODA */ | 242 | /* |
239 | pe_no = phb->bdfn_to_pe ? phb->bdfn_to_pe(phb, bus, bdfn & 0xff) : 0; | 243 | * Get the PE#. During the PCI probe stage, we might not |
244 | * setup that yet. So all ER errors should be mapped to | ||
245 | * PE#0 | ||
246 | */ | ||
247 | pe_no = PCI_DN(dn)->pe_number; | ||
248 | if (pe_no == IODA_INVALID_PE) | ||
249 | pe_no = 0; | ||
240 | 250 | ||
241 | /* Read freeze status */ | 251 | /* Read freeze status */ |
242 | rc = opal_pci_eeh_freeze_status(phb->opal_id, pe_no, &fstate, &pcierr, | 252 | rc = opal_pci_eeh_freeze_status(phb->opal_id, pe_no, &fstate, &pcierr, |
243 | NULL); | 253 | NULL); |
244 | if (rc) { | 254 | if (rc) { |
245 | pr_warning("PCI %d: Failed to read EEH status for PE#%d," | 255 | pr_warning("%s: Can't read EEH status (PE#%d) for " |
246 | " err %lld\n", phb->hose->global_number, pe_no, rc); | 256 | "%s, err %lld\n", |
257 | __func__, pe_no, dn->full_name, rc); | ||
247 | return; | 258 | return; |
248 | } | 259 | } |
249 | cfg_dbg(" -> EEH check, bdfn=%04x PE%d fstate=%x\n", | 260 | cfg_dbg(" -> EEH check, bdfn=%04x PE#%d fstate=%x\n", |
250 | bdfn, pe_no, fstate); | 261 | (PCI_DN(dn)->busno << 8) | (PCI_DN(dn)->devfn), |
262 | pe_no, fstate); | ||
251 | if (fstate != 0) | 263 | if (fstate != 0) |
252 | pnv_pci_handle_eeh_config(phb, pe_no); | 264 | pnv_pci_handle_eeh_config(phb, pe_no); |
253 | } | 265 | } |
254 | 266 | ||
255 | static int pnv_pci_read_config(struct pci_bus *bus, | 267 | int pnv_pci_cfg_read(struct device_node *dn, |
256 | unsigned int devfn, | 268 | int where, int size, u32 *val) |
257 | int where, int size, u32 *val) | ||
258 | { | 269 | { |
259 | struct pci_controller *hose = pci_bus_to_host(bus); | 270 | struct pci_dn *pdn = PCI_DN(dn); |
260 | struct pnv_phb *phb = hose->private_data; | 271 | struct pnv_phb *phb = pdn->phb->private_data; |
261 | u32 bdfn = (((uint64_t)bus->number) << 8) | devfn; | 272 | u32 bdfn = (pdn->busno << 8) | pdn->devfn; |
273 | #ifdef CONFIG_EEH | ||
274 | struct eeh_pe *phb_pe = NULL; | ||
275 | #endif | ||
262 | s64 rc; | 276 | s64 rc; |
263 | 277 | ||
264 | if (hose == NULL) | ||
265 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
266 | |||
267 | switch (size) { | 278 | switch (size) { |
268 | case 1: { | 279 | case 1: { |
269 | u8 v8; | 280 | u8 v8; |
@@ -287,28 +298,43 @@ static int pnv_pci_read_config(struct pci_bus *bus, | |||
287 | default: | 298 | default: |
288 | return PCIBIOS_FUNC_NOT_SUPPORTED; | 299 | return PCIBIOS_FUNC_NOT_SUPPORTED; |
289 | } | 300 | } |
290 | cfg_dbg("pnv_pci_read_config bus: %x devfn: %x +%x/%x -> %08x\n", | 301 | cfg_dbg("%s: bus: %x devfn: %x +%x/%x -> %08x\n", |
291 | bus->number, devfn, where, size, *val); | 302 | __func__, pdn->busno, pdn->devfn, where, size, *val); |
292 | 303 | ||
293 | /* Check if the PHB got frozen due to an error (no response) */ | 304 | /* |
294 | pnv_pci_config_check_eeh(phb, bus, bdfn); | 305 | * Check if the specified PE has been put into frozen |
306 | * state. On the other hand, we needn't do that while | ||
307 | * the PHB has been put into frozen state because of | ||
308 | * PHB-fatal errors. | ||
309 | */ | ||
310 | #ifdef CONFIG_EEH | ||
311 | phb_pe = eeh_phb_pe_get(pdn->phb); | ||
312 | if (phb_pe && (phb_pe->state & EEH_PE_ISOLATED)) | ||
313 | return PCIBIOS_SUCCESSFUL; | ||
314 | |||
315 | if (phb->eeh_state & PNV_EEH_STATE_ENABLED) { | ||
316 | if (*val == EEH_IO_ERROR_VALUE(size) && | ||
317 | eeh_dev_check_failure(of_node_to_eeh_dev(dn))) | ||
318 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
319 | } else { | ||
320 | pnv_pci_config_check_eeh(phb, dn); | ||
321 | } | ||
322 | #else | ||
323 | pnv_pci_config_check_eeh(phb, dn); | ||
324 | #endif | ||
295 | 325 | ||
296 | return PCIBIOS_SUCCESSFUL; | 326 | return PCIBIOS_SUCCESSFUL; |
297 | } | 327 | } |
298 | 328 | ||
299 | static int pnv_pci_write_config(struct pci_bus *bus, | 329 | int pnv_pci_cfg_write(struct device_node *dn, |
300 | unsigned int devfn, | 330 | int where, int size, u32 val) |
301 | int where, int size, u32 val) | ||
302 | { | 331 | { |
303 | struct pci_controller *hose = pci_bus_to_host(bus); | 332 | struct pci_dn *pdn = PCI_DN(dn); |
304 | struct pnv_phb *phb = hose->private_data; | 333 | struct pnv_phb *phb = pdn->phb->private_data; |
305 | u32 bdfn = (((uint64_t)bus->number) << 8) | devfn; | 334 | u32 bdfn = (pdn->busno << 8) | pdn->devfn; |
306 | |||
307 | if (hose == NULL) | ||
308 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
309 | 335 | ||
310 | cfg_dbg("pnv_pci_write_config bus: %x devfn: %x +%x/%x -> %08x\n", | 336 | cfg_dbg("%s: bus: %x devfn: %x +%x/%x -> %08x\n", |
311 | bus->number, devfn, where, size, val); | 337 | pdn->busno, pdn->devfn, where, size, val); |
312 | switch (size) { | 338 | switch (size) { |
313 | case 1: | 339 | case 1: |
314 | opal_pci_config_write_byte(phb->opal_id, bdfn, where, val); | 340 | opal_pci_config_write_byte(phb->opal_id, bdfn, where, val); |
@@ -322,14 +348,54 @@ static int pnv_pci_write_config(struct pci_bus *bus, | |||
322 | default: | 348 | default: |
323 | return PCIBIOS_FUNC_NOT_SUPPORTED; | 349 | return PCIBIOS_FUNC_NOT_SUPPORTED; |
324 | } | 350 | } |
351 | |||
325 | /* Check if the PHB got frozen due to an error (no response) */ | 352 | /* Check if the PHB got frozen due to an error (no response) */ |
326 | pnv_pci_config_check_eeh(phb, bus, bdfn); | 353 | #ifdef CONFIG_EEH |
354 | if (!(phb->eeh_state & PNV_EEH_STATE_ENABLED)) | ||
355 | pnv_pci_config_check_eeh(phb, dn); | ||
356 | #else | ||
357 | pnv_pci_config_check_eeh(phb, dn); | ||
358 | #endif | ||
327 | 359 | ||
328 | return PCIBIOS_SUCCESSFUL; | 360 | return PCIBIOS_SUCCESSFUL; |
329 | } | 361 | } |
330 | 362 | ||
363 | static int pnv_pci_read_config(struct pci_bus *bus, | ||
364 | unsigned int devfn, | ||
365 | int where, int size, u32 *val) | ||
366 | { | ||
367 | struct device_node *dn, *busdn = pci_bus_to_OF_node(bus); | ||
368 | struct pci_dn *pdn; | ||
369 | |||
370 | for (dn = busdn->child; dn; dn = dn->sibling) { | ||
371 | pdn = PCI_DN(dn); | ||
372 | if (pdn && pdn->devfn == devfn) | ||
373 | return pnv_pci_cfg_read(dn, where, size, val); | ||
374 | } | ||
375 | |||
376 | *val = 0xFFFFFFFF; | ||
377 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
378 | |||
379 | } | ||
380 | |||
381 | static int pnv_pci_write_config(struct pci_bus *bus, | ||
382 | unsigned int devfn, | ||
383 | int where, int size, u32 val) | ||
384 | { | ||
385 | struct device_node *dn, *busdn = pci_bus_to_OF_node(bus); | ||
386 | struct pci_dn *pdn; | ||
387 | |||
388 | for (dn = busdn->child; dn; dn = dn->sibling) { | ||
389 | pdn = PCI_DN(dn); | ||
390 | if (pdn && pdn->devfn == devfn) | ||
391 | return pnv_pci_cfg_write(dn, where, size, val); | ||
392 | } | ||
393 | |||
394 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
395 | } | ||
396 | |||
331 | struct pci_ops pnv_pci_ops = { | 397 | struct pci_ops pnv_pci_ops = { |
332 | .read = pnv_pci_read_config, | 398 | .read = pnv_pci_read_config, |
333 | .write = pnv_pci_write_config, | 399 | .write = pnv_pci_write_config, |
334 | }; | 400 | }; |
335 | 401 | ||
@@ -412,6 +478,7 @@ static struct iommu_table *pnv_pci_setup_bml_iommu(struct pci_controller *hose) | |||
412 | pnv_pci_setup_iommu_table(tbl, __va(be64_to_cpup(basep)), | 478 | pnv_pci_setup_iommu_table(tbl, __va(be64_to_cpup(basep)), |
413 | be32_to_cpup(sizep), 0); | 479 | be32_to_cpup(sizep), 0); |
414 | iommu_init_table(tbl, hose->node); | 480 | iommu_init_table(tbl, hose->node); |
481 | iommu_register_group(tbl, pci_domain_nr(hose->bus), 0); | ||
415 | 482 | ||
416 | /* Deal with SW invalidated TCEs when needed (BML way) */ | 483 | /* Deal with SW invalidated TCEs when needed (BML way) */ |
417 | swinvp = of_get_property(hose->dn, "linux,tce-sw-invalidate-info", | 484 | swinvp = of_get_property(hose->dn, "linux,tce-sw-invalidate-info", |