powerpc/eeh: I/O chip EEH state retrieval
authorGavin Shan <shangw@linux.vnet.ibm.com>
Thu, 20 Jun 2013 05:21:09 +0000 (13:21 +0800)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Thu, 20 Jun 2013 07:06:30 +0000 (17:06 +1000)
The patch adds I/O chip backend to retrieve the state for the
indicated PE. While the PE state is temperarily unavailable,
the upper layer (powernv platform) should return default delay
(1 second).

Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/platforms/powernv/eeh-ioda.c

index 744eb9e..a76870b 100644 (file)
@@ -116,10 +116,107 @@ static int ioda_eeh_set_option(struct eeh_pe *pe, int option)
        return ret;
 }
 
+/**
+ * ioda_eeh_get_state - Retrieve the state of PE
+ * @pe: EEH PE
+ *
+ * The PE's state should be retrieved from the PEEV, PEST
+ * IODA tables. Since the OPAL has exported the function
+ * to do it, it'd better to use that.
+ */
+static int ioda_eeh_get_state(struct eeh_pe *pe)
+{
+       s64 ret = 0;
+       u8 fstate;
+       u16 pcierr;
+       u32 pe_no;
+       int result;
+       struct pci_controller *hose = pe->phb;
+       struct pnv_phb *phb = hose->private_data;
+
+       /*
+        * Sanity check on PE address. The PHB PE address should
+        * be zero.
+        */
+       if (pe->addr < 0 || pe->addr >= phb->ioda.total_pe) {
+               pr_err("%s: PE address %x out of range [0, %x] "
+                      "on PHB#%x\n",
+                      __func__, pe->addr, phb->ioda.total_pe,
+                      hose->global_number);
+               return EEH_STATE_NOT_SUPPORT;
+       }
+
+       /* Retrieve PE status through OPAL */
+       pe_no = pe->addr;
+       ret = opal_pci_eeh_freeze_status(phb->opal_id, pe_no,
+                       &fstate, &pcierr, NULL);
+       if (ret) {
+               pr_err("%s: Failed to get EEH status on "
+                      "PHB#%x-PE#%x\n, err=%lld\n",
+                      __func__, hose->global_number, pe_no, ret);
+               return EEH_STATE_NOT_SUPPORT;
+       }
+
+       /* Check PHB status */
+       if (pe->type & EEH_PE_PHB) {
+               result = 0;
+               result &= ~EEH_STATE_RESET_ACTIVE;
+
+               if (pcierr != OPAL_EEH_PHB_ERROR) {
+                       result |= EEH_STATE_MMIO_ACTIVE;
+                       result |= EEH_STATE_DMA_ACTIVE;
+                       result |= EEH_STATE_MMIO_ENABLED;
+                       result |= EEH_STATE_DMA_ENABLED;
+               }
+
+               return result;
+       }
+
+       /* Parse result out */
+       result = 0;
+       switch (fstate) {
+       case OPAL_EEH_STOPPED_NOT_FROZEN:
+               result &= ~EEH_STATE_RESET_ACTIVE;
+               result |= EEH_STATE_MMIO_ACTIVE;
+               result |= EEH_STATE_DMA_ACTIVE;
+               result |= EEH_STATE_MMIO_ENABLED;
+               result |= EEH_STATE_DMA_ENABLED;
+               break;
+       case OPAL_EEH_STOPPED_MMIO_FREEZE:
+               result &= ~EEH_STATE_RESET_ACTIVE;
+               result |= EEH_STATE_DMA_ACTIVE;
+               result |= EEH_STATE_DMA_ENABLED;
+               break;
+       case OPAL_EEH_STOPPED_DMA_FREEZE:
+               result &= ~EEH_STATE_RESET_ACTIVE;
+               result |= EEH_STATE_MMIO_ACTIVE;
+               result |= EEH_STATE_MMIO_ENABLED;
+               break;
+       case OPAL_EEH_STOPPED_MMIO_DMA_FREEZE:
+               result &= ~EEH_STATE_RESET_ACTIVE;
+               break;
+       case OPAL_EEH_STOPPED_RESET:
+               result |= EEH_STATE_RESET_ACTIVE;
+               break;
+       case OPAL_EEH_STOPPED_TEMP_UNAVAIL:
+               result |= EEH_STATE_UNAVAILABLE;
+               break;
+       case OPAL_EEH_STOPPED_PERM_UNAVAIL:
+               result |= EEH_STATE_NOT_SUPPORT;
+               break;
+       default:
+               pr_warning("%s: Unexpected EEH status 0x%x "
+                          "on PHB#%x-PE#%x\n",
+                          __func__, fstate, hose->global_number, pe_no);
+       }
+
+       return result;
+}
+
 struct pnv_eeh_ops ioda_eeh_ops = {
        .post_init              = ioda_eeh_post_init,
        .set_option             = ioda_eeh_set_option,
-       .get_state              = NULL,
+       .get_state              = ioda_eeh_get_state,
        .reset                  = NULL,
        .get_log                = NULL,
        .configure_bridge       = NULL,