Merge branch 'pm-opp'
[linux-2.6-microblaze.git] / arch / s390 / pci / pci_clp.c
index d333159..51dc221 100644 (file)
@@ -212,17 +212,22 @@ out:
        return rc;
 }
 
-static int clp_refresh_fh(u32 fid);
-/*
- * Enable/Disable a given PCI function and update its function handle if
- * necessary
+/**
+ * clp_set_pci_fn() - Execute a command on a PCI function
+ * @zdev: Function that will be affected
+ * @fh: Out parameter for updated function handle
+ * @nr_dma_as: DMA address space number
+ * @command: The command code to execute
+ *
+ * Returns: 0 on success, < 0 for Linux errors (e.g. -ENOMEM), and
+ * > 0 for non-success platform responses
  */
-static int clp_set_pci_fn(struct zpci_dev *zdev, u8 nr_dma_as, u8 command)
+static int clp_set_pci_fn(struct zpci_dev *zdev, u32 *fh, u8 nr_dma_as, u8 command)
 {
        struct clp_req_rsp_set_pci *rrb;
        int rc, retries = 100;
-       u32 fid = zdev->fid;
 
+       *fh = 0;
        rrb = clp_alloc_block(GFP_KERNEL);
        if (!rrb)
                return -ENOMEM;
@@ -245,17 +250,13 @@ static int clp_set_pci_fn(struct zpci_dev *zdev, u8 nr_dma_as, u8 command)
                }
        } while (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY);
 
-       if (rc || rrb->response.hdr.rsp != CLP_RC_OK) {
+       if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) {
+               *fh = rrb->response.fh;
+       } else {
                zpci_err("Set PCI FN:\n");
                zpci_err_clp(rrb->response.hdr.rsp, rc);
-       }
-
-       if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) {
-               zdev->fh = rrb->response.fh;
-       } else if (!rc && rrb->response.hdr.rsp == CLP_RC_SETPCIFN_ALRDY &&
-                       rrb->response.fh == 0) {
-               /* Function is already in desired state - update handle */
-               rc = clp_refresh_fh(fid);
+               if (!rc)
+                       rc = rrb->response.hdr.rsp;
        }
        clp_free_block(rrb);
        return rc;
@@ -295,35 +296,62 @@ int clp_setup_writeback_mio(void)
        return rc;
 }
 
-int clp_enable_fh(struct zpci_dev *zdev, u8 nr_dma_as)
+int clp_enable_fh(struct zpci_dev *zdev, u32 *fh, u8 nr_dma_as)
 {
        int rc;
 
-       rc = clp_set_pci_fn(zdev, nr_dma_as, CLP_SET_ENABLE_PCI_FN);
-       zpci_dbg(3, "ena fid:%x, fh:%x, rc:%d\n", zdev->fid, zdev->fh, rc);
-       if (rc)
-               goto out;
-
-       if (zpci_use_mio(zdev)) {
-               rc = clp_set_pci_fn(zdev, nr_dma_as, CLP_SET_ENABLE_MIO);
+       rc = clp_set_pci_fn(zdev, fh, nr_dma_as, CLP_SET_ENABLE_PCI_FN);
+       zpci_dbg(3, "ena fid:%x, fh:%x, rc:%d\n", zdev->fid, *fh, rc);
+       if (!rc && zpci_use_mio(zdev)) {
+               rc = clp_set_pci_fn(zdev, fh, nr_dma_as, CLP_SET_ENABLE_MIO);
                zpci_dbg(3, "ena mio fid:%x, fh:%x, rc:%d\n",
-                               zdev->fid, zdev->fh, rc);
+                               zdev->fid, *fh, rc);
                if (rc)
-                       clp_disable_fh(zdev);
+                       clp_disable_fh(zdev, fh);
        }
-out:
        return rc;
 }
 
-int clp_disable_fh(struct zpci_dev *zdev)
+int clp_disable_fh(struct zpci_dev *zdev, u32 *fh)
 {
        int rc;
 
        if (!zdev_enabled(zdev))
                return 0;
 
-       rc = clp_set_pci_fn(zdev, 0, CLP_SET_DISABLE_PCI_FN);
-       zpci_dbg(3, "dis fid:%x, fh:%x, rc:%d\n", zdev->fid, zdev->fh, rc);
+       rc = clp_set_pci_fn(zdev, fh, 0, CLP_SET_DISABLE_PCI_FN);
+       zpci_dbg(3, "dis fid:%x, fh:%x, rc:%d\n", zdev->fid, *fh, rc);
+       return rc;
+}
+
+static int clp_list_pci_req(struct clp_req_rsp_list_pci *rrb,
+                           u64 *resume_token, int *nentries)
+{
+       int rc;
+
+       memset(rrb, 0, sizeof(*rrb));
+       rrb->request.hdr.len = sizeof(rrb->request);
+       rrb->request.hdr.cmd = CLP_LIST_PCI;
+       /* store as many entries as possible */
+       rrb->response.hdr.len = CLP_BLK_SIZE - LIST_PCI_HDR_LEN;
+       rrb->request.resume_token = *resume_token;
+
+       /* Get PCI function handle list */
+       rc = clp_req(rrb, CLP_LPS_PCI);
+       if (rc || rrb->response.hdr.rsp != CLP_RC_OK) {
+               zpci_err("List PCI FN:\n");
+               zpci_err_clp(rrb->response.hdr.rsp, rc);
+               return -EIO;
+       }
+
+       update_uid_checking(rrb->response.uid_checking);
+       WARN_ON_ONCE(rrb->response.entry_size !=
+               sizeof(struct clp_fh_list_entry));
+
+       *nentries = (rrb->response.hdr.len - LIST_PCI_HDR_LEN) /
+               rrb->response.entry_size;
+       *resume_token = rrb->response.resume_token;
+
        return rc;
 }
 
