--- /dev/null
+#ifndef _ASM_S390_PCI_CLP_H
+#define _ASM_S390_PCI_CLP_H
+
+#include <asm/clp.h>
+
+/*
+ * Call Logical Processor - Command Codes
+ */
+#define CLP_LIST_PCI           0x0002
+#define CLP_QUERY_PCI_FN       0x0003
+#define CLP_QUERY_PCI_FNGRP    0x0004
+#define CLP_SET_PCI_FN         0x0005
+
+/* PCI function handle list entry */
+struct clp_fh_list_entry {
+       u16 device_id;
+       u16 vendor_id;
+       u32 config_state :  1;
+       u32              : 31;
+       u32 fid;                /* PCI function id */
+       u32 fh;                 /* PCI function handle */
+} __packed;
+
+#define CLP_RC_SETPCIFN_FH     0x0101  /* Invalid PCI fn handle */
+#define CLP_RC_SETPCIFN_FHOP   0x0102  /* Fn handle not valid for op */
+#define CLP_RC_SETPCIFN_DMAAS  0x0103  /* Invalid DMA addr space */
+#define CLP_RC_SETPCIFN_RES    0x0104  /* Insufficient resources */
+#define CLP_RC_SETPCIFN_ALRDY  0x0105  /* Fn already in requested state */
+#define CLP_RC_SETPCIFN_ERR    0x0106  /* Fn in permanent error state */
+#define CLP_RC_SETPCIFN_RECPND 0x0107  /* Error recovery pending */
+#define CLP_RC_SETPCIFN_BUSY   0x0108  /* Fn busy */
+#define CLP_RC_LISTPCI_BADRT   0x010a  /* Resume token not recognized */
+#define CLP_RC_QUERYPCIFG_PFGID        0x010b  /* Unrecognized PFGID */
+
+/* request or response block header length */
+#define LIST_PCI_HDR_LEN       32
+
+/* Number of function handles fitting in response block */
+#define CLP_FH_LIST_NR_ENTRIES                         \
+       ((CLP_BLK_SIZE - 2 * LIST_PCI_HDR_LEN)          \
+               / sizeof(struct clp_fh_list_entry))
+
+#define CLP_SET_ENABLE_PCI_FN  0       /* Yes, 0 enables it */
+#define CLP_SET_DISABLE_PCI_FN 1       /* Yes, 1 disables it */
+
+#define CLP_UTIL_STR_LEN       64
+
+/* List PCI functions request */
+struct clp_req_list_pci {
+       struct clp_req_hdr hdr;
+       u32 fmt                 :  4;   /* cmd request block format */
+       u32                     : 28;
+       u64 reserved1;
+       u64 resume_token;
+       u64 reserved2;
+} __packed;
+
+/* List PCI functions response */
+struct clp_rsp_list_pci {
+       struct clp_rsp_hdr hdr;
+       u32 fmt                 :  4;   /* cmd request block format */
+       u32                     : 28;
+       u64 reserved1;
+       u64 resume_token;
+       u32 reserved2;
+       u16 max_fn;
+       u8 reserved3;
+       u8 entry_size;
+       struct clp_fh_list_entry fh_list[CLP_FH_LIST_NR_ENTRIES];
+} __packed;
+
+/* Query PCI function request */
+struct clp_req_query_pci {
+       struct clp_req_hdr hdr;
+       u32 fmt                 :  4;   /* cmd request block format */
+       u32                     : 28;
+       u64 reserved1;
+       u32 fh;                         /* function handle */
+       u32 reserved2;
+       u64 reserved3;
+} __packed;
+
+/* Query PCI function response */
+struct clp_rsp_query_pci {
+       struct clp_rsp_hdr hdr;
+       u32 fmt                 :  4;   /* cmd request block format */
+       u32                     : 28;
+       u64 reserved1;
+       u16 vfn;                        /* virtual fn number */
+       u16                     :  7;
+       u16 util_str_avail      :  1;   /* utility string available? */
+       u16 pfgid               :  8;   /* pci function group id */
+       u32 fid;                        /* pci function id */
+       u8 bar_size[PCI_BAR_COUNT];
+       u16 pchid;
+       u32 bar[PCI_BAR_COUNT];
+       u64 reserved2;
+       u64 sdma;                       /* start dma as */
+       u64 edma;                       /* end dma as */
+       u64 reserved3[6];
+       u8 util_str[CLP_UTIL_STR_LEN];  /* utility string */
+} __packed;
+
+/* Query PCI function group request */
+struct clp_req_query_pci_grp {
+       struct clp_req_hdr hdr;
+       u32 fmt                 :  4;   /* cmd request block format */
+       u32                     : 28;
+       u64 reserved1;
+       u32                     : 24;
+       u32 pfgid               :  8;   /* function group id */
+       u32 reserved2;
+       u64 reserved3;
+} __packed;
+
+/* Query PCI function group response */
+struct clp_rsp_query_pci_grp {
+       struct clp_rsp_hdr hdr;
+       u32 fmt                 :  4;   /* cmd request block format */
+       u32                     : 28;
+       u64 reserved1;
+       u16                     :  4;
+       u16 noi                 : 12;   /* number of interrupts */
+       u8 version;
+       u8                      :  6;
+       u8 frame                :  1;
+       u8 refresh              :  1;   /* TLB refresh mode */
+       u16 reserved2;
+       u16 mui;
+       u64 reserved3;
+       u64 dasm;                       /* dma address space mask */
+       u64 msia;                       /* MSI address */
+       u64 reserved4;
+       u64 reserved5;
+} __packed;
+
+/* Set PCI function request */
+struct clp_req_set_pci {
+       struct clp_req_hdr hdr;
+       u32 fmt                 :  4;   /* cmd request block format */
+       u32                     : 28;
+       u64 reserved1;
+       u32 fh;                         /* function handle */
+       u16 reserved2;
+       u8 oc;                          /* operation controls */
+       u8 ndas;                        /* number of dma spaces */
+       u64 reserved3;
+} __packed;
+
+/* Set PCI function response */
+struct clp_rsp_set_pci {
+       struct clp_rsp_hdr hdr;
+       u32 fmt                 :  4;   /* cmd request block format */
+       u32                     : 28;
+       u64 reserved1;
+       u32 fh;                         /* function handle */
+       u32 reserved3;
+       u64 reserved4;
+} __packed;
+
+/* Combined request/response block structures used by clp insn */
+struct clp_req_rsp_list_pci {
+       struct clp_req_list_pci request;
+       struct clp_rsp_list_pci response;
+} __packed;
+
+struct clp_req_rsp_set_pci {
+       struct clp_req_set_pci request;
+       struct clp_rsp_set_pci response;
+} __packed;
+
+struct clp_req_rsp_query_pci {
+       struct clp_req_query_pci request;
+       struct clp_rsp_query_pci response;
+} __packed;
+
+struct clp_req_rsp_query_pci_grp {
+       struct clp_req_query_pci_grp request;
+       struct clp_rsp_query_pci_grp response;
+} __packed;
+
+#endif
 
