blob: 32610fd8868e97b6978a0d15160bad466eb31a89 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/s390/cio/device_pgid.c
3 *
4 * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
5 * IBM Corporation
Cornelia Huck4ce3b302006-01-14 13:21:04 -08006 * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * Martin Schwidefsky (schwidefsky@de.ibm.com)
8 *
9 * Path Group ID functions.
10 */
11
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/module.h>
13#include <linux/init.h>
14
15#include <asm/ccwdev.h>
16#include <asm/cio.h>
17#include <asm/delay.h>
18#include <asm/lowcore.h>
19
20#include "cio.h"
21#include "cio_debug.h"
22#include "css.h"
23#include "device.h"
Cornelia Huck6810a2b2006-01-06 00:19:13 -080024#include "ioasm.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
26/*
27 * Start Sense Path Group ID helper function. Used in ccw_device_recog
28 * and ccw_device_sense_pgid.
29 */
30static int
31__ccw_device_sense_pgid_start(struct ccw_device *cdev)
32{
33 struct subchannel *sch;
34 struct ccw1 *ccw;
35 int ret;
Cornelia Huck7e560812006-07-12 16:40:19 +020036 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
38 sch = to_subchannel(cdev->dev.parent);
Cornelia Huck7e560812006-07-12 16:40:19 +020039 /* 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);
43
Linus Torvalds1da177e2005-04-16 15:20:36 -070044 /* Setup sense path group id channel program. */
45 ccw = cdev->private->iccws;
46 ccw->cmd_code = CCW_CMD_SENSE_PGID;
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 ccw->count = sizeof (struct pgid);
48 ccw->flags = CCW_FLAG_SLI;
49
50 /* Reset device status. */
51 memset(&cdev->private->irb, 0, sizeof(struct irb));
52 /* Try on every path. */
53 ret = -ENODEV;
54 while (cdev->private->imask != 0) {
55 /* Try every path multiple times. */
Cornelia Huck7e560812006-07-12 16:40:19 +020056 ccw->cda = (__u32) __pa (&cdev->private->pgid[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 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 */
62 if (ret != -EACCES)
63 return ret;
64 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel "
Cornelia Huckfb6958a2006-01-06 00:19:25 -080065 "0.%x.%04x, lpm %02X, became 'not "
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 "operational'\n",
Cornelia Huckfb6958a2006-01-06 00:19:25 -080067 cdev->private->devno, sch->schid.ssid,
68 sch->schid.sch_no, cdev->private->imask);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069
70 }
71 cdev->private->imask >>= 1;
72 cdev->private->iretry = 5;
Cornelia Huck7e560812006-07-12 16:40:19 +020073 i++;
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 }
Cornelia Huck7e560812006-07-12 16:40:19 +020075
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 return ret;
77}
78
79void
80ccw_device_sense_pgid_start(struct ccw_device *cdev)
81{
82 int ret;
83
84 cdev->private->state = DEV_STATE_SENSE_PGID;
85 cdev->private->imask = 0x80;
86 cdev->private->iretry = 5;
Cornelia Huck7e560812006-07-12 16:40:19 +020087 memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid));
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 ret = __ccw_device_sense_pgid_start(cdev);
89 if (ret && ret != -EBUSY)
90 ccw_device_sense_pgid_done(cdev, ret);
91}
92
93/*
94 * Called from interrupt context to check if a valid answer
95 * to Sense Path Group ID was received.
96 */
97static int
98__ccw_device_check_sense_pgid(struct ccw_device *cdev)
99{
100 struct subchannel *sch;
101 struct irb *irb;
Cornelia Huck7e560812006-07-12 16:40:19 +0200102 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103
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))
107 return -ETIME;
108 if (irb->esw.esw0.erw.cons &&
109 (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) {
110 /*
111 * If the device doesn't support the Sense Path Group ID
112 * command further retries wouldn't help ...
113 */
114 return -EOPNOTSUPP;
115 }
116 if (irb->esw.esw0.erw.cons) {
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800117 CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 "lpum %02X, cnt %02d, sns : "
119 "%02X%02X%02X%02X %02X%02X%02X%02X ...\n",
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800120 cdev->private->ssid, cdev->private->devno,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 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]);
127 return -EAGAIN;
128 }
129 if (irb->scsw.cc == 3) {
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800130 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 return -EACCES;
135 }
Cornelia Huck7e560812006-07-12 16:40:19 +0200136 i = 8 - ffs(cdev->private->imask);
137 if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800138 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 "is reserved by someone else\n",
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800140 cdev->private->devno, sch->schid.ssid,
141 sch->schid.sch_no);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 return -EUSERS;
143 }
144 return 0;
145}
146
147/*
148 * Got interrupt for Sense Path Group ID.
149 */
150void
151ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
152{
153 struct subchannel *sch;
154 struct irb *irb;
155 int ret;
156
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);
165 }
166 return;
167 }
168 if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
169 return;
170 sch = to_subchannel(cdev->dev.parent);
171 ret = __ccw_device_check_sense_pgid(cdev);
172 memset(&cdev->private->irb, 0, sizeof(struct irb));
173 switch (ret) {
174 /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 case -EOPNOTSUPP: /* Sense Path Group ID not supported */
176 ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP);
177 break;
178 case -ETIME: /* Sense path group id stopped by timeout. */
179 ccw_device_sense_pgid_done(cdev, -ETIME);
180 break;
181 case -EACCES: /* channel is not operational. */
182 sch->lpm &= ~cdev->private->imask;
Cornelia Huck7e560812006-07-12 16:40:19 +0200183 /* Fall through. */
184 case 0: /* Sense Path Group ID successful. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 cdev->private->imask >>= 1;
186 cdev->private->iretry = 5;
187 /* Fall through. */
188 case -EAGAIN: /* Try again. */
189 ret = __ccw_device_sense_pgid_start(cdev);
190 if (ret != 0 && ret != -EBUSY)
Cornelia Huck7e560812006-07-12 16:40:19 +0200191 ccw_device_sense_pgid_done(cdev, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 break;
193 case -EUSERS: /* device is reserved for someone else. */
194 ccw_device_sense_pgid_done(cdev, -EUSERS);
195 break;
196 }
197}
198
199/*
200 * Path Group ID helper function.
201 */
202static int
203__ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
204{
205 struct subchannel *sch;
206 struct ccw1 *ccw;
207 int ret;
208
209 sch = to_subchannel(cdev->dev.parent);
210
211 /* Setup sense path group id channel program. */
Cornelia Huck7e560812006-07-12 16:40:19 +0200212 cdev->private->pgid[0].inf.fc = func;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 ccw = cdev->private->iccws;
214 if (!cdev->private->flags.pgid_single) {
Cornelia Huck7e560812006-07-12 16:40:19 +0200215 cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 ccw->cmd_code = CCW_CMD_SUSPEND_RECONN;
217 ccw->cda = 0;
218 ccw->count = 0;
219 ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC;
220 ccw++;
221 } else
Cornelia Huck7e560812006-07-12 16:40:19 +0200222 cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223
224 ccw->cmd_code = CCW_CMD_SET_PGID;
Cornelia Huck7e560812006-07-12 16:40:19 +0200225 ccw->cda = (__u32) __pa (&cdev->private->pgid[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 ccw->count = sizeof (struct pgid);
227 ccw->flags = CCW_FLAG_SLI;
228
229 /* Reset device status. */
230 memset(&cdev->private->irb, 0, sizeof(struct irb));
231
232 /* Try multiple times. */
233 ret = -ENODEV;
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))
240 return ret;
241 }
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 "
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800246 "0.%x.%04x, lpm %02X, became 'not operational'\n",
247 cdev->private->devno, sch->schid.ssid,
248 sch->schid.sch_no, cdev->private->imask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 return ret;
250}
251
252/*
Cornelia Huck7e560812006-07-12 16:40:19 +0200253 * Helper function to send a nop ccw down a path.
254 */
255static int __ccw_device_do_nop(struct ccw_device *cdev)
256{
257 struct subchannel *sch;
258 struct ccw1 *ccw;
259 int ret;
260
261 sch = to_subchannel(cdev->dev.parent);
262
263 /* Setup nop channel program. */
264 ccw = cdev->private->iccws;
265 ccw->cmd_code = CCW_CMD_NOOP;
266 ccw->cda = 0;
267 ccw->count = 0;
268 ccw->flags = CCW_FLAG_SLI;
269
270 /* Reset device status. */
271 memset(&cdev->private->irb, 0, sizeof(struct irb));
272
273 /* Try multiple times. */
274 ret = -ENODEV;
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))
281 return ret;
282 }
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);
290 return ret;
291}
292
293
294/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 * Called from interrupt context to check if a valid answer
296 * to Set Path Group ID was received.
297 */
298static int
299__ccw_device_check_pgid(struct ccw_device *cdev)
300{
301 struct subchannel *sch;
302 struct irb *irb;
303
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))
307 return -ETIME;
308 if (irb->esw.esw0.erw.cons) {
309 if (irb->ecw[0] & SNS0_CMD_REJECT)
310 return -EOPNOTSUPP;
311 /* Hmm, whatever happened, try again. */
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800312 CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, "
313 "cnt %02d, "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800315 cdev->private->ssid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 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]);
321 return -EAGAIN;
322 }
323 if (irb->scsw.cc == 3) {
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800324 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 return -EACCES;
329 }
330 return 0;
331}
332
Cornelia Huck7e560812006-07-12 16:40:19 +0200333/*
334 * Called from interrupt context to check the path status after a nop has
335 * been send.
336 */
337static int __ccw_device_check_nop(struct ccw_device *cdev)
338{
339 struct subchannel *sch;
340 struct irb *irb;
341
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))
345 return -ETIME;
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);
351 return -EACCES;
352 }
353 return 0;
354}
355
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356static void
357__ccw_device_verify_start(struct ccw_device *cdev)
358{
359 struct subchannel *sch;
360 __u8 imask, func;
361 int ret;
362
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))
368 break;
369 cdev->private->imask = imask;
Cornelia Huck7e560812006-07-12 16:40:19 +0200370 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);
374 } else
375 ret = __ccw_device_do_nop(cdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 if (ret == 0 || ret == -EBUSY)
377 return;
378 cdev->private->iretry = 5;
379 }
380 ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
381}
382
383/*
384 * Got interrupt for Set Path Group ID.
385 */
386void
387ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
388{
389 struct subchannel *sch;
390 struct irb *irb;
391 int ret;
392
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);
399 return;
400 }
401 if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
402 return;
403 sch = to_subchannel(cdev->dev.parent);
Cornelia Huck7e560812006-07-12 16:40:19 +0200404 if (cdev->private->options.pgroup)
405 ret = __ccw_device_check_pgid(cdev);
406 else
407 ret = __ccw_device_check_nop(cdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 memset(&cdev->private->irb, 0, sizeof(struct irb));
409 switch (ret) {
410 /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
411 case 0:
412 /* Establish or Resign Path Group done. Update vpm. */
413 if ((sch->lpm & cdev->private->imask) != 0)
414 sch->vpm |= cdev->private->imask;
415 else
416 sch->vpm &= ~cdev->private->imask;
417 cdev->private->iretry = 5;
418 __ccw_device_verify_start(cdev);
419 break;
420 case -EOPNOTSUPP:
421 /*
422 * One of those strange devices which claim to be able
423 * to do multipathing but not for Set Path Group ID.
424 */
Cornelia Huck7e560812006-07-12 16:40:19 +0200425 if (cdev->private->flags.pgid_single)
426 cdev->private->options.pgroup = 0;
427 else
428 cdev->private->flags.pgid_single = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 /* fall through. */
430 case -EAGAIN: /* Try again. */
431 __ccw_device_verify_start(cdev);
432 break;
433 case -ETIME: /* Set path group id stopped by timeout. */
434 ccw_device_verify_done(cdev, -ETIME);
435 break;
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);
441 break;
442 }
443}
444
445void
446ccw_device_verify_start(struct ccw_device *cdev)
447{
Cornelia Huck6810a2b2006-01-06 00:19:13 -0800448 struct subchannel *sch = to_subchannel(cdev->dev.parent);
449
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 cdev->private->flags.pgid_single = 0;
451 cdev->private->iretry = 5;
Cornelia Huck6810a2b2006-01-06 00:19:13 -0800452 /*
453 * Update sch->lpm with current values to catch paths becoming
454 * available again.
455 */
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800456 if (stsch(sch->schid, &sch->schib)) {
Cornelia Huck6810a2b2006-01-06 00:19:13 -0800457 ccw_device_verify_done(cdev, -ENODEV);
458 return;
459 }
460 sch->lpm = sch->schib.pmcw.pim &
461 sch->schib.pmcw.pam &
462 sch->schib.pmcw.pom &
463 sch->opm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 __ccw_device_verify_start(cdev);
465}
466
467static void
468__ccw_device_disband_start(struct ccw_device *cdev)
469{
470 struct subchannel *sch;
471 int ret;
472
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);
477 if (ret == 0)
478 return;
479 }
480 cdev->private->iretry = 5;
481 cdev->private->imask >>= 1;
482 }
Cornelia Huck6d751c42006-02-17 13:52:45 -0800483 ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484}
485
486/*
487 * Got interrupt for Unset Path Group ID.
488 */
489void
490ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
491{
492 struct subchannel *sch;
493 struct irb *irb;
494 int ret;
495
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);
502 return;
503 }
504 if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
505 return;
506 sch = to_subchannel(cdev->dev.parent);
507 ret = __ccw_device_check_pgid(cdev);
508 memset(&cdev->private->irb, 0, sizeof(struct irb));
509 switch (ret) {
510 /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
511 case 0: /* disband successful. */
512 sch->vpm = 0;
513 ccw_device_disband_done(cdev, ret);
514 break;
515 case -EOPNOTSUPP:
516 /*
517 * One of those strange devices which claim to be able
518 * to do multipathing but not for Unset Path Group ID.
519 */
520 cdev->private->flags.pgid_single = 1;
521 /* fall through. */
522 case -EAGAIN: /* Try again. */
523 __ccw_device_disband_start(cdev);
524 break;
525 case -ETIME: /* Set path group id stopped by timeout. */
526 ccw_device_disband_done(cdev, -ETIME);
527 break;
528 case -EACCES: /* channel is not operational. */
529 cdev->private->imask >>= 1;
530 cdev->private->iretry = 5;
531 __ccw_device_disband_start(cdev);
532 break;
533 }
534}
535
536void
537ccw_device_disband_start(struct ccw_device *cdev)
538{
539 cdev->private->flags.pgid_single = 0;
540 cdev->private->iretry = 5;
541 cdev->private->imask = 0x80;
542 __ccw_device_disband_start(cdev);
543}