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