Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[firefly-linux-kernel-4.4.55.git] / arch / powerpc / kernel / eeh.c
index 3b2252e7731b61ee47ac334ee4dce9c129727831..a4c62eb0ee48c8f5ca9c64f9c64dc780df6e1b3a 100644 (file)
@@ -164,30 +164,34 @@ __setup("eeh=", eeh_setup);
  */
 static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)
 {
-       struct device_node *dn = eeh_dev_to_of_node(edev);
+       struct pci_dn *pdn = eeh_dev_to_pdn(edev);
        u32 cfg;
        int cap, i;
        int n = 0, l = 0;
        char buffer[128];
 
-       n += scnprintf(buf+n, len-n, "%s\n", dn->full_name);
-       pr_warn("EEH: of node=%s\n", dn->full_name);
+       n += scnprintf(buf+n, len-n, "%04x:%02x:%02x:%01x\n",
+                      edev->phb->global_number, pdn->busno,
+                      PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn));
+       pr_warn("EEH: of node=%04x:%02x:%02x:%01x\n",
+               edev->phb->global_number, pdn->busno,
+               PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn));
 
-       eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg);
+       eeh_ops->read_config(pdn, PCI_VENDOR_ID, 4, &cfg);
        n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);
        pr_warn("EEH: PCI device/vendor: %08x\n", cfg);
 
-       eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg);
+       eeh_ops->read_config(pdn, PCI_COMMAND, 4, &cfg);
        n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
        pr_warn("EEH: PCI cmd/status register: %08x\n", cfg);
 
        /* Gather bridge-specific registers */
        if (edev->mode & EEH_DEV_BRIDGE) {
-               eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg);
+               eeh_ops->read_config(pdn, PCI_SEC_STATUS, 2, &cfg);
                n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
                pr_warn("EEH: Bridge secondary status: %04x\n", cfg);
 
-               eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg);
+               eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &cfg);
                n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
                pr_warn("EEH: Bridge control: %04x\n", cfg);
        }
@@ -195,11 +199,11 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)
        /* Dump out the PCI-X command and status regs */
        cap = edev->pcix_cap;
        if (cap) {
-               eeh_ops->read_config(dn, cap, 4, &cfg);
+               eeh_ops->read_config(pdn, cap, 4, &cfg);
                n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
                pr_warn("EEH: PCI-X cmd: %08x\n", cfg);
 
-               eeh_ops->read_config(dn, cap+4, 4, &cfg);
+               eeh_ops->read_config(pdn, cap+4, 4, &cfg);
                n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);
                pr_warn("EEH: PCI-X status: %08x\n", cfg);
        }
