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