]> 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 0f1b63714718fc492ca5489debd346672ed461b5..f5677684429e3cc0c84b52cf3d3918bd2c32dca9 100644 (file)
@@ -542,17 +542,16 @@ EXPORT_SYMBOL_GPL(eeh_dev_check_failure);
 
 /**
  * eeh_check_failure - Check if all 1's data is due to EEH slot freeze
- * @token: I/O token, should be address in the form 0xA....
- * @val: value, should be all 1's (XXX why do we need this arg??)
+ * @token: I/O address
  *
- * Check for an EEH failure at the given token address.  Call this
+ * Check for an EEH failure at the given I/O address. Call this
  * routine if the result of a read was all 0xff's and you want to
- * find out if this is due to an EEH slot freeze event.  This routine
+ * find out if this is due to an EEH slot freeze event. This routine
  * will query firmware for the EEH status.
  *
  * Note this routine is safe to call in an interrupt context.
  */
-unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val)
+int eeh_check_failure(const volatile void __iomem *token)
 {
        unsigned long addr;
        struct eeh_dev *edev;
@@ -562,13 +561,11 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
        edev = eeh_addr_cache_get_dev(addr);
        if (!edev) {
                eeh_stats.no_device++;
-               return val;
+               return 0;
        }
 
-       eeh_dev_check_failure(edev);
-       return val;
+       return eeh_dev_check_failure(edev);
 }
-
 EXPORT_SYMBOL(eeh_check_failure);
 
 
@@ -1153,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);
 
@@ -1165,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);
@@ -1172,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);
 
@@ -1345,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
@@ -1371,20 +1449,17 @@ 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:
+               /*
+                * Proactively freeze the PE to drop all MMIO access
+                * during reset, which should be banned as it's always
+                * cause recursive EEH error.
+                */
+               eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE);
+
                ret = eeh_ops->reset(pe, option);
                break;
        default:
@@ -1413,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);