diff options
Diffstat (limited to 'arch/powerpc/platforms/powernv/pci-ioda.c')
-rw-r--r-- | arch/powerpc/platforms/powernv/pci-ioda.c | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 899fe4049b44..b136108ddc99 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c | |||
@@ -347,6 +347,146 @@ static void __init pnv_ioda_parse_m64_window(struct pnv_phb *phb) | |||
347 | phb->pick_m64_pe = pnv_ioda2_pick_m64_pe; | 347 | phb->pick_m64_pe = pnv_ioda2_pick_m64_pe; |
348 | } | 348 | } |
349 | 349 | ||
350 | static void pnv_ioda_freeze_pe(struct pnv_phb *phb, int pe_no) | ||
351 | { | ||
352 | struct pnv_ioda_pe *pe = &phb->ioda.pe_array[pe_no]; | ||
353 | struct pnv_ioda_pe *slave; | ||
354 | s64 rc; | ||
355 | |||
356 | /* Fetch master PE */ | ||
357 | if (pe->flags & PNV_IODA_PE_SLAVE) { | ||
358 | pe = pe->master; | ||
359 | WARN_ON(!pe || !(pe->flags & PNV_IODA_PE_MASTER)); | ||
360 | pe_no = pe->pe_number; | ||
361 | } | ||
362 | |||
363 | /* Freeze master PE */ | ||
364 | rc = opal_pci_eeh_freeze_set(phb->opal_id, | ||
365 | pe_no, | ||
366 | OPAL_EEH_ACTION_SET_FREEZE_ALL); | ||
367 | if (rc != OPAL_SUCCESS) { | ||
368 | pr_warn("%s: Failure %lld freezing PHB#%x-PE#%x\n", | ||
369 | __func__, rc, phb->hose->global_number, pe_no); | ||
370 | return; | ||
371 | } | ||
372 | |||
373 | /* Freeze slave PEs */ | ||
374 | if (!(pe->flags & PNV_IODA_PE_MASTER)) | ||
375 | return; | ||
376 | |||
377 | list_for_each_entry(slave, &pe->slaves, list) { | ||
378 | rc = opal_pci_eeh_freeze_set(phb->opal_id, | ||
379 | slave->pe_number, | ||
380 | OPAL_EEH_ACTION_SET_FREEZE_ALL); | ||
381 | if (rc != OPAL_SUCCESS) | ||
382 | pr_warn("%s: Failure %lld freezing PHB#%x-PE#%x\n", | ||
383 | __func__, rc, phb->hose->global_number, | ||
384 | slave->pe_number); | ||
385 | } | ||
386 | } | ||
387 | |||
388 | int pnv_ioda_unfreeze_pe(struct pnv_phb *phb, int pe_no, int opt) | ||
389 | { | ||
390 | struct pnv_ioda_pe *pe, *slave; | ||
391 | s64 rc; | ||
392 | |||
393 | /* Find master PE */ | ||
394 | pe = &phb->ioda.pe_array[pe_no]; | ||
395 | if (pe->flags & PNV_IODA_PE_SLAVE) { | ||
396 | pe = pe->master; | ||
397 | WARN_ON(!pe || !(pe->flags & PNV_IODA_PE_MASTER)); | ||
398 | pe_no = pe->pe_number; | ||
399 | } | ||
400 | |||
401 | /* Clear frozen state for master PE */ | ||
402 | rc = opal_pci_eeh_freeze_clear(phb->opal_id, pe_no, opt); | ||
403 | if (rc != OPAL_SUCCESS) { | ||
404 | pr_warn("%s: Failure %lld clear %d on PHB#%x-PE#%x\n", | ||
405 | __func__, rc, opt, phb->hose->global_number, pe_no); | ||
406 | return -EIO; | ||
407 | } | ||
408 | |||
409 | if (!(pe->flags & PNV_IODA_PE_MASTER)) | ||
410 | return 0; | ||
411 | |||
412 | /* Clear frozen state for slave PEs */ | ||
413 | list_for_each_entry(slave, &pe->slaves, list) { | ||
414 | rc = opal_pci_eeh_freeze_clear(phb->opal_id, | ||
415 | slave->pe_number, | ||
416 | opt); | ||
417 | if (rc != OPAL_SUCCESS) { | ||
418 | pr_warn("%s: Failure %lld clear %d on PHB#%x-PE#%x\n", | ||
419 | __func__, rc, opt, phb->hose->global_number, | ||
420 | slave->pe_number); | ||
421 | return -EIO; | ||
422 | } | ||
423 | } | ||
424 | |||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | static int pnv_ioda_get_pe_state(struct pnv_phb *phb, int pe_no) | ||
429 | { | ||
430 | struct pnv_ioda_pe *slave, *pe; | ||
431 | u8 fstate, state; | ||
432 | __be16 pcierr; | ||
433 | s64 rc; | ||
434 | |||
435 | /* Sanity check on PE number */ | ||
436 | if (pe_no < 0 || pe_no >= phb->ioda.total_pe) | ||
437 | return OPAL_EEH_STOPPED_PERM_UNAVAIL; | ||
438 | |||
439 | /* | ||
440 | * Fetch the master PE and the PE instance might be | ||
441 | * not initialized yet. | ||
442 | */ | ||
443 | pe = &phb->ioda.pe_array[pe_no]; | ||
444 | if (pe->flags & PNV_IODA_PE_SLAVE) { | ||
445 | pe = pe->master; | ||
446 | WARN_ON(!pe || !(pe->flags & PNV_IODA_PE_MASTER)); | ||
447 | pe_no = pe->pe_number; | ||
448 | } | ||
449 | |||
450 | /* Check the master PE */ | ||
451 | rc = opal_pci_eeh_freeze_status(phb->opal_id, pe_no, | ||
452 | &state, &pcierr, NULL); | ||
453 | if (rc != OPAL_SUCCESS) { | ||
454 | pr_warn("%s: Failure %lld getting " | ||
455 | "PHB#%x-PE#%x state\n", | ||
456 | __func__, rc, | ||
457 | phb->hose->global_number, pe_no); | ||
458 | return OPAL_EEH_STOPPED_TEMP_UNAVAIL; | ||
459 | } | ||
460 | |||
461 | /* Check the slave PE */ | ||
462 | if (!(pe->flags & PNV_IODA_PE_MASTER)) | ||
463 | return state; | ||
464 | |||
465 | list_for_each_entry(slave, &pe->slaves, list) { | ||
466 | rc = opal_pci_eeh_freeze_status(phb->opal_id, | ||
467 | slave->pe_number, | ||
468 | &fstate, | ||
469 | &pcierr, | ||
470 | NULL); | ||
471 | if (rc != OPAL_SUCCESS) { | ||
472 | pr_warn("%s: Failure %lld getting " | ||
473 | "PHB#%x-PE#%x state\n", | ||
474 | __func__, rc, | ||
475 | phb->hose->global_number, slave->pe_number); | ||
476 | return OPAL_EEH_STOPPED_TEMP_UNAVAIL; | ||
477 | } | ||
478 | |||
479 | /* | ||
480 | * Override the result based on the ascending | ||
481 | * priority. | ||
482 | */ | ||
483 | if (fstate > state) | ||
484 | state = fstate; | ||
485 | } | ||
486 | |||
487 | return state; | ||
488 | } | ||
489 | |||
350 | /* Currently those 2 are only used when MSIs are enabled, this will change | 490 | /* Currently those 2 are only used when MSIs are enabled, this will change |
351 | * but in the meantime, we need to protect them to avoid warnings | 491 | * but in the meantime, we need to protect them to avoid warnings |
352 | */ | 492 | */ |
@@ -1629,6 +1769,9 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np, | |||
1629 | 1769 | ||
1630 | 1770 | ||
1631 | phb->hose->ops = &pnv_pci_ops; | 1771 | phb->hose->ops = &pnv_pci_ops; |
1772 | phb->get_pe_state = pnv_ioda_get_pe_state; | ||
1773 | phb->freeze_pe = pnv_ioda_freeze_pe; | ||
1774 | phb->unfreeze_pe = pnv_ioda_unfreeze_pe; | ||
1632 | #ifdef CONFIG_EEH | 1775 | #ifdef CONFIG_EEH |
1633 | phb->eeh_ops = &ioda_eeh_ops; | 1776 | phb->eeh_ops = &ioda_eeh_ops; |
1634 | #endif | 1777 | #endif |