@@ -211,7 +215,7 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)
                pr_warn("EEH: PCI-E capabilities and status follow:\n");
 
                for (i=0; i<=8; i++) {
-                       eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
+                       eeh_ops->read_config(pdn, cap+4*i, 4, &cfg);
                        n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
 
                        if ((i % 4) == 0) {
@@ -238,7 +242,7 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)
                pr_warn("EEH: PCI-E AER capability register set follows:\n");
 
                for (i=0; i<=13; i++) {
-                       eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
+                       eeh_ops->read_config(pdn, cap+4*i, 4, &cfg);
                        n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
 
                        if ((i % 4) == 0) {
@@ -414,11 +418,11 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
        int ret;
        int active_flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
        unsigned long flags;
-       struct device_node *dn;
+       struct pci_dn *pdn;
        struct pci_dev *dev;
        struct eeh_pe *pe, *parent_pe, *phb_pe;
        int rc = 0;
-       const char *location;
+       const char *location = NULL;
 
        eeh_stats.total_mmio_ffs++;
 
@@ -429,15 +433,14 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
                eeh_stats.no_dn++;
                return 0;
        }
-       dn = eeh_dev_to_of_node(edev);
        dev = eeh_dev_to_pci_dev(edev);
        pe = eeh_dev_to_pe(edev);
 
        /* Access to IO BARs might get this far and still not want checking. */
        if (!pe) {
                eeh_stats.ignored_check++;
-               pr_debug("EEH: Ignored check for %s %s\n",
-                       eeh_pci_name(dev), dn->full_name);
+               pr_debug("EEH: Ignored check for %s\n",
+                       eeh_pci_name(dev));
                return 0;
        }
 
@@ -473,10 +476,13 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
        if (pe->state & EEH_PE_ISOLATED) {
                pe->check_count++;
                if (pe->check_count % EEH_MAX_FAILS == 0) {
-                       location = of_get_property(dn, "ibm,loc-code", NULL);
+                       pdn = eeh_dev_to_pdn(edev);
+                       if (pdn->node)
+                               location = of_get_property(pdn->node, "ibm,loc-code", NULL);
                        printk(KERN_ERR "EEH: %d reads ignored for recovering device at "
                                "location=%s driver=%s pci addr=%s\n",
-                               pe->check_count, location,
+                               pe->check_count,
+                               location ? location : "unknown",
                                eeh_driver_name(dev), eeh_pci_name(dev));
                        printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n",
                                eeh_driver_name(dev));
@@ -667,6 +673,55 @@ int eeh_pci_enable(struct eeh_pe *pe, int function)
        return rc;
 }
 
+static void *eeh_disable_and_save_dev_state(void *data, void *userdata)
+{
+       struct eeh_dev *edev = data;
+       struct pci_dev *pdev = eeh_dev_to_pci_dev(edev);
+       struct pci_dev *dev = userdata;
+
+       /*
+        * The caller should have disabled and saved the
+        * state for the specified device
+        */
+       if (!pdev || pdev == dev)
+               return NULL;
+
+       /* Ensure we have D0 power state */
+       pci_set_power_state(pdev, PCI_D0);
+
+       /* Save device state */
+       pci_save_state(pdev);
+
+       /*
+        * Disable device to avoid any DMA traffic and
+        * interrupt from the device
+        */
+       pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
+
+       return NULL;
+}
+
+static void *eeh_restore_dev_state(void *data, void *userdata)
+{
+       struct eeh_dev *edev = data;
+       struct pci_dn *pdn = eeh_dev_to_pdn(edev);
+       struct pci_dev *pdev = eeh_dev_to_pci_dev(edev);
+       struct pci_dev *dev = userdata;
+
+       if (!pdev)
+               return NULL;
+
+       /* Apply customization from firmware */
+       if (pdn && eeh_ops->restore_config)
+               eeh_ops->restore_config(pdn);
+
+       /* The caller should restore state for the specified device */
+       if (pdev != dev)
+               pci_save_state(pdev);
+
+       return NULL;
+}
+
 /**
  * pcibios_set_pcie_slot_reset - Set PCI-E reset state
  * @dev: pci device struct
@@ -689,13 +744,19 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat
        switch (state) {
        case pcie_deassert_reset:
                eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
+               eeh_unfreeze_pe(pe, false);
                eeh_pe_state_clear(pe, EEH_PE_CFG_BLOCKED);
+               eeh_pe_dev_traverse(pe, eeh_restore_dev_state, dev);
                break;
        case pcie_hot_reset:
+               eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE);
+               eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev);
                eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED);
                eeh_ops->reset(pe, EEH_RESET_HOT);
                break;
        case pcie_warm_reset:
+               eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE);
+               eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev);
                eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED);
                eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
                break;
@@ -815,15 +876,15 @@ out:
  */
 void eeh_save_bars(struct eeh_dev *edev)
 {
+       struct pci_dn *pdn;
        int i;
-       struct device_node *dn;
 
-       if (!edev)
+       pdn = eeh_dev_to_pdn(edev);
+       if (!pdn)
                return;
-       dn = eeh_dev_to_of_node(edev);
 
        for (i = 0; i < 16; i++)
-               eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]);
+               eeh_ops->read_config(pdn, i * 4, 4, &edev->config_space[i]);
 
        /*
         * For PCI bridges including root port, we need enable bus
@@ -914,7 +975,7 @@ static struct notifier_block eeh_reboot_nb = {
 int eeh_init(void)
 {
        struct pci_controller *hose, *tmp;
-       struct device_node *phb;
+       struct pci_dn *pdn;
        static int cnt = 0;
        int ret = 0;
 
@@ -949,20 +1010,9 @@ int eeh_init(void)
                return ret;
 
        /* Enable EEH for all adapters */
-       if (eeh_has_flag(EEH_PROBE_MODE_DEVTREE)) {
-               list_for_each_entry_safe(hose, tmp,
-                       &hose_list, list_node) {
-                       phb = hose->dn;
-                       traverse_pci_devices(phb, eeh_ops->of_probe, NULL);
-               }
-       } else if (eeh_has_flag(EEH_PROBE_MODE_DEV)) {
-               list_for_each_entry_safe(hose, tmp,
-                       &hose_list, list_node)
-                       pci_walk_bus(hose->bus, eeh_ops->dev_probe, NULL);
-       } else {
-               pr_warn("%s: Invalid probe mode %x",
-                       __func__, eeh_subsystem_flags);
-               return -EINVAL;
+       list_for_each_entry_safe(hose, tmp, &hose_list, list_node) {
+               pdn = hose->pci_data;
+               traverse_pci_dn(pdn, eeh_ops->probe, NULL);
        }
 
        /*
@@ -987,8 +1037,8 @@ int eeh_init(void)
 core_initcall_sync(eeh_init);
 
 /**
- * eeh_add_device_early - Enable EEH for the indicated device_node
- * @dn: device node for which to set up EEH
+ * eeh_add_device_early - Enable EEH for the indicated device node
+ * @pdn: PCI device node for which to set up EEH
  *
  * This routine must be used to perform EEH initialization for PCI
  * devices that were added after system boot (e.g. hotplug, dlpar).
@@ -998,44 +1048,41 @@ core_initcall_sync(eeh_init);
  * on the CEC architecture, type of the device, on earlier boot
  * command-line arguments & etc.
  */
-void eeh_add_device_early(struct device_node *dn)
+void eeh_add_device_early(struct pci_dn *pdn)
 {
        struct pci_controller *phb;
+       struct eeh_dev *edev = pdn_to_eeh_dev(pdn);
 
-       /*
-        * If we're doing EEH probe based on PCI device, we
-        * would delay the probe until late stage because
-        * the PCI device isn't available this moment.
-        */
-       if (!eeh_has_flag(EEH_PROBE_MODE_DEVTREE))
+       if (!edev || !eeh_enabled())
                return;
 
-       if (!of_node_to_eeh_dev(dn))
-               return;
-       phb = of_node_to_eeh_dev(dn)->phb;
-
        /* USB Bus children of PCI devices will not have BUID's */
-       if (NULL == phb || 0 == phb->buid)
+       phb = edev->phb;
+       if (NULL == phb ||
+           (eeh_has_flag(EEH_PROBE_MODE_DEVTREE) && 0 == phb->buid))
                return;
 
-       eeh_ops->of_probe(dn, NULL);
+       eeh_ops->probe(pdn, NULL);
 }
 
 /**
  * eeh_add_device_tree_early - Enable EEH for the indicated device
- * @dn: device node
+ * @pdn: PCI device node
  *
  * This routine must be used to perform EEH initialization for the
  * indicated PCI device that was added after system boot (e.g.
  * hotplug, dlpar).
  */
-void eeh_add_device_tree_early(struct device_node *dn)
+void eeh_add_device_tree_early(struct pci_dn *pdn)
 {
-       struct device_node *sib;
+       struct pci_dn *n;
 
-       for_each_child_of_node(dn, sib)
-               eeh_add_device_tree_early(sib);
-       eeh_add_device_early(dn);
+       if (!pdn)
+               return;
+
+       list_for_each_entry(n, &pdn->child_list, list)
+               eeh_add_device_tree_early(n);
+       eeh_add_device_early(pdn);
 }
 EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
 
@@ -1048,7 +1095,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
  */
 void eeh_add_device_late(struct pci_dev *dev)
 {
-       struct device_node *dn;
+       struct pci_dn *pdn;
        struct eeh_dev *edev;
 
        if (!dev || !eeh_enabled())
@@ -1056,8 +1103,8 @@ void eeh_add_device_late(struct pci_dev *dev)
 
        pr_debug("EEH: Adding device %s\n", pci_name(dev));
 
-       dn = pci_device_to_OF_node(dev);
-       edev = of_node_to_eeh_dev(dn);
+       pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn);
+       edev = pdn_to_eeh_dev(pdn);
        if (edev->pdev == dev) {
                pr_debug("EEH: Already referenced !\n");
                return;
@@ -1089,13 +1136,6 @@ void eeh_add_device_late(struct pci_dev *dev)
        edev->pdev = dev;
        dev->dev.archdata.edev = edev;
 
-       /*
-        * We have to do the EEH probe here because the PCI device
-        * hasn't been created yet in the early stage.
-        */
-       if (eeh_has_flag(EEH_PROBE_MODE_DEV))
-               eeh_ops->dev_probe(dev, NULL);
-
        eeh_addr_cache_insert_dev(dev);
 }