summaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/powernv
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/powernv')
-rw-r--r--arch/powerpc/platforms/powernv/eeh-powernv.c86
-rw-r--r--arch/powerpc/platforms/powernv/opal-irqchip.c66
-rw-r--r--arch/powerpc/platforms/powernv/opal.c2
-rw-r--r--arch/powerpc/platforms/powernv/setup.c21
4 files changed, 88 insertions, 87 deletions
diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
index 3bb6acb76..e1c907255 100644
--- a/arch/powerpc/platforms/powernv/eeh-powernv.c
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
@@ -43,17 +43,11 @@
static bool pnv_eeh_nb_init = false;
static int eeh_event_irq = -EINVAL;
-/**
- * pnv_eeh_init - EEH platform dependent initialization
- *
- * EEH platform dependent initialization on powernv
- */
static int pnv_eeh_init(void)
{
struct pci_controller *hose;
struct pnv_phb *phb;
- /* We require OPALv3 */
if (!firmware_has_feature(FW_FEATURE_OPALv3)) {
pr_warn("%s: OPALv3 is required !\n",
__func__);
@@ -77,9 +71,9 @@ static int pnv_eeh_init(void)
/*
* PE#0 should be regarded as valid by EEH core
* if it's not the reserved one. Currently, we
- * have the reserved PE#0 and PE#127 for PHB3
+ * have the reserved PE#255 and PE#127 for PHB3
* and P7IOC separately. So we should regard
- * PE#0 as valid for P7IOC.
+ * PE#0 as valid for PHB3 and P7IOC.
*/
if (phb->ioda.reserved_pe != 0)
eeh_add_flag(EEH_VALID_PE_ZERO);
@@ -284,33 +278,23 @@ static int pnv_eeh_post_init(void)
#endif /* CONFIG_DEBUG_FS */
}
-
return ret;
}
-static int pnv_eeh_cap_start(struct pci_dn *pdn)
+static int pnv_eeh_find_cap(struct pci_dn *pdn, int cap)
{
- u32 status;
+ int pos = PCI_CAPABILITY_LIST;
+ int cnt = 48; /* Maximal number of capabilities */
+ u32 status, id;
if (!pdn)
return 0;
+ /* Check if the device supports capabilities */
pnv_pci_cfg_read(pdn, PCI_STATUS, 2, &status);
if (!(status & PCI_STATUS_CAP_LIST))
return 0;
- return PCI_CAPABILITY_LIST;
-}
-
-static int pnv_eeh_find_cap(struct pci_dn *pdn, int cap)
-{
- int pos = pnv_eeh_cap_start(pdn);
- int cnt = 48; /* Maximal number of capabilities */
- u32 id;
-
- if (!pos)
- return 0;
-
while (cnt--) {
pnv_pci_cfg_read(pdn, pos, 1, &pos);
if (pos < 0x40)
@@ -443,11 +427,14 @@ static void *pnv_eeh_probe(struct pci_dn *pdn, void *data)
* that PE to block its config space.
*
* Broadcom Austin 4-ports NICs (14e4:1657)
+ * Broadcom Shiner 4-ports 1G NICs (14e4:168a)
* Broadcom Shiner 2-ports 10G NICs (14e4:168e)
*/
if ((pdn->vendor_id == PCI_VENDOR_ID_BROADCOM &&
pdn->device_id == 0x1657) ||
(pdn->vendor_id == PCI_VENDOR_ID_BROADCOM &&
+ pdn->device_id == 0x168a) ||
+ (pdn->vendor_id == PCI_VENDOR_ID_BROADCOM &&
pdn->device_id == 0x168e))
edev->pe->state |= EEH_PE_CFG_RESTRICTED;
@@ -487,10 +474,9 @@ static int pnv_eeh_set_option(struct eeh_pe *pe, int option)
struct pci_controller *hose = pe->phb;
struct pnv_phb *phb = hose->private_data;
bool freeze_pe = false;
- int opt, ret = 0;
+ int opt;
s64 rc;
- /* Sanity check on option */
switch (option) {
case EEH_OPT_DISABLE:
return -EPERM;
@@ -511,38 +497,37 @@ static int pnv_eeh_set_option(struct eeh_pe *pe, int option)
return -EINVAL;
}
- /* If PHB supports compound PE, to handle it */
+ /* Freeze master and slave PEs if PHB supports compound PEs */
if (freeze_pe) {
if (phb->freeze_pe) {
phb->freeze_pe(phb, pe->addr);
- } else {
- rc = opal_pci_eeh_freeze_set(phb->opal_id,
- pe->addr, opt);
- if (rc != OPAL_SUCCESS) {
- pr_warn("%s: Failure %lld freezing "
- "PHB#%x-PE#%x\n",
- __func__, rc,
- phb->hose->global_number, pe->addr);
- ret = -EIO;
- }
+ return 0;
}
- } else {
- if (phb->unfreeze_pe) {
- ret = phb->unfreeze_pe(phb, pe->addr, opt);
- } else {
- rc = opal_pci_eeh_freeze_clear(phb->opal_id,
- pe->addr, opt);
- if (rc != OPAL_SUCCESS) {
- pr_warn("%s: Failure %lld enable %d "
- "for PHB#%x-PE#%x\n",
- __func__, rc, option,
- phb->hose->global_number, pe->addr);
- ret = -EIO;
- }
+
+ rc = opal_pci_eeh_freeze_set(phb->opal_id, pe->addr, opt);
+ if (rc != OPAL_SUCCESS) {
+ pr_warn("%s: Failure %lld freezing PHB#%x-PE#%x\n",
+ __func__, rc, phb->hose->global_number,
+ pe->addr);
+ return -EIO;
}
+
+ return 0;
}
- return ret;
+ /* Unfreeze master and slave PEs if PHB supports */
+ if (phb->unfreeze_pe)
+ return phb->unfreeze_pe(phb, pe->addr, opt);
+
+ rc = opal_pci_eeh_freeze_clear(phb->opal_id, pe->addr, opt);
+ if (rc != OPAL_SUCCESS) {
+ pr_warn("%s: Failure %lld enable %d for PHB#%x-PE#%x\n",
+ __func__, rc, option, phb->hose->global_number,
+ pe->addr);
+ return -EIO;
+ }
+
+ return 0;
}
/**
@@ -1065,7 +1050,6 @@ static int pnv_eeh_err_inject(struct eeh_pe *pe, int type, int func,
struct pnv_phb *phb = hose->private_data;
s64 rc;
- /* Sanity check on error type */
if (type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR &&
type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64) {
pr_warn("%s: Invalid error type %d\n",
diff --git a/arch/powerpc/platforms/powernv/opal-irqchip.c b/arch/powerpc/platforms/powernv/opal-irqchip.c
index 2c91ee780..e505223b4 100644
--- a/arch/powerpc/platforms/powernv/opal-irqchip.c
+++ b/arch/powerpc/platforms/powernv/opal-irqchip.c
@@ -43,11 +43,34 @@ static unsigned int opal_irq_count;
static unsigned int *opal_irqs;
static void opal_handle_irq_work(struct irq_work *work);
-static __be64 last_outstanding_events;
+static u64 last_outstanding_events;
static struct irq_work opal_event_irq_work = {
.func = opal_handle_irq_work,
};
+void opal_handle_events(uint64_t events)
+{
+ int virq, hwirq = 0;
+ u64 mask = opal_event_irqchip.mask;
+
+ if (!in_irq() && (events & mask)) {
+ last_outstanding_events = events;
+ irq_work_queue(&opal_event_irq_work);
+ return;
+ }
+
+ while (events & mask) {
+ hwirq = fls64(events) - 1;
+ if (BIT_ULL(hwirq) & mask) {
+ virq = irq_find_mapping(opal_event_irqchip.domain,
+ hwirq);
+ if (virq)
+ generic_handle_irq(virq);
+ }
+ events &= ~BIT_ULL(hwirq);
+ }
+}
+
static void opal_event_mask(struct irq_data *d)
{
clear_bit(d->hwirq, &opal_event_irqchip.mask);
@@ -55,9 +78,21 @@ static void opal_event_mask(struct irq_data *d)
static void opal_event_unmask(struct irq_data *d)
{
+ __be64 events;
+
set_bit(d->hwirq, &opal_event_irqchip.mask);
- opal_poll_events(&last_outstanding_events);
+ opal_poll_events(&events);
+ last_outstanding_events = be64_to_cpu(events);
+
+ /*
+ * We can't just handle the events now with opal_handle_events().
+ * If we did we would deadlock when opal_event_unmask() is called from
+ * handle_level_irq() with the irq descriptor lock held, because
+ * calling opal_handle_events() would call generic_handle_irq() and
+ * then handle_level_irq() which would try to take the descriptor lock
+ * again. Instead queue the events for later.
+ */
if (last_outstanding_events & opal_event_irqchip.mask)
/* Need to retrigger the interrupt */
irq_work_queue(&opal_event_irq_work);
@@ -96,29 +131,6 @@ static int opal_event_map(struct irq_domain *d, unsigned int irq,
return 0;
}
-void opal_handle_events(uint64_t events)
-{
- int virq, hwirq = 0;
- u64 mask = opal_event_irqchip.mask;
-
- if (!in_irq() && (events & mask)) {
- last_outstanding_events = events;
- irq_work_queue(&opal_event_irq_work);
- return;
- }
-
- while (events & mask) {
- hwirq = fls64(events) - 1;
- if (BIT_ULL(hwirq) & mask) {
- virq = irq_find_mapping(opal_event_irqchip.domain,
- hwirq);
- if (virq)
- generic_handle_irq(virq);
- }
- events &= ~BIT_ULL(hwirq);
- }
-}
-
static irqreturn_t opal_interrupt(int irq, void *data)
{
__be64 events;
@@ -131,13 +143,13 @@ static irqreturn_t opal_interrupt(int irq, void *data)
static void opal_handle_irq_work(struct irq_work *work)
{
- opal_handle_events(be64_to_cpu(last_outstanding_events));
+ opal_handle_events(last_outstanding_events);
}
static int opal_event_match(struct irq_domain *h, struct device_node *node,
enum irq_domain_bus_token bus_token)
{
- return h->of_node == node;
+ return irq_domain_get_of_node(h) == node;
}
static int opal_event_xlate(struct irq_domain *h, struct device_node *np,
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 4296d55e8..57cffb80b 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -278,7 +278,7 @@ static void opal_handle_message(void)
/* Sanity check */
if (type >= OPAL_MSG_TYPE_MAX) {
- pr_warning("%s: Unknown message type: %u\n", __func__, type);
+ pr_warn_once("%s: Unknown message type: %u\n", __func__, type);
return;
}
opal_message_do_notify(type, (void *)&msg);
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 685b3cbe1..a9a8fa37a 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -187,7 +187,7 @@ static void pnv_kexec_wait_secondaries_down(void)
for_each_online_cpu(i) {
uint8_t status;
- int64_t rc;
+ int64_t rc, timeout = 1000;
if (i == my_cpu)
continue;
@@ -204,6 +204,18 @@ static void pnv_kexec_wait_secondaries_down(void)
i, paca[i].hw_cpu_id);
notified = i;
}
+
+ /*
+ * On crash secondaries might be unreachable or hung,
+ * so timeout if we've waited too long
+ * */
+ mdelay(1);
+ if (timeout-- == 0) {
+ printk(KERN_ERR "kexec: timed out waiting for "
+ "cpu %d (physical %d) to enter OPAL\n",
+ i, paca[i].hw_cpu_id);
+ break;
+ }
}
}
}
@@ -225,13 +237,6 @@ static void pnv_kexec_cpu_down(int crash_shutdown, int secondary)
/* Return the CPU to OPAL */
opal_return_cpu();
- } else if (crash_shutdown) {
- /*
- * On crash, we don't wait for secondaries to go
- * down as they might be unreachable or hung, so
- * instead we just wait a bit and move on.
- */
- mdelay(1);
} else {
/* Primary waits for the secondaries to have reached OPAL */
pnv_kexec_wait_secondaries_down();