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