--- /dev/null
+/*
+ * Copyright IBM Corp. 2012
+ *
+ * Author(s):
+ *   Jan Glauber <jang@linux.vnet.ibm.com>
+ */
+
+#define COMPONENT "zPCI"
+#define pr_fmt(fmt) COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <asm/pci_clp.h>
+
+/*
+ * Call Logical Processor
+ * Retry logic is handled by the caller.
+ */
+static inline u8 clp_instr(void *req)
+{
+       u64 ilpm;
+       u8 cc;
+
+       asm volatile (
+               "       .insn   rrf,0xb9a00000,%[ilpm],%[req],0x0,0x2\n"
+               "       ipm     %[cc]\n"
+               "       srl     %[cc],28\n"
+               : [cc] "=d" (cc), [ilpm] "=d" (ilpm)
+               : [req] "a" (req)
+               : "cc", "memory");
+       return cc;
+}
+
+static void *clp_alloc_block(void)
+{
+       struct page *page = alloc_pages(GFP_KERNEL, get_order(CLP_BLK_SIZE));
+       return (page) ? page_address(page) : NULL;
+}
+
+static void clp_free_block(void *ptr)
+{
+       free_pages((unsigned long) ptr, get_order(CLP_BLK_SIZE));
+}
+
+static void clp_store_query_pci_fngrp(struct zpci_dev *zdev,
+                                     struct clp_rsp_query_pci_grp *response)
+{
+       switch (response->version) {
+       case 1:
+               zdev->max_bus_speed = PCIE_SPEED_5_0GT;
+               break;
+       default:
+               zdev->max_bus_speed = PCI_SPEED_UNKNOWN;
+               break;
+       }
+}
+
+static int clp_query_pci_fngrp(struct zpci_dev *zdev, u8 pfgid)
+{
+       struct clp_req_rsp_query_pci_grp *rrb;
+       int rc;
+
+       rrb = clp_alloc_block();
+       if (!rrb)
+               return -ENOMEM;
+
+       memset(rrb, 0, sizeof(*rrb));
+       rrb->request.hdr.len = sizeof(rrb->request);
+       rrb->request.hdr.cmd = CLP_QUERY_PCI_FNGRP;
+       rrb->response.hdr.len = sizeof(rrb->response);
+       rrb->request.pfgid = pfgid;
+
+       rc = clp_instr(rrb);
+       if (!rc && rrb->response.hdr.rsp == CLP_RC_OK)
+               clp_store_query_pci_fngrp(zdev, &rrb->response);
+       else {
+               pr_err("Query PCI FNGRP failed with response: %x  cc: %d\n",
+                       rrb->response.hdr.rsp, rc);
+               rc = -EIO;
+       }
+       clp_free_block(rrb);
+       return rc;
+}
+
+static int clp_store_query_pci_fn(struct zpci_dev *zdev,
+                                 struct clp_rsp_query_pci *response)
+{
+       int i;
+
+       for (i = 0; i < PCI_BAR_COUNT; i++) {
+               zdev->bars[i].val = le32_to_cpu(response->bar[i]);
+               zdev->bars[i].size = response->bar_size[i];
+       }
+       zdev->pchid = response->pchid;
+       zdev->pfgid = response->pfgid;
+       return 0;
+}
+
+static int clp_query_pci_fn(struct zpci_dev *zdev, u32 fh)
+{
+       struct clp_req_rsp_query_pci *rrb;
+       int rc;
+
+       rrb = clp_alloc_block();
+       if (!rrb)
+               return -ENOMEM;
+
+       memset(rrb, 0, sizeof(*rrb));
+       rrb->request.hdr.len = sizeof(rrb->request);
+       rrb->request.hdr.cmd = CLP_QUERY_PCI_FN;
+       rrb->response.hdr.len = sizeof(rrb->response);
+       rrb->request.fh = fh;
+
+       rc = clp_instr(rrb);
+       if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) {
+               rc = clp_store_query_pci_fn(zdev, &rrb->response);
+               if (rc)
+                       goto out;
+               if (rrb->response.pfgid)
+                       rc = clp_query_pci_fngrp(zdev, rrb->response.pfgid);
+       } else {
+               pr_err("Query PCI failed with response: %x  cc: %d\n",
+                        rrb->response.hdr.rsp, rc);
+               rc = -EIO;
+       }
+out:
+       clp_free_block(rrb);
+       return rc;
+}
+
+int clp_add_pci_device(u32 fid, u32 fh, int configured)
+{
+       struct zpci_dev *zdev;
+       int rc;
+
+       zdev = zpci_alloc_device();
+       if (IS_ERR(zdev))
+               return PTR_ERR(zdev);
+
+       zdev->fh = fh;
+       zdev->fid = fid;
+
+       /* Query function properties and update zdev */
+       rc = clp_query_pci_fn(zdev, fh);
+       if (rc)
+               goto error;
+
+       if (configured)
+               zdev->state = ZPCI_FN_STATE_CONFIGURED;
+       else
+               zdev->state = ZPCI_FN_STATE_STANDBY;
+
+       rc = zpci_create_device(zdev);
+       if (rc)
+               goto error;
+       return 0;
+
+error:
+       zpci_free_device(zdev);
+       return rc;
+}
+
+/*
+ * Enable/Disable a given PCI function defined by its function handle.
+ */
+static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command)
+{
+       struct clp_req_rsp_set_pci *rrb;
+       int rc, retries = 1000;
+
+       rrb = clp_alloc_block();
+       if (!rrb)
+               return -ENOMEM;
+
+       do {
+               memset(rrb, 0, sizeof(*rrb));
+               rrb->request.hdr.len = sizeof(rrb->request);
+               rrb->request.hdr.cmd = CLP_SET_PCI_FN;
+               rrb->response.hdr.len = sizeof(rrb->response);
+               rrb->request.fh = *fh;
+               rrb->request.oc = command;
+               rrb->request.ndas = nr_dma_as;
+
+               rc = clp_instr(rrb);
+               if (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY) {
+                       retries--;
+                       if (retries < 0)
+                               break;
+                       msleep(1);
+               }
+       } while (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY);
+
+       if (!rc && rrb->response.hdr.rsp == CLP_RC_OK)
+               *fh = rrb->response.fh;
+       else {
+               pr_err("Set PCI FN failed with response: %x  cc: %d\n",
+                       rrb->response.hdr.rsp, rc);
+               rc = -EIO;
+       }
+       clp_free_block(rrb);
+       return rc;
+}
+
+int clp_enable_fh(struct zpci_dev *zdev, u8 nr_dma_as)
+{
+       u32 fh = zdev->fh;
+       int rc;
+
+       rc = clp_set_pci_fn(&fh, nr_dma_as, CLP_SET_ENABLE_PCI_FN);
+       if (!rc)
+               /* Success -> store enabled handle in zdev */
+               zdev->fh = fh;
+       return rc;
+}
+
+int clp_disable_fh(struct zpci_dev *zdev)
+{
+       u32 fh = zdev->fh;
+       int rc;
+
+       if (!zdev_enabled(zdev))
+               return 0;
+
+       dev_info(&zdev->pdev->dev, "disabling fn handle: 0x%x\n", fh);
+       rc = clp_set_pci_fn(&fh, 0, CLP_SET_DISABLE_PCI_FN);
+       if (!rc)
+               /* Success -> store disabled handle in zdev */
+               zdev->fh = fh;
+       else
+               dev_err(&zdev->pdev->dev,
+                       "Failed to disable fn handle: 0x%x\n", fh);
+       return rc;
+}
+
+static void clp_check_pcifn_entry(struct clp_fh_list_entry *entry)
+{
+       int present, rc;
+
+       if (!entry->vendor_id)
+               return;
+
+       /* TODO: be a little bit more scalable */
+       present = zpci_fid_present(entry->fid);
+
+       if (present)
+               pr_debug("%s: device %x already present\n", __func__, entry->fid);
+
+       /* skip already used functions */
+       if (present && entry->config_state)
+               return;
+
+       /* aev 306: function moved to stand-by state */
+       if (present && !entry->config_state) {
+               /*
+                * The handle is already disabled, that means no iota/irq freeing via
+                * the firmware interfaces anymore. Need to free resources manually
+                * (DMA memory, debug, sysfs)...
+                */
+               zpci_stop_device(get_zdev_by_fid(entry->fid));
+               return;
+       }
+
+       rc = clp_add_pci_device(entry->fid, entry->fh, entry->config_state);
+       if (rc)
+               pr_err("Failed to add fid: 0x%x\n", entry->fid);
+}
+
+int clp_find_pci_devices(void)
+{
+       struct clp_req_rsp_list_pci *rrb;
+       u64 resume_token = 0;
+       int entries, i, rc;
+
+       rrb = clp_alloc_block();
+       if (!rrb)
+               return -ENOMEM;
+
+       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_instr(rrb);
+               if (rc || rrb->response.hdr.rsp != CLP_RC_OK) {
+                       pr_err("List PCI failed with response: 0x%x  cc: %d\n",
+                               rrb->response.hdr.rsp, rc);
+                       rc = -EIO;
+                       goto out;
+               }
+
+               WARN_ON_ONCE(rrb->response.entry_size !=
+                       sizeof(struct clp_fh_list_entry));
+
+               entries = (rrb->response.hdr.len - LIST_PCI_HDR_LEN) /
+                       rrb->response.entry_size;
+               pr_info("Detected number of PCI functions: %u\n", entries);
+
+               /* Store the returned resume token as input for the next call */
+               resume_token = rrb->response.resume_token;
+
+               for (i = 0; i < entries; i++)
+                       clp_check_pcifn_entry(&rrb->response.fh_list[i]);
+       } while (resume_token);
+
+       pr_debug("Maximum number of supported PCI functions: %u\n",
+               rrb->response.max_fn);
+out:
+       clp_free_block(rrb);
+       return rc;
+}