@@ -331,38 +359,40 @@ static int clp_list_pci(struct clp_req_rsp_list_pci *rrb, void *data,
                        void (*cb)(struct clp_fh_list_entry *, void *))
 {
        u64 resume_token = 0;
-       int entries, i, rc;
+       int nentries, i, rc;
 
        do {
-               memset(rrb, 0, sizeof(*rrb));
-               rrb->request.hdr.len = sizeof(rrb->request);
-               rrb->request.hdr.cmd = CLP_LIST_PCI;
-               /* store as many entries as possible */
-               rrb->response.hdr.len = CLP_BLK_SIZE - LIST_PCI_HDR_LEN;
-               rrb->request.resume_token = resume_token;
-
-               /* Get PCI function handle list */
-               rc = clp_req(rrb, CLP_LPS_PCI);
-               if (rc || rrb->response.hdr.rsp != CLP_RC_OK) {
-                       zpci_err("List PCI FN:\n");
-                       zpci_err_clp(rrb->response.hdr.rsp, rc);
-                       rc = -EIO;
-                       goto out;
-               }
+               rc = clp_list_pci_req(rrb, &resume_token, &nentries);
+               if (rc)
+                       return rc;
+               for (i = 0; i < nentries; i++)
+                       cb(&rrb->response.fh_list[i], data);
+       } while (resume_token);
 
-               update_uid_checking(rrb->response.uid_checking);
-               WARN_ON_ONCE(rrb->response.entry_size !=
-                       sizeof(struct clp_fh_list_entry));
+       return rc;
+}
 
-               entries = (rrb->response.hdr.len - LIST_PCI_HDR_LEN) /
-                       rrb->response.entry_size;
+static int clp_find_pci(struct clp_req_rsp_list_pci *rrb, u32 fid,
+                       struct clp_fh_list_entry *entry)
+{
+       struct clp_fh_list_entry *fh_list;
+       u64 resume_token = 0;
+       int nentries, i, rc;
 
-               resume_token = rrb->response.resume_token;
-               for (i = 0; i < entries; i++)
-                       cb(&rrb->response.fh_list[i], data);
+       do {
+               rc = clp_list_pci_req(rrb, &resume_token, &nentries);
+               if (rc)
+                       return rc;
+               for (i = 0; i < nentries; i++) {
+                       fh_list = rrb->response.fh_list;
+                       if (fh_list[i].fid == fid) {
+                               *entry = fh_list[i];
+                               return 0;
+                       }
+               }
        } while (resume_token);
-out:
-       return rc;
+
+       return -ENODEV;
 }
 
 static void __clp_add(struct clp_fh_list_entry *entry, void *data)
@@ -392,67 +422,41 @@ int clp_scan_pci_devices(void)
        return rc;
 }
 
-static void __clp_refresh_fh(struct clp_fh_list_entry *entry, void *data)
-{
-       struct zpci_dev *zdev;
-       u32 fid = *((u32 *)data);
-
-       if (!entry->vendor_id || fid != entry->fid)
-               return;
-
-       zdev = get_zdev_by_fid(fid);
-       if (!zdev)
-               return;
-
-       zdev->fh = entry->fh;
-}
-
 /*
- * Refresh the function handle of the function matching @fid
+ * Get the current function handle of the function matching @fid
  */
-static int clp_refresh_fh(u32 fid)
+int clp_refresh_fh(u32 fid, u32 *fh)
 {
        struct clp_req_rsp_list_pci *rrb;
+       struct clp_fh_list_entry entry;
        int rc;
 
        rrb = clp_alloc_block(GFP_NOWAIT);
        if (!rrb)
                return -ENOMEM;
 
-       rc = clp_list_pci(rrb, &fid, __clp_refresh_fh);
+       rc = clp_find_pci(rrb, fid, &entry);
+       if (!rc)
+               *fh = entry.fh;
 
        clp_free_block(rrb);
        return rc;
 }
 
-struct clp_state_data {
-       u32 fid;
-       enum zpci_state state;
-};
-
-static void __clp_get_state(struct clp_fh_list_entry *entry, void *data)
-{
-       struct clp_state_data *sd = data;
-
-       if (entry->fid != sd->fid)
-               return;
-
-       sd->state = entry->config_state;
-}
-
 int clp_get_state(u32 fid, enum zpci_state *state)
 {
        struct clp_req_rsp_list_pci *rrb;
-       struct clp_state_data sd = {fid, ZPCI_FN_STATE_RESERVED};
+       struct clp_fh_list_entry entry;
        int rc;
 
+       *state = ZPCI_FN_STATE_RESERVED;
        rrb = clp_alloc_block(GFP_ATOMIC);
        if (!rrb)
                return -ENOMEM;
 
-       rc = clp_list_pci(rrb, &sd, __clp_get_state);
+       rc = clp_find_pci(rrb, fid, &entry);
        if (!rc)
-               *state = sd.state;
+               *state = entry.config_state;
 
        clp_free_block(rrb);
        return rc;