summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/s390/char/sclp.h2
-rw-r--r--drivers/s390/char/sclp_pci.c120
2 files changed, 120 insertions, 2 deletions
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index 026e38990952..6079efa95eaa 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -22,6 +22,7 @@
#define EVTYP_DIAG_TEST 0x07
#define EVTYP_STATECHANGE 0x08
#define EVTYP_PMSGCMD 0x09
+#define EVTYP_ERRNOTIFY 0x18
#define EVTYP_CNTLPROGOPCMD 0x20
#define EVTYP_CNTLPROGIDENT 0x0B
#define EVTYP_SIGQUIESCE 0x1D
@@ -36,6 +37,7 @@
#define EVTYP_DIAG_TEST_MASK 0x02000000
#define EVTYP_STATECHANGE_MASK 0x01000000
#define EVTYP_PMSGCMD_MASK 0x00800000
+#define EVTYP_ERRNOTIFY_MASK 0x00000100
#define EVTYP_CTLPROGOPCMD_MASK 0x00000001
#define EVTYP_CTLPROGIDENT_MASK 0x00200000
#define EVTYP_SIGQUIESCE_MASK 0x00000008
diff --git a/drivers/s390/char/sclp_pci.c b/drivers/s390/char/sclp_pci.c
index 943e92539e65..0c8973c45b48 100644
--- a/drivers/s390/char/sclp_pci.c
+++ b/drivers/s390/char/sclp_pci.c
@@ -8,6 +8,7 @@
#include <linux/completion.h>
#include <linux/export.h>
+#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
@@ -20,7 +21,29 @@
#define SCLP_CMDW_CONFIGURE_PCI 0x001a0001
#define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001
-#define SCLP_RECONFIG_PCI_ATPYE 2
+#define SCLP_ATYPE_PCI 2
+
+#define SCLP_ERRNOTIFY_AQ_REPAIR 1
+#define SCLP_ERRNOTIFY_AQ_INFO_LOG 2
+
+static DEFINE_MUTEX(sclp_pci_mutex);
+static struct sclp_register sclp_pci_event = {
+ .send_mask = EVTYP_ERRNOTIFY_MASK,
+};
+
+struct err_notify_evbuf {
+ struct evbuf_header header;
+ u8 action;
+ u8 atype;
+ u32 fh;
+ u32 fid;
+ u8 data[0];
+} __packed;
+
+struct err_notify_sccb {
+ struct sccb_header header;
+ struct err_notify_evbuf evbuf;
+} __packed;
struct pci_cfg_sccb {
struct sccb_header header;
@@ -43,7 +66,7 @@ static int do_pci_configure(sclp_cmdw_t cmd, u32 fid)
return -ENOMEM;
sccb->header.length = PAGE_SIZE;
- sccb->atype = SCLP_RECONFIG_PCI_ATPYE;
+ sccb->atype = SCLP_ATYPE_PCI;
sccb->aid = fid;
rc = sclp_sync_request(cmd, sccb);
if (rc)
@@ -74,3 +97,96 @@ int sclp_pci_deconfigure(u32 fid)
return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid);
}
EXPORT_SYMBOL(sclp_pci_deconfigure);
+
+static void sclp_pci_callback(struct sclp_req *req, void *data)
+{
+ struct completion *completion = data;
+
+ complete(completion);
+}
+
+static int sclp_pci_check_report(struct zpci_report_error_header *report)
+{
+ if (report->version != 1)
+ return -EINVAL;
+
+ if (report->action != SCLP_ERRNOTIFY_AQ_REPAIR &&
+ report->action != SCLP_ERRNOTIFY_AQ_INFO_LOG)
+ return -EINVAL;
+
+ if (report->length > (PAGE_SIZE - sizeof(struct err_notify_sccb)))
+ return -EINVAL;
+
+ return 0;
+}
+
+int sclp_pci_report(struct zpci_report_error_header *report, u32 fh, u32 fid)
+{
+ DECLARE_COMPLETION_ONSTACK(completion);
+ struct err_notify_sccb *sccb;
+ struct sclp_req req = {0};
+ int ret;
+
+ ret = sclp_pci_check_report(report);
+ if (ret)
+ return ret;
+
+ mutex_lock(&sclp_pci_mutex);
+ ret = sclp_register(&sclp_pci_event);
+ if (ret)
+ goto out_unlock;
+
+ if (!(sclp_pci_event.sclp_receive_mask & EVTYP_ERRNOTIFY_MASK)) {
+ ret = -EOPNOTSUPP;
+ goto out_unregister;
+ }
+
+ sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb) {
+ ret = -ENOMEM;
+ goto out_unregister;
+ }
+
+ req.callback_data = &completion;
+ req.callback = sclp_pci_callback;
+ req.command = SCLP_CMDW_WRITE_EVENT_DATA;
+ req.status = SCLP_REQ_FILLED;
+ req.sccb = sccb;
+
+ sccb->evbuf.header.length = sizeof(sccb->evbuf) + report->length;
+ sccb->evbuf.header.type = EVTYP_ERRNOTIFY;
+ sccb->header.length = sizeof(sccb->header) + sccb->evbuf.header.length;
+
+ sccb->evbuf.action = report->action;
+ sccb->evbuf.atype = SCLP_ATYPE_PCI;
+ sccb->evbuf.fh = fh;
+ sccb->evbuf.fid = fid;
+
+ memcpy(sccb->evbuf.data, report->data, report->length);
+
+ ret = sclp_add_request(&req);
+ if (ret)
+ goto out_free_req;
+
+ wait_for_completion(&completion);
+ if (req.status != SCLP_REQ_DONE) {
+ pr_warn("request failed (status=0x%02x)\n",
+ req.status);
+ ret = -EIO;
+ goto out_free_req;
+ }
+
+ if (sccb->header.response_code != 0x0020) {
+ pr_warn("request failed with response code 0x%x\n",
+ sccb->header.response_code);
+ ret = -EIO;
+ }
+
+out_free_req:
+ free_page((unsigned long) sccb);
+out_unregister:
+ sclp_unregister(&sclp_pci_event);
+out_unlock:
+ mutex_unlock(&sclp_pci_mutex);
+ return ret;
+}
OpenPOWER on IntegriCloud