Merge tag 'pinctrl-v4.1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw...
[cascardo/linux.git] / arch / powerpc / platforms / powernv / eeh-powernv.c
index e51ac2d..ce738ab 100644 (file)
@@ -286,10 +286,82 @@ static int pnv_eeh_post_init(void)
        return ret;
 }
 
+static int pnv_eeh_cap_start(struct pci_dn *pdn)
+{
+       u32 status;
+
+       if (!pdn)
+               return 0;
+
+       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)
+                       break;
+
+               pos &= ~3;
+               pnv_pci_cfg_read(pdn, pos + PCI_CAP_LIST_ID, 1, &id);
+               if (id == 0xff)
+                       break;
+
+               /* Found */
+               if (id == cap)
+                       return pos;
+
+               /* Next one */
+               pos += PCI_CAP_LIST_NEXT;
+       }
+
+       return 0;
+}
+
+static int pnv_eeh_find_ecap(struct pci_dn *pdn, int cap)
+{
+       struct eeh_dev *edev = pdn_to_eeh_dev(pdn);
+       u32 header;
+       int pos = 256, ttl = (4096 - 256) / 8;
+
+       if (!edev || !edev->pcie_cap)
+               return 0;
+       if (pnv_pci_cfg_read(pdn, pos, 4, &header) != PCIBIOS_SUCCESSFUL)
+               return 0;
+       else if (!header)
+               return 0;
+
+       while (ttl-- > 0) {
+               if (PCI_EXT_CAP_ID(header) == cap && pos)
+                       return pos;
+
+               pos = PCI_EXT_CAP_NEXT(header);
+               if (pos < 256)
+                       break;
+
+               if (pnv_pci_cfg_read(pdn, pos, 4, &header) != PCIBIOS_SUCCESSFUL)
+                       break;
+       }
+
+       return 0;
+}
+
 /**
- * pnv_eeh_dev_probe - Do probe on PCI device
- * @dev: PCI device
- * @flag: unused
+ * pnv_eeh_probe - Do probe on PCI device
+ * @pdn: PCI device node
+ * @data: unused
  *
  * When EEH module is installed during system boot, all PCI devices
  * are checked one by one to see if it supports EEH. The function
@@ -303,12 +375,12 @@ static int pnv_eeh_post_init(void)
  * was possiblly triggered by EEH core, the binding between EEH device
  * and the PCI device isn't built yet.
  */
-static int pnv_eeh_dev_probe(struct pci_dev *dev, void *flag)
+static void *pnv_eeh_probe(struct pci_dn *pdn, void *data)
 {
-       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+       struct pci_controller *hose = pdn->phb;
        struct pnv_phb *phb = hose->private_data;
-       struct device_node *dn = pci_device_to_OF_node(dev);
-       struct eeh_dev *edev = of_node_to_eeh_dev(dn);
+       struct eeh_dev *edev = pdn_to_eeh_dev(pdn);
+       uint32_t pcie_flags;
        int ret;
 
        /*
@@ -317,40 +389,42 @@ static int pnv_eeh_dev_probe(struct pci_dev *dev, void *flag)
         * the root bridge. So it's not reasonable to continue
         * the probing.
         */
-       if (!dn || !edev || edev->pe)
-               return 0;
+       if (!edev || edev->pe)
+               return NULL;
 
        /* Skip for PCI-ISA bridge */
-       if ((dev->class >> 8) == PCI_CLASS_BRIDGE_ISA)
-               return 0;
+       if ((pdn->class_code >> 8) == PCI_CLASS_BRIDGE_ISA)
+               return NULL;
 
        /* Initialize eeh device */
-       edev->class_code = dev->class;
+       edev->class_code = pdn->class_code;
        edev->mode      &= 0xFFFFFF00;
-       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
+       edev->pcix_cap = pnv_eeh_find_cap(pdn, PCI_CAP_ID_PCIX);
+       edev->pcie_cap = pnv_eeh_find_cap(pdn, PCI_CAP_ID_EXP);
+       edev->aer_cap  = pnv_eeh_find_ecap(pdn, PCI_EXT_CAP_ID_ERR);
+       if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) {
                edev->mode |= EEH_DEV_BRIDGE;
-       edev->pcix_cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
-       if (pci_is_pcie(dev)) {
-               edev->pcie_cap = pci_pcie_cap(dev);
-
-               if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
-                       edev->mode |= EEH_DEV_ROOT_PORT;
-               else if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM)
-                       edev->mode |= EEH_DEV_DS_PORT;
-
-               edev->aer_cap = pci_find_ext_capability(dev,
-                                                       PCI_EXT_CAP_ID_ERR);
+               if (edev->pcie_cap) {
+                       pnv_pci_cfg_read(pdn, edev->pcie_cap + PCI_EXP_FLAGS,
+                                        2, &pcie_flags);
+                       pcie_flags = (pcie_flags & PCI_EXP_FLAGS_TYPE) >> 4;
+                       if (pcie_flags == PCI_EXP_TYPE_ROOT_PORT)
+                               edev->mode |= EEH_DEV_ROOT_PORT;
+                       else if (pcie_flags == PCI_EXP_TYPE_DOWNSTREAM)
+                               edev->mode |= EEH_DEV_DS_PORT;
+               }
        }
 
