Merge tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck...
[linux-drm-fsl-dcu.git] / arch / s390 / pci / pci_event.c
index 278e671ec9ac22b977c0598aaa31552d3e32b567..800f064b0da7c9a32c3910ecefdfb3e6979d7f9b 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <asm/pci_debug.h>
+#include <asm/sclp.h>
 
 /* Content Code Description for PCI Function Error */
 struct zpci_ccdf_err {
@@ -42,10 +43,27 @@ struct zpci_ccdf_avail {
        u16 pec;                        /* PCI event code */
 } __packed;
 
-static void zpci_event_log_avail(struct zpci_ccdf_avail *ccdf)
+void zpci_event_error(void *data)
 {
+       struct zpci_ccdf_err *ccdf = data;
+       struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
+
+       zpci_err("error CCDF:\n");
+       zpci_err_hex(ccdf, sizeof(*ccdf));
+
+       if (!zdev)
+               return;
+
+       pr_err("%s: Event 0x%x reports an error for PCI function 0x%x\n",
+              pci_name(zdev->pdev), ccdf->pec, ccdf->fid);
+}
+
+void zpci_event_availability(void *data)
+{
+       struct zpci_ccdf_avail *ccdf = data;
        struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
        struct pci_dev *pdev = zdev ? zdev->pdev : NULL;
+       int ret;
 
        pr_info("%s: Event 0x%x reconfigured PCI function 0x%x\n",
                pdev ? pci_name(pdev) : "n/a", ccdf->pec, ccdf->fid);
@@ -53,36 +71,47 @@ static void zpci_event_log_avail(struct zpci_ccdf_avail *ccdf)
        zpci_err_hex(ccdf, sizeof(*ccdf));
 
        switch (ccdf->pec) {
-       case 0x0301:
-               zpci_enable_device(zdev);
+       case 0x0301: /* Standby -> Configured */
+               if (!zdev || zdev->state == ZPCI_FN_STATE_CONFIGURED)
+                       break;
+               zdev->state = ZPCI_FN_STATE_CONFIGURED;
+               ret = zpci_enable_device(zdev);
+               if (ret)
+                       break;
+               pci_rescan_bus(zdev->bus);
                break;
-       case 0x0302:
+       case 0x0302: /* Reserved -> Standby */
                clp_add_pci_device(ccdf->fid, ccdf->fh, 0);
                break;
-       case 0x0306:
+       case 0x0303: /* Deconfiguration requested */
+               if (pdev)
+                       pci_stop_and_remove_bus_device(pdev);
+
+               ret = zpci_disable_device(zdev);
+               if (ret)
+                       break;
+
+               ret = sclp_pci_deconfigure(zdev->fid);
+               zpci_dbg(3, "deconf fid:%x, rc:%d\n", zdev->fid, ret);
+               if (!ret)
+                       zdev->state = ZPCI_FN_STATE_STANDBY;
+
+               break;
+       case 0x0304: /* Configured -> Standby */
+               if (pdev)
+                       pci_stop_and_remove_bus_device(pdev);
+
+               zpci_disable_device(zdev);
+               zdev->state = ZPCI_FN_STATE_STANDBY;
+               break;
+       case 0x0306: /* 0x308 or 0x302 for multiple devices */
                clp_rescan_pci_devices();
                break;
+       case 0x0308: /* Standby -> Reserved */
+               pci_stop_root_bus(zdev->bus);
+               pci_remove_root_bus(zdev->bus);
+               break;
        default:
                break;
        }
 }
-
-void zpci_event_error(void *data)
-{
-       struct zpci_ccdf_err *ccdf = data;
-       struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
-
-       zpci_err("error CCDF:\n");
-       zpci_err_hex(ccdf, sizeof(*ccdf));
-
-       if (!zdev)
-               return;
-
-       pr_err("%s: Event 0x%x reports an error for PCI function 0x%x\n",
-              pci_name(zdev->pdev), ccdf->pec, ccdf->fid);
-}
-
-void zpci_event_availability(void *data)
-{
-       zpci_event_log_avail(data);
-}