xref: /openbmc/linux/drivers/s390/cio/device_pgid.c (revision 87c2ce3b)
1 /*
2  * drivers/s390/cio/device_pgid.c
3  *
4  *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
5  *			 IBM Corporation
6  *    Author(s): Cornelia Huck(cohuck@de.ibm.com)
7  *		 Martin Schwidefsky (schwidefsky@de.ibm.com)
8  *
9  * Path Group ID functions.
10  */
11 
12 #include <linux/config.h>
13 #include <linux/module.h>
14 #include <linux/init.h>
15 
16 #include <asm/ccwdev.h>
17 #include <asm/cio.h>
18 #include <asm/delay.h>
19 #include <asm/lowcore.h>
20 
21 #include "cio.h"
22 #include "cio_debug.h"
23 #include "css.h"
24 #include "device.h"
25 #include "ioasm.h"
26 
27 /*
28  * Start Sense Path Group ID helper function. Used in ccw_device_recog
29  * and ccw_device_sense_pgid.
30  */
31 static int
32 __ccw_device_sense_pgid_start(struct ccw_device *cdev)
33 {
34 	struct subchannel *sch;
35 	struct ccw1 *ccw;
36 	int ret;
37 
38 	sch = to_subchannel(cdev->dev.parent);
39 	/* Setup sense path group id channel program. */
40 	ccw = cdev->private->iccws;
41 	ccw->cmd_code = CCW_CMD_SENSE_PGID;
42 	ccw->cda = (__u32) __pa (&cdev->private->pgid);
43 	ccw->count = sizeof (struct pgid);
44 	ccw->flags = CCW_FLAG_SLI;
45 
46 	/* Reset device status. */
47 	memset(&cdev->private->irb, 0, sizeof(struct irb));
48 	/* Try on every path. */
49 	ret = -ENODEV;
50 	while (cdev->private->imask != 0) {
51 		/* Try every path multiple times. */
52 		if (cdev->private->iretry > 0) {
53 			cdev->private->iretry--;
54 			ret = cio_start (sch, cdev->private->iccws,
55 					 cdev->private->imask);
56 			/* ret is 0, -EBUSY, -EACCES or -ENODEV */
57 			if (ret != -EACCES)
58 				return ret;
59 			CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel "
60 				      "0.%x.%04x, lpm %02X, became 'not "
61 				      "operational'\n",
62 				      cdev->private->devno, sch->schid.ssid,
63 				      sch->schid.sch_no, cdev->private->imask);
64 
65 		}
66 		cdev->private->imask >>= 1;
67 		cdev->private->iretry = 5;
68 	}
69 	return ret;
70 }
71 
72 void
73 ccw_device_sense_pgid_start(struct ccw_device *cdev)
74 {
75 	int ret;
76 
77 	cdev->private->state = DEV_STATE_SENSE_PGID;
78 	cdev->private->imask = 0x80;
79 	cdev->private->iretry = 5;
80 	memset (&cdev->private->pgid, 0, sizeof (struct pgid));
81 	ret = __ccw_device_sense_pgid_start(cdev);
82 	if (ret && ret != -EBUSY)
83 		ccw_device_sense_pgid_done(cdev, ret);
84 }
85 
86 /*
87  * Called from interrupt context to check if a valid answer
88  * to Sense Path Group ID was received.
89  */
90 static int
91 __ccw_device_check_sense_pgid(struct ccw_device *cdev)
92 {
93 	struct subchannel *sch;
94 	struct irb *irb;
95 
96 	sch = to_subchannel(cdev->dev.parent);
97 	irb = &cdev->private->irb;
98 	if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
99 		return -ETIME;
100 	if (irb->esw.esw0.erw.cons &&
101 	    (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) {
102 		/*
103 		 * If the device doesn't support the Sense Path Group ID
104 		 *  command further retries wouldn't help ...
105 		 */
106 		return -EOPNOTSUPP;
107 	}
108 	if (irb->esw.esw0.erw.cons) {
109 		CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, "
110 			      "lpum %02X, cnt %02d, sns : "
111 			      "%02X%02X%02X%02X %02X%02X%02X%02X ...\n",
112 			      cdev->private->ssid, cdev->private->devno,
113 			      irb->esw.esw0.sublog.lpum,
114 			      irb->esw.esw0.erw.scnt,
115 			      irb->ecw[0], irb->ecw[1],
116 			      irb->ecw[2], irb->ecw[3],
117 			      irb->ecw[4], irb->ecw[5],
118 			      irb->ecw[6], irb->ecw[7]);
119 		return -EAGAIN;
120 	}
121 	if (irb->scsw.cc == 3) {
122 		CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x,"
123 			      " lpm %02X, became 'not operational'\n",
124 			      cdev->private->devno, sch->schid.ssid,
125 			      sch->schid.sch_no, sch->orb.lpm);
126 		return -EACCES;
127 	}
128 	if (cdev->private->pgid.inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
129 		CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
130 			      "is reserved by someone else\n",
131 			      cdev->private->devno, sch->schid.ssid,
132 			      sch->schid.sch_no);
133 		return -EUSERS;
134 	}
135 	return 0;
136 }
137 
138 /*
139  * Got interrupt for Sense Path Group ID.
140  */
141 void
142 ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
143 {
144 	struct subchannel *sch;
145 	struct irb *irb;
146 	int ret;
147 
148 	irb = (struct irb *) __LC_IRB;
149 	/* Retry sense pgid for cc=1. */
150 	if (irb->scsw.stctl ==
151 	    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
152 		if (irb->scsw.cc == 1) {
153 			ret = __ccw_device_sense_pgid_start(cdev);
154 			if (ret && ret != -EBUSY)
155 				ccw_device_sense_pgid_done(cdev, ret);
156 		}
157 		return;
158 	}
159 	if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
160 		return;
161 	sch = to_subchannel(cdev->dev.parent);
162 	ret = __ccw_device_check_sense_pgid(cdev);
163 	memset(&cdev->private->irb, 0, sizeof(struct irb));
164 	switch (ret) {
165 	/* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
166 	case 0:			/* Sense Path Group ID successful. */
167 		if (cdev->private->pgid.inf.ps.state1 == SNID_STATE1_RESET)
168 			memcpy(&cdev->private->pgid, &css[0]->global_pgid,
169 			       sizeof(struct pgid));
170 		ccw_device_sense_pgid_done(cdev, 0);
171 		break;
172 	case -EOPNOTSUPP:	/* Sense Path Group ID not supported */
173 		ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP);
174 		break;
175 	case -ETIME:		/* Sense path group id stopped by timeout. */
176 		ccw_device_sense_pgid_done(cdev, -ETIME);
177 		break;
178 	case -EACCES:		/* channel is not operational. */
179 		sch->lpm &= ~cdev->private->imask;
180 		cdev->private->imask >>= 1;
181 		cdev->private->iretry = 5;
182 		/* Fall through. */
183 	case -EAGAIN:		/* Try again. */
184 		ret = __ccw_device_sense_pgid_start(cdev);
185 		if (ret != 0 && ret != -EBUSY)
186 			ccw_device_sense_pgid_done(cdev, -ENODEV);
187 		break;
188 	case -EUSERS:		/* device is reserved for someone else. */
189 		ccw_device_sense_pgid_done(cdev, -EUSERS);
190 		break;
191 	}
192 }
193 
194 /*
195  * Path Group ID helper function.
196  */
197 static int
198 __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
199 {
200 	struct subchannel *sch;
201 	struct ccw1 *ccw;
202 	int ret;
203 
204 	sch = to_subchannel(cdev->dev.parent);
205 
206 	/* Setup sense path group id channel program. */
207 	cdev->private->pgid.inf.fc = func;
208 	ccw = cdev->private->iccws;
209 	if (!cdev->private->flags.pgid_single) {
210 		cdev->private->pgid.inf.fc |= SPID_FUNC_MULTI_PATH;
211 		ccw->cmd_code = CCW_CMD_SUSPEND_RECONN;
212 		ccw->cda = 0;
213 		ccw->count = 0;
214 		ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC;
215 		ccw++;
216 	} else
217 		cdev->private->pgid.inf.fc |= SPID_FUNC_SINGLE_PATH;
218 
219 	ccw->cmd_code = CCW_CMD_SET_PGID;
220 	ccw->cda = (__u32) __pa (&cdev->private->pgid);
221 	ccw->count = sizeof (struct pgid);
222 	ccw->flags = CCW_FLAG_SLI;
223 
224 	/* Reset device status. */
225 	memset(&cdev->private->irb, 0, sizeof(struct irb));
226 
227 	/* Try multiple times. */
228 	ret = -ENODEV;
229 	if (cdev->private->iretry > 0) {
230 		cdev->private->iretry--;
231 		ret = cio_start (sch, cdev->private->iccws,
232 				 cdev->private->imask);
233 		/* ret is 0, -EBUSY, -EACCES or -ENODEV */
234 		if ((ret != -EACCES) && (ret != -ENODEV))
235 			return ret;
236 	}
237 	/* PGID command failed on this path. Switch it off. */
238 	sch->lpm &= ~cdev->private->imask;
239 	sch->vpm &= ~cdev->private->imask;
240 	CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
241 		      "0.%x.%04x, lpm %02X, became 'not operational'\n",
242 		      cdev->private->devno, sch->schid.ssid,
243 		      sch->schid.sch_no, cdev->private->imask);
244 	return ret;
245 }
246 
247 /*
248  * Called from interrupt context to check if a valid answer
249  * to Set Path Group ID was received.
250  */
251 static int
252 __ccw_device_check_pgid(struct ccw_device *cdev)
253 {
254 	struct subchannel *sch;
255 	struct irb *irb;
256 
257 	sch = to_subchannel(cdev->dev.parent);
258 	irb = &cdev->private->irb;
259 	if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
260 		return -ETIME;
261 	if (irb->esw.esw0.erw.cons) {
262 		if (irb->ecw[0] & SNS0_CMD_REJECT)
263 			return -EOPNOTSUPP;
264 		/* Hmm, whatever happened, try again. */
265 		CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, "
266 			      "cnt %02d, "
267 			      "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
268 			      cdev->private->ssid,
269 			      cdev->private->devno, irb->esw.esw0.erw.scnt,
270 			      irb->ecw[0], irb->ecw[1],
271 			      irb->ecw[2], irb->ecw[3],
272 			      irb->ecw[4], irb->ecw[5],
273 			      irb->ecw[6], irb->ecw[7]);
274 		return -EAGAIN;
275 	}
276 	if (irb->scsw.cc == 3) {
277 		CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel 0.%x.%04x,"
278 			      " lpm %02X, became 'not operational'\n",
279 			      cdev->private->devno, sch->schid.ssid,
280 			      sch->schid.sch_no, cdev->private->imask);
281 		return -EACCES;
282 	}
283 	return 0;
284 }
285 
286 static void
287 __ccw_device_verify_start(struct ccw_device *cdev)
288 {
289 	struct subchannel *sch;
290 	__u8 imask, func;
291 	int ret;
292 
293 	sch = to_subchannel(cdev->dev.parent);
294 	while (sch->vpm != sch->lpm) {
295 		/* Find first unequal bit in vpm vs. lpm */
296 		for (imask = 0x80; imask != 0; imask >>= 1)
297 			if ((sch->vpm & imask) != (sch->lpm & imask))
298 				break;
299 		cdev->private->imask = imask;
300 		func = (sch->vpm & imask) ?
301 			SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH;
302 		ret = __ccw_device_do_pgid(cdev, func);
303 		if (ret == 0 || ret == -EBUSY)
304 			return;
305 		cdev->private->iretry = 5;
306 	}
307 	ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
308 }
309 
310 /*
311  * Got interrupt for Set Path Group ID.
312  */
313 void
314 ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
315 {
316 	struct subchannel *sch;
317 	struct irb *irb;
318 	int ret;
319 
320 	irb = (struct irb *) __LC_IRB;
321 	/* Retry set pgid for cc=1. */
322 	if (irb->scsw.stctl ==
323 	    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
324 		if (irb->scsw.cc == 1)
325 			__ccw_device_verify_start(cdev);
326 		return;
327 	}
328 	if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
329 		return;
330 	sch = to_subchannel(cdev->dev.parent);
331 	ret = __ccw_device_check_pgid(cdev);
332 	memset(&cdev->private->irb, 0, sizeof(struct irb));
333 	switch (ret) {
334 	/* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
335 	case 0:
336 		/* Establish or Resign Path Group done. Update vpm. */
337 		if ((sch->lpm & cdev->private->imask) != 0)
338 			sch->vpm |= cdev->private->imask;
339 		else
340 			sch->vpm &= ~cdev->private->imask;
341 		cdev->private->iretry = 5;
342 		__ccw_device_verify_start(cdev);
343 		break;
344 	case -EOPNOTSUPP:
345 		/*
346 		 * One of those strange devices which claim to be able
347 		 * to do multipathing but not for Set Path Group ID.
348 		 */
349 		if (cdev->private->flags.pgid_single) {
350 			ccw_device_verify_done(cdev, -EOPNOTSUPP);
351 			break;
352 		}
353 		cdev->private->flags.pgid_single = 1;
354 		/* fall through. */
355 	case -EAGAIN:		/* Try again. */
356 		__ccw_device_verify_start(cdev);
357 		break;
358 	case -ETIME:		/* Set path group id stopped by timeout. */
359 		ccw_device_verify_done(cdev, -ETIME);
360 		break;
361 	case -EACCES:		/* channel is not operational. */
362 		sch->lpm &= ~cdev->private->imask;
363 		sch->vpm &= ~cdev->private->imask;
364 		cdev->private->iretry = 5;
365 		__ccw_device_verify_start(cdev);
366 		break;
367 	}
368 }
369 
370 void
371 ccw_device_verify_start(struct ccw_device *cdev)
372 {
373 	struct subchannel *sch = to_subchannel(cdev->dev.parent);
374 
375 	cdev->private->flags.pgid_single = 0;
376 	cdev->private->iretry = 5;
377 	/*
378 	 * Update sch->lpm with current values to catch paths becoming
379 	 * available again.
380 	 */
381 	if (stsch(sch->schid, &sch->schib)) {
382 		ccw_device_verify_done(cdev, -ENODEV);
383 		return;
384 	}
385 	sch->lpm = sch->schib.pmcw.pim &
386 		sch->schib.pmcw.pam &
387 		sch->schib.pmcw.pom &
388 		sch->opm;
389 	__ccw_device_verify_start(cdev);
390 }
391 
392 static void
393 __ccw_device_disband_start(struct ccw_device *cdev)
394 {
395 	struct subchannel *sch;
396 	int ret;
397 
398 	sch = to_subchannel(cdev->dev.parent);
399 	while (cdev->private->imask != 0) {
400 		if (sch->lpm & cdev->private->imask) {
401 			ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND);
402 			if (ret == 0)
403 				return;
404 		}
405 		cdev->private->iretry = 5;
406 		cdev->private->imask >>= 1;
407 	}
408 	ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
409 }
410 
411 /*
412  * Got interrupt for Unset Path Group ID.
413  */
414 void
415 ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
416 {
417 	struct subchannel *sch;
418 	struct irb *irb;
419 	int ret;
420 
421 	irb = (struct irb *) __LC_IRB;
422 	/* Retry set pgid for cc=1. */
423 	if (irb->scsw.stctl ==
424 	    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
425 		if (irb->scsw.cc == 1)
426 			__ccw_device_disband_start(cdev);
427 		return;
428 	}
429 	if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
430 		return;
431 	sch = to_subchannel(cdev->dev.parent);
432 	ret = __ccw_device_check_pgid(cdev);
433 	memset(&cdev->private->irb, 0, sizeof(struct irb));
434 	switch (ret) {
435 	/* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
436 	case 0:			/* disband successful. */
437 		sch->vpm = 0;
438 		ccw_device_disband_done(cdev, ret);
439 		break;
440 	case -EOPNOTSUPP:
441 		/*
442 		 * One of those strange devices which claim to be able
443 		 * to do multipathing but not for Unset Path Group ID.
444 		 */
445 		cdev->private->flags.pgid_single = 1;
446 		/* fall through. */
447 	case -EAGAIN:		/* Try again. */
448 		__ccw_device_disband_start(cdev);
449 		break;
450 	case -ETIME:		/* Set path group id stopped by timeout. */
451 		ccw_device_disband_done(cdev, -ETIME);
452 		break;
453 	case -EACCES:		/* channel is not operational. */
454 		cdev->private->imask >>= 1;
455 		cdev->private->iretry = 5;
456 		__ccw_device_disband_start(cdev);
457 		break;
458 	}
459 }
460 
461 void
462 ccw_device_disband_start(struct ccw_device *cdev)
463 {
464 	cdev->private->flags.pgid_single = 0;
465 	cdev->private->iretry = 5;
466 	cdev->private->imask = 0x80;
467 	__ccw_device_disband_start(cdev);
468 }
469