-       edev->config_addr       = ((dev->bus->number << 8) | dev->devfn);
-       edev->pe_config_addr    = phb->bdfn_to_pe(phb, dev->bus, dev->devfn & 0xff);
+       edev->config_addr    = (pdn->busno << 8) | (pdn->devfn);
+       edev->pe_config_addr = phb->ioda.pe_rmap[edev->config_addr];
 
        /* Create PE */
        ret = eeh_add_to_parent_pe(edev);
        if (ret) {
-               pr_warn("%s: Can't add PCI dev %s to parent PE (%d)\n",
-                       __func__, pci_name(dev), ret);
-               return ret;
+               pr_warn("%s: Can't add PCI dev %04x:%02x:%02x.%01x to parent PE (%d)\n",
+                       __func__, hose->global_number, pdn->busno,
+                       PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn), ret);
+               return NULL;
        }
 
        /*
@@ -369,8 +443,10 @@ static int pnv_eeh_dev_probe(struct pci_dev *dev, void *flag)
         * Broadcom Austin 4-ports NICs (14e4:1657)
         * Broadcom Shiner 2-ports 10G NICs (14e4:168e)
         */
-       if ((dev->vendor == PCI_VENDOR_ID_BROADCOM && dev->device == 0x1657) ||
-           (dev->vendor == PCI_VENDOR_ID_BROADCOM && dev->device == 0x168e))
+       if ((pdn->vendor_id == PCI_VENDOR_ID_BROADCOM &&
+            pdn->device_id == 0x1657) ||
+           (pdn->vendor_id == PCI_VENDOR_ID_BROADCOM &&
+            pdn->device_id == 0x168e))
                edev->pe->state |= EEH_PE_CFG_RESTRICTED;
 
        /*
@@ -380,7 +456,8 @@ static int pnv_eeh_dev_probe(struct pci_dev *dev, void *flag)
         * to PE reset.
         */
        if (!edev->pe->bus)
-               edev->pe->bus = dev->bus;
+               edev->pe->bus = pci_find_bus(hose->global_number,
+                                            pdn->busno);
 
        /*
         * Enable EEH explicitly so that we will do EEH check
@@ -391,7 +468,7 @@ static int pnv_eeh_dev_probe(struct pci_dev *dev, void *flag)
        /* Save memory bars */
        eeh_save_bars(edev);
 
