2 * drivers/s390/cio/device_pgid.c
4 * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
6 * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
7 * Martin Schwidefsky (schwidefsky@de.ibm.com)
9 * Path Group ID functions.
12 #include <linux/module.h>
13 #include <linux/init.h>
15 #include <asm/ccwdev.h>
17 #include <asm/delay.h>
18 #include <asm/lowcore.h>
21 #include "cio_debug.h"
27 * Start Sense Path Group ID helper function. Used in ccw_device_recog
28 * and ccw_device_sense_pgid.
31 __ccw_device_sense_pgid_start(struct ccw_device *cdev)
33 struct subchannel *sch;
38 sch = to_subchannel(cdev->dev.parent);
39 /* Return if we already checked on all paths. */
40 if (cdev->private->imask == 0)
41 return (sch->lpm == 0) ? -ENODEV : -EACCES;
42 i = 8 - ffs(cdev->private->imask);
44 /* Setup sense path group id channel program. */
45 ccw = cdev->private->iccws;
46 ccw->cmd_code = CCW_CMD_SENSE_PGID;
47 ccw->count = sizeof (struct pgid);
48 ccw->flags = CCW_FLAG_SLI;
50 /* Reset device status. */
51 memset(&cdev->private->irb, 0, sizeof(struct irb));
52 /* Try on every path. */
54 while (cdev->private->imask != 0) {
55 /* Try every path multiple times. */
56 ccw->cda = (__u32) __pa (&cdev->private->pgid[i]);
57 if (cdev->private->iretry > 0) {
58 cdev->private->iretry--;
59 ret = cio_start (sch, cdev->private->iccws,
60 cdev->private->imask);
61 /* ret is 0, -EBUSY, -EACCES or -ENODEV */
64 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel "
65 "0.%x.%04x, lpm %02X, became 'not "
67 cdev->private->devno, sch->schid.ssid,
68 sch->schid.sch_no, cdev->private->imask);
71 cdev->private->imask >>= 1;
72 cdev->private->iretry = 5;
80 ccw_device_sense_pgid_start(struct ccw_device *cdev)
84 cdev->private->state = DEV_STATE_SENSE_PGID;
85 cdev->private->imask = 0x80;
86 cdev->private->iretry = 5;
87 memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid));
88 ret = __ccw_device_sense_pgid_start(cdev);
89 if (ret && ret != -EBUSY)
90 ccw_device_sense_pgid_done(cdev, ret);
94 * Called from interrupt context to check if a valid answer
95 * to Sense Path Group ID was received.
98 __ccw_device_check_sense_pgid(struct ccw_device *cdev)
100 struct subchannel *sch;
104 sch = to_subchannel(cdev->dev.parent);
105 irb = &cdev->private->irb;
106 if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
108 if (irb->esw.esw0.erw.cons &&
109 (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) {
111 * If the device doesn't support the Sense Path Group ID
112 * command further retries wouldn't help ...
116 if (irb->esw.esw0.erw.cons) {
117 CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, "
118 "lpum %02X, cnt %02d, sns : "
119 "%02X%02X%02X%02X %02X%02X%02X%02X ...\n",
120 cdev->private->ssid, cdev->private->devno,
121 irb->esw.esw0.sublog.lpum,
122 irb->esw.esw0.erw.scnt,
123 irb->ecw[0], irb->ecw[1],
124 irb->ecw[2], irb->ecw[3],
125 irb->ecw[4], irb->ecw[5],
126 irb->ecw[6], irb->ecw[7]);
129 if (irb->scsw.cc == 3) {
130 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x,"
131 " lpm %02X, became 'not operational'\n",
132 cdev->private->devno, sch->schid.ssid,
133 sch->schid.sch_no, sch->orb.lpm);
136 i = 8 - ffs(cdev->private->imask);
137 if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
138 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
139 "is reserved by someone else\n",
140 cdev->private->devno, sch->schid.ssid,
148 * Got interrupt for Sense Path Group ID.
151 ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
153 struct subchannel *sch;
157 irb = (struct irb *) __LC_IRB;
158 /* Retry sense pgid for cc=1. */
159 if (irb->scsw.stctl ==
160 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
161 if (irb->scsw.cc == 1) {
162 ret = __ccw_device_sense_pgid_start(cdev);
163 if (ret && ret != -EBUSY)
164 ccw_device_sense_pgid_done(cdev, ret);
168 if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
170 sch = to_subchannel(cdev->dev.parent);
171 ret = __ccw_device_check_sense_pgid(cdev);
172 memset(&cdev->private->irb, 0, sizeof(struct irb));
174 /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
175 case -EOPNOTSUPP: /* Sense Path Group ID not supported */
176 ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP);
178 case -ETIME: /* Sense path group id stopped by timeout. */
179 ccw_device_sense_pgid_done(cdev, -ETIME);
181 case -EACCES: /* channel is not operational. */
182 sch->lpm &= ~cdev->private->imask;
184 case 0: /* Sense Path Group ID successful. */
185 cdev->private->imask >>= 1;
186 cdev->private->iretry = 5;
188 case -EAGAIN: /* Try again. */
189 ret = __ccw_device_sense_pgid_start(cdev);
190 if (ret != 0 && ret != -EBUSY)
191 ccw_device_sense_pgid_done(cdev, ret);
193 case -EUSERS: /* device is reserved for someone else. */
194 ccw_device_sense_pgid_done(cdev, -EUSERS);
200 * Path Group ID helper function.
203 __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
205 struct subchannel *sch;
209 sch = to_subchannel(cdev->dev.parent);
211 /* Setup sense path group id channel program. */
212 cdev->private->pgid[0].inf.fc = func;
213 ccw = cdev->private->iccws;
214 if (!cdev->private->flags.pgid_single) {
215 cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH;
216 ccw->cmd_code = CCW_CMD_SUSPEND_RECONN;
219 ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC;
222 cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH;
224 ccw->cmd_code = CCW_CMD_SET_PGID;
225 ccw->cda = (__u32) __pa (&cdev->private->pgid[0]);
226 ccw->count = sizeof (struct pgid);
227 ccw->flags = CCW_FLAG_SLI;
229 /* Reset device status. */
230 memset(&cdev->private->irb, 0, sizeof(struct irb));
232 /* Try multiple times. */
234 if (cdev->private->iretry > 0) {
235 cdev->private->iretry--;
236 ret = cio_start (sch, cdev->private->iccws,
237 cdev->private->imask);
238 /* ret is 0, -EBUSY, -EACCES or -ENODEV */
239 if ((ret != -EACCES) && (ret != -ENODEV))
242 /* PGID command failed on this path. Switch it off. */
243 sch->lpm &= ~cdev->private->imask;
244 sch->vpm &= ~cdev->private->imask;
245 CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
246 "0.%x.%04x, lpm %02X, became 'not operational'\n",
247 cdev->private->devno, sch->schid.ssid,
248 sch->schid.sch_no, cdev->private->imask);
253 * Helper function to send a nop ccw down a path.
255 static int __ccw_device_do_nop(struct ccw_device *cdev)
257 struct subchannel *sch;
261 sch = to_subchannel(cdev->dev.parent);
263 /* Setup nop channel program. */
264 ccw = cdev->private->iccws;
265 ccw->cmd_code = CCW_CMD_NOOP;
268 ccw->flags = CCW_FLAG_SLI;
270 /* Reset device status. */
271 memset(&cdev->private->irb, 0, sizeof(struct irb));
273 /* Try multiple times. */
275 if (cdev->private->iretry > 0) {
276 cdev->private->iretry--;
277 ret = cio_start (sch, cdev->private->iccws,
278 cdev->private->imask);
279 /* ret is 0, -EBUSY, -EACCES or -ENODEV */
280 if ((ret != -EACCES) && (ret != -ENODEV))
283 /* nop command failed on this path. Switch it off. */
284 sch->lpm &= ~cdev->private->imask;
285 sch->vpm &= ~cdev->private->imask;
286 CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel "
287 "0.%x.%04x, lpm %02X, became 'not operational'\n",
288 cdev->private->devno, sch->schid.ssid,
289 sch->schid.sch_no, cdev->private->imask);
295 * Called from interrupt context to check if a valid answer
296 * to Set Path Group ID was received.
299 __ccw_device_check_pgid(struct ccw_device *cdev)
301 struct subchannel *sch;
304 sch = to_subchannel(cdev->dev.parent);
305 irb = &cdev->private->irb;
306 if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
308 if (irb->esw.esw0.erw.cons) {
309 if (irb->ecw[0] & SNS0_CMD_REJECT)
311 /* Hmm, whatever happened, try again. */
312 CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, "
314 "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
316 cdev->private->devno, irb->esw.esw0.erw.scnt,
317 irb->ecw[0], irb->ecw[1],
318 irb->ecw[2], irb->ecw[3],
319 irb->ecw[4], irb->ecw[5],
320 irb->ecw[6], irb->ecw[7]);
323 if (irb->scsw.cc == 3) {
324 CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel 0.%x.%04x,"
325 " lpm %02X, became 'not operational'\n",
326 cdev->private->devno, sch->schid.ssid,
327 sch->schid.sch_no, cdev->private->imask);
334 * Called from interrupt context to check the path status after a nop has
337 static int __ccw_device_check_nop(struct ccw_device *cdev)
339 struct subchannel *sch;
342 sch = to_subchannel(cdev->dev.parent);
343 irb = &cdev->private->irb;
344 if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
346 if (irb->scsw.cc == 3) {
347 CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel 0.%x.%04x,"
348 " lpm %02X, became 'not operational'\n",
349 cdev->private->devno, sch->schid.ssid,
350 sch->schid.sch_no, cdev->private->imask);
357 __ccw_device_verify_start(struct ccw_device *cdev)
359 struct subchannel *sch;
363 sch = to_subchannel(cdev->dev.parent);
364 while (sch->vpm != sch->lpm) {
365 /* Find first unequal bit in vpm vs. lpm */
366 for (imask = 0x80; imask != 0; imask >>= 1)
367 if ((sch->vpm & imask) != (sch->lpm & imask))
369 cdev->private->imask = imask;
370 if (cdev->private->options.pgroup) {
371 func = (sch->vpm & imask) ?
372 SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH;
373 ret = __ccw_device_do_pgid(cdev, func);
375 ret = __ccw_device_do_nop(cdev);
376 if (ret == 0 || ret == -EBUSY)
378 cdev->private->iretry = 5;
380 ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
384 * Got interrupt for Set Path Group ID.
387 ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
389 struct subchannel *sch;
393 irb = (struct irb *) __LC_IRB;
394 /* Retry set pgid for cc=1. */
395 if (irb->scsw.stctl ==
396 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
397 if (irb->scsw.cc == 1)
398 __ccw_device_verify_start(cdev);
401 if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
403 sch = to_subchannel(cdev->dev.parent);
404 if (cdev->private->options.pgroup)
405 ret = __ccw_device_check_pgid(cdev);
407 ret = __ccw_device_check_nop(cdev);
408 memset(&cdev->private->irb, 0, sizeof(struct irb));
410 /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
412 /* Establish or Resign Path Group done. Update vpm. */
413 if ((sch->lpm & cdev->private->imask) != 0)
414 sch->vpm |= cdev->private->imask;
416 sch->vpm &= ~cdev->private->imask;
417 cdev->private->iretry = 5;
418 __ccw_device_verify_start(cdev);
422 * One of those strange devices which claim to be able
423 * to do multipathing but not for Set Path Group ID.
425 if (cdev->private->flags.pgid_single)
426 cdev->private->options.pgroup = 0;
428 cdev->private->flags.pgid_single = 1;
430 case -EAGAIN: /* Try again. */
431 __ccw_device_verify_start(cdev);
433 case -ETIME: /* Set path group id stopped by timeout. */
434 ccw_device_verify_done(cdev, -ETIME);
436 case -EACCES: /* channel is not operational. */
437 sch->lpm &= ~cdev->private->imask;
438 sch->vpm &= ~cdev->private->imask;
439 cdev->private->iretry = 5;
440 __ccw_device_verify_start(cdev);
446 ccw_device_verify_start(struct ccw_device *cdev)
448 struct subchannel *sch = to_subchannel(cdev->dev.parent);
450 cdev->private->flags.pgid_single = 0;
451 cdev->private->iretry = 5;
453 * Update sch->lpm with current values to catch paths becoming
456 if (stsch(sch->schid, &sch->schib)) {
457 ccw_device_verify_done(cdev, -ENODEV);
460 sch->lpm = sch->schib.pmcw.pim &
461 sch->schib.pmcw.pam &
462 sch->schib.pmcw.pom &
464 __ccw_device_verify_start(cdev);
468 __ccw_device_disband_start(struct ccw_device *cdev)
470 struct subchannel *sch;
473 sch = to_subchannel(cdev->dev.parent);
474 while (cdev->private->imask != 0) {
475 if (sch->lpm & cdev->private->imask) {
476 ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND);
480 cdev->private->iretry = 5;
481 cdev->private->imask >>= 1;
483 ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
487 * Got interrupt for Unset Path Group ID.
490 ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
492 struct subchannel *sch;
496 irb = (struct irb *) __LC_IRB;
497 /* Retry set pgid for cc=1. */
498 if (irb->scsw.stctl ==
499 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
500 if (irb->scsw.cc == 1)
501 __ccw_device_disband_start(cdev);
504 if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
506 sch = to_subchannel(cdev->dev.parent);
507 ret = __ccw_device_check_pgid(cdev);
508 memset(&cdev->private->irb, 0, sizeof(struct irb));
510 /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
511 case 0: /* disband successful. */
513 ccw_device_disband_done(cdev, ret);
517 * One of those strange devices which claim to be able
518 * to do multipathing but not for Unset Path Group ID.
520 cdev->private->flags.pgid_single = 1;
522 case -EAGAIN: /* Try again. */
523 __ccw_device_disband_start(cdev);
525 case -ETIME: /* Set path group id stopped by timeout. */
526 ccw_device_disband_done(cdev, -ETIME);
528 case -EACCES: /* channel is not operational. */
529 cdev->private->imask >>= 1;
530 cdev->private->iretry = 5;
531 __ccw_device_disband_start(cdev);
537 ccw_device_disband_start(struct ccw_device *cdev)
539 cdev->private->flags.pgid_single = 0;
540 cdev->private->iretry = 5;
541 cdev->private->imask = 0x80;
542 __ccw_device_disband_start(cdev);