xref: /OK3568_Linux_fs/kernel/drivers/s390/char/sclp_pci.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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