-       return 0;
+       return NULL;
 }
 
 /**
@@ -665,21 +742,236 @@ static int pnv_eeh_get_state(struct eeh_pe *pe, int *delay)
        return ret;
 }
 
+static s64 pnv_eeh_phb_poll(struct pnv_phb *phb)
+{
+       s64 rc = OPAL_HARDWARE;
+
+       while (1) {
+               rc = opal_pci_poll(phb->opal_id);
+               if (rc <= 0)
+                       break;
+
+               if (system_state < SYSTEM_RUNNING)
+                       udelay(1000 * rc);
+               else
+                       msleep(rc);
+       }
+
+       return rc;
+}
+
+int pnv_eeh_phb_reset(struct pci_controller *hose, int option)
+{
+       struct pnv_phb *phb = hose->private_data;
+       s64 rc = OPAL_HARDWARE;
+
+       pr_debug("%s: Reset PHB#%x, option=%d\n",
+                __func__, hose->global_number, option);
+
+       /* Issue PHB complete reset request */
+       if (option == EEH_RESET_FUNDAMENTAL ||
+           option == EEH_RESET_HOT)
+               rc = opal_pci_reset(phb->opal_id,
+                                   OPAL_RESET_PHB_COMPLETE,
+                                   OPAL_ASSERT_RESET);
+       else if (option == EEH_RESET_DEACTIVATE)
+               rc = opal_pci_reset(phb->opal_id,
+                                   OPAL_RESET_PHB_COMPLETE,
+                                   OPAL_DEASSERT_RESET);
+       if (rc < 0)
+               goto out;
+
+       /*
+        * Poll state of the PHB until the request is done
+        * successfully. The PHB reset is usually PHB complete
+        * reset followed by hot reset on root bus. So we also
+        * need the PCI bus settlement delay.
+        */
+       rc = pnv_eeh_phb_poll(phb);
+       if (option == EEH_RESET_DEACTIVATE) {
+               if (system_state < SYSTEM_RUNNING)
+                       udelay(1000 * EEH_PE_RST_SETTLE_TIME);
+               else
+                       msleep(EEH_PE_RST_SETTLE_TIME);
+       }
+out:
+       if (rc != OPAL_SUCCESS)
+               return -EIO;
+
+       return 0;
+}
+
+static int pnv_eeh_root_reset(struct pci_controller *hose, int option)
+{
+       struct pnv_phb *phb = hose->private_data;
+       s64 rc = OPAL_HARDWARE;
+
+       pr_debug("%s: Reset PHB#%x, option=%d\n",
+                __func__, hose->global_number, option);
+
+       /*
+        * During the reset deassert time, we needn't care
+        * the reset scope because the firmware does nothing
+        * for fundamental or hot reset during deassert phase.
+        */
+       if (option == EEH_RESET_FUNDAMENTAL)
+               rc = opal_pci_reset(phb->opal_id,
+                                   OPAL_RESET_PCI_FUNDAMENTAL,
+                                   OPAL_ASSERT_RESET);
+       else if (option == EEH_RESET_HOT)
+               rc = opal_pci_reset(phb->opal_id,
+                                   OPAL_RESET_PCI_HOT,
+                                   OPAL_ASSERT_RESET);
+       else if (option == EEH_RESET_DEACTIVATE)
+               rc = opal_pci_reset(phb->opal_id,
+                                   OPAL_RESET_PCI_HOT,
+                                   OPAL_DEASSERT_RESET);
+       if (rc < 0)
+               goto out;
+
+       /* Poll state of the PHB until the request is done */
+       rc = pnv_eeh_phb_poll(phb);
+       if (option == EEH_RESET_DEACTIVATE)
+               msleep(EEH_PE_RST_SETTLE_TIME);
+out:
+       if (rc != OPAL_SUCCESS)
+               return -EIO;
+
+       return 0;
+}
+
+static int pnv_eeh_bridge_reset(struct pci_dev *dev, int option)
+{
+       struct pci_dn *pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn);
+       struct eeh_dev *edev = pdn_to_eeh_dev(pdn);
+       int aer = edev ? edev->aer_cap : 0;
+       u32 ctrl;
+
+       pr_debug("%s: Reset PCI bus %04x:%02x with option %d\n",
+                __func__, pci_domain_nr(dev->bus),
+                dev->bus->number, option);
+
+       switch (option) {
+       case EEH_RESET_FUNDAMENTAL:
+       case EEH_RESET_HOT:
+               /* Don't report linkDown event */
+               if (aer) {
+                       eeh_ops->read_config(pdn, aer + PCI_ERR_UNCOR_MASK,
+                                            4, &ctrl);
+                       ctrl |= PCI_ERR_UNC_SURPDN;
+                       eeh_ops->write_config(pdn, aer + PCI_ERR_UNCOR_MASK,
+                                             4, ctrl);
+               }
+
+               eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &ctrl);
+               ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
+               eeh_ops->write_config(pdn, PCI_BRIDGE_CONTROL, 2, ctrl);
+
+               msleep(EEH_PE_RST_HOLD_TIME);
+               break;
+       case EEH_RESET_DEACTIVATE:
+               eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &ctrl);
+               ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
+               eeh_ops->write_config(pdn, PCI_BRIDGE_CONTROL, 2, ctrl);
+
+               msleep(EEH_PE_RST_SETTLE_TIME);
+
+               /* Continue reporting linkDown event */
+               if (aer) {
+                       eeh_ops->read_config(pdn, aer + PCI_ERR_UNCOR_MASK,
+                                            4, &ctrl);
+                       ctrl &= ~PCI_ERR_UNC_SURPDN;
+                       eeh_ops->write_config(pdn, aer + PCI_ERR_UNCOR_MASK,
+                                             4, ctrl);
+               }
+
+               break;
+       }
+
+       return 0;
+}
+
+void pnv_pci_reset_secondary_bus(struct pci_dev *dev)
+{
+       struct pci_controller *hose;
+
+       if (pci_is_root_bus(dev->bus)) {
+               hose = pci_bus_to_host(dev->bus);
+               pnv_eeh_root_reset(hose, EEH_RESET_HOT);
+               pnv_eeh_root_reset(hose, EEH_RESET_DEACTIVATE);
+       } else {
+               pnv_eeh_bridge_reset(dev, EEH_RESET_HOT);
+               pnv_eeh_bridge_reset(dev, EEH_RESET_DEACTIVATE);
+       }
+}
+
 /**
  * pnv_eeh_reset - Reset the specified PE
  * @pe: EEH PE
  * @option: reset option
  *
- * Reset the specified PE
+ * Do reset on the indicated PE. For PCI bus sensitive PE,
+ * we need to reset the parent p2p bridge. The PHB has to
+ * be reinitialized if the p2p bridge is root bridge. For
+ * PCI device sensitive PE, we will try to reset the device
+ * through FLR. For now, we don't have OPAL APIs to do HARD
+ * reset yet, so all reset would be SOFT (HOT) reset.
  */
 static int pnv_eeh_reset(struct eeh_pe *pe, int option)
 {
        struct pci_controller *hose = pe->phb;
-       struct pnv_phb *phb = hose->private_data;
-       int ret = -EEXIST;
+       struct pci_bus *bus;
+       int ret;
+
+       /*
+        * For PHB reset, we always have complete reset. For those PEs whose
+        * primary bus derived from root complex (root bus) or root port
+        * (usually bus#1), we apply hot or fundamental reset on the root port.
+        * For other PEs, we always have hot reset on the PE primary bus.
+        *
+        * Here, we have different design to pHyp, which always clear the
+        * frozen state during PE reset. However, the good idea here from
+        * benh is to keep frozen state before we get PE reset done completely
+        * (until BAR restore). With the frozen state, HW drops illegal IO
+        * or MMIO access, which can incur recrusive frozen PE during PE
+        * reset. The side effect is that EEH core has to clear the frozen
+        * state explicitly after BAR restore.
+        */
+       if (pe->type & EEH_PE_PHB) {
+               ret = pnv_eeh_phb_reset(hose, option);
+       } else {
+               struct pnv_phb *phb;
+               s64 rc;
 
-       if (phb->eeh_ops && phb->eeh_ops->reset)
-               ret = phb->eeh_ops->reset(pe, option);
+               /*
+                * The frozen PE might be caused by PAPR error injection
+                * registers, which are expected to be cleared after hitting
+                * frozen PE as stated in the hardware spec. Unfortunately,
+                * that's not true on P7IOC. So we have to clear it manually
+                * to avoid recursive EEH errors during recovery.
+                */
+               phb = hose->private_data;
+               if (phb->model == PNV_PHB_MODEL_P7IOC &&
+                   (option == EEH_RESET_HOT ||
+                   option == EEH_RESET_FUNDAMENTAL)) {
+                       rc = opal_pci_reset(phb->opal_id,
+                                           OPAL_RESET_PHB_ERROR,
+                                           OPAL_ASSERT_RESET);
+                       if (rc != OPAL_SUCCESS) {
+                               pr_warn("%s: Failure %lld clearing "
+                                       "error injection registers\n",
+                                       __func__, rc);
+                               return -EIO;
+                       }
+               }
+
+               bus = eeh_pe_bus_get(pe);
+               if (pci_is_root_bus(bus) ||
+                       pci_is_root_bus(bus->parent))
+                       ret = pnv_eeh_root_reset(hose, option);
+               else
+                       ret = pnv_eeh_bridge_reset(bus->self, option);
+       }
 
        return ret;
 }
