]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - arch/powerpc/kernel/eeh.c
powerpc/eeh: Clear frozen state on passing device
[karo-tx-linux.git] / arch / powerpc / kernel / eeh.c
index 633f6bf965c3094d1af7b9f282339586f6de524f..f5677684429e3cc0c84b52cf3d3918bd2c32dca9 100644 (file)
@@ -1150,6 +1150,8 @@ void eeh_remove_device(struct pci_dev *dev)
 int eeh_dev_open(struct pci_dev *pdev)
 {
        struct eeh_dev *edev;
+       int flag = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
+       int ret = -ENODEV;
 
        mutex_lock(&eeh_dev_mutex);
 
@@ -1162,6 +1164,38 @@ int eeh_dev_open(struct pci_dev *pdev)
        if (!edev || !edev->pe)
                goto out;
 
+       /*
+        * The PE might have been put into frozen state, but we
+        * didn't detect that yet. The passed through PCI devices
+        * in frozen PE won't work properly. Clear the frozen state
+        * in advance.
+        */
+       ret = eeh_ops->get_state(edev->pe, NULL);
+       if (ret > 0 && ret != EEH_STATE_NOT_SUPPORT &&
+           (ret & flag) != flag) {
+               ret = eeh_ops->set_option(edev->pe, EEH_OPT_THAW_MMIO);
+               if (ret) {
+                       pr_warn("%s: Failure %d enabling MMIO "
+                               "for PHB#%x-PE#%x\n",
+                               __func__, ret, edev->phb->global_number,
+                               edev->pe->addr);
+                       goto out;
+               }
+
+               ret = eeh_ops->set_option(edev->pe, EEH_OPT_THAW_DMA);
+               if (ret) {
+                       pr_warn("%s: Failure %d enabling DMA "
+                               "for PHB#%x-PE#%x\n",
+                               __func__, ret, edev->phb->global_number,
+                               edev->pe->addr);
+                       goto out;
+               }
+       }
+
+       /* Clear software isolated state */
+       if (edev->pe->state & EEH_PE_ISOLATED)
+               eeh_pe_state_clear(edev->pe, EEH_PE_ISOLATED);
+
        /* Increase PE's pass through count */
        atomic_inc(&edev->pe->pass_dev_cnt);
        mutex_unlock(&eeh_dev_mutex);
@@ -1169,7 +1203,7 @@ int eeh_dev_open(struct pci_dev *pdev)
        return 0;
 out:
        mutex_unlock(&eeh_dev_mutex);
-       return -ENODEV;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(eeh_dev_open);
 
@@ -1342,6 +1376,53 @@ int eeh_pe_get_state(struct eeh_pe *pe)
 }
 EXPORT_SYMBOL_GPL(eeh_pe_get_state);
 
+static int eeh_pe_reenable_devices(struct eeh_pe *pe)
+{
+       struct eeh_dev *edev, *tmp;
+       struct pci_dev *pdev;
+       int ret = 0;
+
+       /* Restore config space */
+       eeh_pe_restore_bars(pe);
+
+       /*
+        * Reenable PCI devices as the devices passed
+        * through are always enabled before the reset.
+        */
+       eeh_pe_for_each_dev(pe, edev, tmp) {
+               pdev = eeh_dev_to_pci_dev(edev);
+               if (!pdev)
+                       continue;
+
+               ret = pci_reenable_device(pdev);
+               if (ret) {
+                       pr_warn("%s: Failure %d reenabling %s\n",
+                               __func__, ret, pci_name(pdev));
+                       return ret;
+               }
+       }
+
+       /* The PE is still in frozen state */
+       ret = eeh_ops->set_option(pe, EEH_OPT_THAW_MMIO);
+       if (ret) {
+               pr_warn("%s: Failure %d enabling MMIO for PHB#%x-PE#%x\n",
+                       __func__, ret, pe->phb->global_number, pe->addr);
+               return ret;
+       }
+
+       ret = eeh_ops->set_option(pe, EEH_OPT_THAW_DMA);
+       if (ret) {
+               pr_warn("%s: Failure %d enabling DMA for PHB#%x-PE#%x\n",
+                       __func__, ret, pe->phb->global_number, pe->addr);
+               return ret;
+       }
+
+       /* Clear software isolated state */
+       eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
+
+       return ret;
+}
+
 /**
  * eeh_pe_reset - Issue PE reset according to specified type
  * @pe: EEH PE
@@ -1368,17 +1449,7 @@ int eeh_pe_reset(struct eeh_pe *pe, int option)
                if (ret)
                        break;
 
-               /*
-                * The PE is still in frozen state and we need to clear
-                * that. It's good to clear frozen state after deassert
-                * to avoid messy IO access during reset, which might
-                * cause recursive frozen PE.
-                */
-               ret = eeh_ops->set_option(pe, EEH_OPT_THAW_MMIO);
-               if (!ret)
-                       ret = eeh_ops->set_option(pe, EEH_OPT_THAW_DMA);
-               if (!ret)
-                       eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
+               ret = eeh_pe_reenable_devices(pe);
                break;
        case EEH_RESET_HOT:
        case EEH_RESET_FUNDAMENTAL:
@@ -1417,9 +1488,6 @@ int eeh_pe_configure(struct eeh_pe *pe)
        if (!pe)
                return -ENODEV;
 
-       /* Restore config space for the affected devices */
-       eeh_pe_restore_bars(pe);
-
        return ret;
 }
 EXPORT_SYMBOL_GPL(eeh_pe_configure);