linux/drivers/s390/char/sclp_pci.c
<<
>>
Prefs
   1/*
   2 * PCI I/O adapter configuration related functions.
   3 *
   4 * Copyright IBM Corp. 2016
   5 */
   6#define KMSG_COMPONENT "sclp_cmd"
   7#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
   8
   9#include <linux/completion.h>
  10#include <linux/export.h>
  11#include <linux/mutex.h>
  12#include <linux/errno.h>
  13#include <linux/slab.h>
  14#include <linux/init.h>
  15#include <linux/err.h>
  16
  17#include <asm/sclp.h>
  18
  19#include "sclp.h"
  20
  21#define SCLP_CMDW_CONFIGURE_PCI                 0x001a0001
  22#define SCLP_CMDW_DECONFIGURE_PCI               0x001b0001
  23
  24#define SCLP_ATYPE_PCI                          2
  25
  26#define SCLP_ERRNOTIFY_AQ_REPAIR                1
  27#define SCLP_ERRNOTIFY_AQ_INFO_LOG              2
  28
  29static DEFINE_MUTEX(sclp_pci_mutex);
  30static struct sclp_register sclp_pci_event = {
  31        .send_mask = EVTYP_ERRNOTIFY_MASK,
  32};
  33
  34struct err_notify_evbuf {
  35        struct evbuf_header header;
  36        u8 action;
  37        u8 atype;
  38        u32 fh;
  39        u32 fid;
  40        u8 data[0];
  41} __packed;
  42
  43struct err_notify_sccb {
  44        struct sccb_header header;
  45        struct err_notify_evbuf evbuf;
  46} __packed;
  47
  48struct pci_cfg_sccb {
  49        struct sccb_header header;
  50        u8 atype;               /* adapter type */
  51        u8 reserved1;
  52        u16 reserved2;
  53        u32 aid;                /* adapter identifier */
  54} __packed;
  55
  56static int do_pci_configure(sclp_cmdw_t cmd, u32 fid)
  57{
  58        struct pci_cfg_sccb *sccb;
  59        int rc;
  60
  61        if (!SCLP_HAS_PCI_RECONFIG)
  62                return -EOPNOTSUPP;
  63
  64        sccb = (struct pci_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
  65        if (!sccb)
  66                return -ENOMEM;
  67
  68        sccb->header.length = PAGE_SIZE;
  69        sccb->atype = SCLP_ATYPE_PCI;
  70        sccb->aid = fid;
  71        rc = sclp_sync_request(cmd, sccb);
  72        if (rc)
  73                goto out;
  74        switch (sccb->header.response_code) {
  75        case 0x0020:
  76        case 0x0120:
  77                break;
  78        default:
  79                pr_warn("configure PCI I/O adapter failed: cmd=0x%08x  response=0x%04x\n",
  80                        cmd, sccb->header.response_code);
  81                rc = -EIO;
  82                break;
  83        }
  84out:
  85        free_page((unsigned long) sccb);
  86        return rc;
  87}
  88
  89int sclp_pci_configure(u32 fid)
  90{
  91        return do_pci_configure(SCLP_CMDW_CONFIGURE_PCI, fid);
  92}
  93EXPORT_SYMBOL(sclp_pci_configure);
  94
  95int sclp_pci_deconfigure(u32 fid)
  96{
  97        return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid);
  98}
  99EXPORT_SYMBOL(sclp_pci_deconfigure);
 100
 101static void sclp_pci_callback(struct sclp_req *req, void *data)
 102{
 103        struct completion *completion = data;
 104
 105        complete(completion);
 106}
 107
 108static int sclp_pci_check_report(struct zpci_report_error_header *report)
 109{
 110        if (report->version != 1)
 111                return -EINVAL;
 112
 113        if (report->action != SCLP_ERRNOTIFY_AQ_REPAIR &&
 114            report->action != SCLP_ERRNOTIFY_AQ_INFO_LOG)
 115                return -EINVAL;
 116
 117        if (report->length > (PAGE_SIZE - sizeof(struct err_notify_sccb)))
 118                return -EINVAL;
 119
 120        return 0;
 121}
 122
 123int sclp_pci_report(struct zpci_report_error_header *report, u32 fh, u32 fid)
 124{
 125        DECLARE_COMPLETION_ONSTACK(completion);
 126        struct err_notify_sccb *sccb;
 127        struct sclp_req req;
 128        int ret;
 129
 130        ret = sclp_pci_check_report(report);
 131        if (ret)
 132                return ret;
 133
 134        mutex_lock(&sclp_pci_mutex);
 135        ret = sclp_register(&sclp_pci_event);
 136        if (ret)
 137                goto out_unlock;
 138
 139        if (!(sclp_pci_event.sclp_receive_mask & EVTYP_ERRNOTIFY_MASK)) {
 140                ret = -EOPNOTSUPP;
 141                goto out_unregister;
 142        }
 143
 144        sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
 145        if (!sccb) {
 146                ret = -ENOMEM;
 147                goto out_unregister;
 148        }
 149
 150        memset(&req, 0, sizeof(req));
 151        req.callback_data = &completion;
 152        req.callback = sclp_pci_callback;
 153        req.command = SCLP_CMDW_WRITE_EVENT_DATA;
 154        req.status = SCLP_REQ_FILLED;
 155        req.sccb = sccb;
 156
 157        sccb->evbuf.header.length = sizeof(sccb->evbuf) + report->length;
 158        sccb->evbuf.header.type = EVTYP_ERRNOTIFY;
 159        sccb->header.length = sizeof(sccb->header) + sccb->evbuf.header.length;
 160
 161        sccb->evbuf.action = report->action;
 162        sccb->evbuf.atype = SCLP_ATYPE_PCI;
 163        sccb->evbuf.fh = fh;
 164        sccb->evbuf.fid = fid;
 165
 166        memcpy(sccb->evbuf.data, report->data, report->length);
 167
 168        ret = sclp_add_request(&req);
 169        if (ret)
 170                goto out_free_req;
 171
 172        wait_for_completion(&completion);
 173        if (req.status != SCLP_REQ_DONE) {
 174                pr_warn("request failed (status=0x%02x)\n",
 175                        req.status);
 176                ret = -EIO;
 177                goto out_free_req;
 178        }
 179
 180        if (sccb->header.response_code != 0x0020) {
 181                pr_warn("request failed with response code 0x%x\n",
 182                        sccb->header.response_code);
 183                ret = -EIO;
 184        }
 185
 186out_free_req:
 187        free_page((unsigned long) sccb);
 188out_unregister:
 189        sclp_unregister(&sclp_pci_event);
 190out_unlock:
 191        mutex_unlock(&sclp_pci_mutex);
 192        return ret;
 193}
 194