@@ -807,9 +1099,9 @@ static int pnv_eeh_err_inject(struct eeh_pe *pe, int type, int func,
        return 0;
 }
 
-static inline bool pnv_eeh_cfg_blocked(struct device_node *dn)
+static inline bool pnv_eeh_cfg_blocked(struct pci_dn *pdn)
 {
-       struct eeh_dev *edev = of_node_to_eeh_dev(dn);
+       struct eeh_dev *edev = pdn_to_eeh_dev(pdn);
 
        if (!edev || !edev->pe)
                return false;
@@ -820,24 +1112,30 @@ static inline bool pnv_eeh_cfg_blocked(struct device_node *dn)
        return false;
 }
 
-static int pnv_eeh_read_config(struct device_node *dn,
+static int pnv_eeh_read_config(struct pci_dn *pdn,
                               int where, int size, u32 *val)
 {
-       if (pnv_eeh_cfg_blocked(dn)) {
+       if (!pdn)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       if (pnv_eeh_cfg_blocked(pdn)) {
                *val = 0xFFFFFFFF;
                return PCIBIOS_SET_FAILED;
        }
 
-       return pnv_pci_cfg_read(dn, where, size, val);
+       return pnv_pci_cfg_read(pdn, where, size, val);
 }
 
-static int pnv_eeh_write_config(struct device_node *dn,
+static int pnv_eeh_write_config(struct pci_dn *pdn,
                                int where, int size, u32 val)
 {
-       if (pnv_eeh_cfg_blocked(dn))
+       if (!pdn)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       if (pnv_eeh_cfg_blocked(pdn))
                return PCIBIOS_SET_FAILED;
 
-       return pnv_pci_cfg_write(dn, where, size, val);
+       return pnv_pci_cfg_write(pdn, where, size, val);
 }
 
 static void pnv_eeh_dump_hub_diag_common(struct OpalIoP7IOCErrorData *data)
@@ -1182,9 +1480,9 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
        return ret;
 }
 
-static int pnv_eeh_restore_config(struct device_node *dn)
+static int pnv_eeh_restore_config(struct pci_dn *pdn)
 {
-       struct eeh_dev *edev = of_node_to_eeh_dev(dn);
+       struct eeh_dev *edev = pdn_to_eeh_dev(pdn);
        struct pnv_phb *phb;
        s64 ret;
 
@@ -1207,8 +1505,7 @@ static struct eeh_ops pnv_eeh_ops = {
        .name                   = "powernv",
        .init                   = pnv_eeh_init,
        .post_init              = pnv_eeh_post_init,
-       .of_probe               = NULL,
-       .dev_probe              = pnv_eeh_dev_probe,
+       .probe                  = pnv_eeh_probe,
        .set_option             = pnv_eeh_set_option,
        .get_pe_addr            = pnv_eeh_get_pe_addr,
        .get_state              = pnv_eeh_get_state,