Merge git://git.infradead.org/mtd-2.6
[linux-2.6] / drivers / s390 / cio / device_pgid.c
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 (cornelia.huck@de.ibm.com)
7  *               Martin Schwidefsky (schwidefsky@de.ibm.com)
8  *
9  * Path Group ID functions.
10  */
11
12 #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"
24 #include "ioasm.h"
25
26 /*
27  * Helper function called from interrupt context to decide whether an
28  * operation should be tried again.
29  */
30 static int __ccw_device_should_retry(struct scsw *scsw)
31 {
32         /* CC is only valid if start function bit is set. */
33         if ((scsw->fctl & SCSW_FCTL_START_FUNC) && scsw->cc == 1)
34                 return 1;
35         /* No more activity. For sense and set PGID we stubbornly try again. */
36         if (!scsw->actl)
37                 return 1;
38         return 0;
39 }
40
41 /*
42  * Start Sense Path Group ID helper function. Used in ccw_device_recog
43  * and ccw_device_sense_pgid.
44  */
45 static int
46 __ccw_device_sense_pgid_start(struct ccw_device *cdev)
47 {
48         struct subchannel *sch;
49         struct ccw1 *ccw;
50         int ret;
51         int i;
52
53         sch = to_subchannel(cdev->dev.parent);
54         /* Return if we already checked on all paths. */
55         if (cdev->private->imask == 0)
56                 return (sch->lpm == 0) ? -ENODEV : -EACCES;
57         i = 8 - ffs(cdev->private->imask);
58
59         /* Setup sense path group id channel program. */
60         ccw = cdev->private->iccws;
61         ccw->cmd_code = CCW_CMD_SENSE_PGID;
62         ccw->count = sizeof (struct pgid);
63         ccw->flags = CCW_FLAG_SLI;
64
65         /* Reset device status. */
66         memset(&cdev->private->irb, 0, sizeof(struct irb));
67         /* Try on every path. */
68         ret = -ENODEV;
69         while (cdev->private->imask != 0) {
70                 /* Try every path multiple times. */
71                 ccw->cda = (__u32) __pa (&cdev->private->pgid[i]);
72                 if (cdev->private->iretry > 0) {
73                         cdev->private->iretry--;
74                         ret = cio_start (sch, cdev->private->iccws, 
75                                          cdev->private->imask);
76                         /* ret is 0, -EBUSY, -EACCES or -ENODEV */
77                         if (ret != -EACCES)
78                                 return ret;
79                         CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel "
80                                       "0.%x.%04x, lpm %02X, became 'not "
81                                       "operational'\n",
82                                       cdev->private->devno, sch->schid.ssid,
83                                       sch->schid.sch_no, cdev->private->imask);
84
85                 }
86                 cdev->private->imask >>= 1;
87                 cdev->private->iretry = 5;
88                 i++;
89         }
90
91         return ret;
92 }
93
94 void
95 ccw_device_sense_pgid_start(struct ccw_device *cdev)
96 {
97         int ret;
98
99         cdev->private->state = DEV_STATE_SENSE_PGID;
100         cdev->private->imask = 0x80;
101         cdev->private->iretry = 5;
102         memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid));
103         ret = __ccw_device_sense_pgid_start(cdev);
104         if (ret && ret != -EBUSY)
105                 ccw_device_sense_pgid_done(cdev, ret);
106 }
107
108 /*
109  * Called from interrupt context to check if a valid answer
110  * to Sense Path Group ID was received.
111  */
112 static int
113 __ccw_device_check_sense_pgid(struct ccw_device *cdev)
114 {
115         struct subchannel *sch;
116         struct irb *irb;
117         int i;
118
119         sch = to_subchannel(cdev->dev.parent);
120         irb = &cdev->private->irb;
121         if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
122                 return -ETIME;
123         if (irb->esw.esw0.erw.cons &&
124             (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) {
125                 /*
126                  * If the device doesn't support the Sense Path Group ID
127                  *  command further retries wouldn't help ...
128                  */
129                 return -EOPNOTSUPP;
130         }
131         if (irb->esw.esw0.erw.cons) {
132                 CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, "
133                               "lpum %02X, cnt %02d, sns : "
134                               "%02X%02X%02X%02X %02X%02X%02X%02X ...\n",
135                               cdev->private->ssid, cdev->private->devno,
136                               irb->esw.esw0.sublog.lpum,
137                               irb->esw.esw0.erw.scnt,
138                               irb->ecw[0], irb->ecw[1],
139                               irb->ecw[2], irb->ecw[3],
140                               irb->ecw[4], irb->ecw[5],
141                               irb->ecw[6], irb->ecw[7]);
142                 return -EAGAIN;
143         }
144         if (irb->scsw.cc == 3) {
145                 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x,"
146                               " lpm %02X, became 'not operational'\n",
147                               cdev->private->devno, sch->schid.ssid,
148                               sch->schid.sch_no, sch->orb.lpm);
149                 return -EACCES;
150         }
151         i = 8 - ffs(cdev->private->imask);
152         if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
153                 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
154                               "is reserved by someone else\n",
155                               cdev->private->devno, sch->schid.ssid,
156                               sch->schid.sch_no);
157                 return -EUSERS;
158         }
159         return 0;
160 }
161
162 /*
163  * Got interrupt for Sense Path Group ID.
164  */
165 void
166 ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
167 {
168         struct subchannel *sch;
169         struct irb *irb;
170         int ret;
171
172         irb = (struct irb *) __LC_IRB;
173
174         if (irb->scsw.stctl ==
175             (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
176                 if (__ccw_device_should_retry(&irb->scsw)) {
177                         ret = __ccw_device_sense_pgid_start(cdev);
178                         if (ret && ret != -EBUSY)
179                                 ccw_device_sense_pgid_done(cdev, ret);
180                 }
181                 return;
182         }
183         if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
184                 return;
185         sch = to_subchannel(cdev->dev.parent);
186         ret = __ccw_device_check_sense_pgid(cdev);
187         memset(&cdev->private->irb, 0, sizeof(struct irb));
188         switch (ret) {
189         /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
190         case -EOPNOTSUPP:       /* Sense Path Group ID not supported */
191                 ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP);
192                 break;
193         case -ETIME:            /* Sense path group id stopped by timeout. */
194                 ccw_device_sense_pgid_done(cdev, -ETIME);
195                 break;
196         case -EACCES:           /* channel is not operational. */
197                 sch->lpm &= ~cdev->private->imask;
198                 /* Fall through. */
199         case 0:                 /* Sense Path Group ID successful. */
200                 cdev->private->imask >>= 1;
201                 cdev->private->iretry = 5;
202                 /* Fall through. */
203         case -EAGAIN:           /* Try again. */
204                 ret = __ccw_device_sense_pgid_start(cdev);
205                 if (ret != 0 && ret != -EBUSY)
206                         ccw_device_sense_pgid_done(cdev, ret);
207                 break;
208         case -EUSERS:           /* device is reserved for someone else. */
209                 ccw_device_sense_pgid_done(cdev, -EUSERS);
210                 break;
211         }
212 }
213
214 /*
215  * Path Group ID helper function.
216  */
217 static int
218 __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
219 {
220         struct subchannel *sch;
221         struct ccw1 *ccw;
222         int ret;
223
224         sch = to_subchannel(cdev->dev.parent);
225
226         /* Setup sense path group id channel program. */
227         cdev->private->pgid[0].inf.fc = func;
228         ccw = cdev->private->iccws;
229         if (!cdev->private->flags.pgid_single) {
230                 cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH;
231                 ccw->cmd_code = CCW_CMD_SUSPEND_RECONN;
232                 ccw->cda = 0;
233                 ccw->count = 0;
234                 ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC;
235                 ccw++;
236         } else
237                 cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH;
238
239         ccw->cmd_code = CCW_CMD_SET_PGID;
240         ccw->cda = (__u32) __pa (&cdev->private->pgid[0]);
241         ccw->count = sizeof (struct pgid);
242         ccw->flags = CCW_FLAG_SLI;
243
244         /* Reset device status. */
245         memset(&cdev->private->irb, 0, sizeof(struct irb));
246
247         /* Try multiple times. */
248         ret = -EACCES;
249         if (cdev->private->iretry > 0) {
250                 cdev->private->iretry--;
251                 ret = cio_start (sch, cdev->private->iccws,
252                                  cdev->private->imask);
253                 /* We expect an interrupt in case of success or busy
254                  * indication. */
255                 if ((ret == 0) || (ret == -EBUSY))
256                         return ret;
257         }
258         /* PGID command failed on this path. */
259         CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
260                       "0.%x.%04x, lpm %02X, became 'not operational'\n",
261                       cdev->private->devno, sch->schid.ssid,
262                       sch->schid.sch_no, cdev->private->imask);
263         return ret;
264 }
265
266 /*
267  * Helper function to send a nop ccw down a path.
268  */
269 static int __ccw_device_do_nop(struct ccw_device *cdev)
270 {
271         struct subchannel *sch;
272         struct ccw1 *ccw;
273         int ret;
274
275         sch = to_subchannel(cdev->dev.parent);
276
277         /* Setup nop channel program. */
278         ccw = cdev->private->iccws;
279         ccw->cmd_code = CCW_CMD_NOOP;
280         ccw->cda = 0;
281         ccw->count = 0;
282         ccw->flags = CCW_FLAG_SLI;
283
284         /* Reset device status. */
285         memset(&cdev->private->irb, 0, sizeof(struct irb));
286
287         /* Try multiple times. */
288         ret = -EACCES;
289         if (cdev->private->iretry > 0) {
290                 cdev->private->iretry--;
291                 ret = cio_start (sch, cdev->private->iccws,
292                                  cdev->private->imask);
293                 /* We expect an interrupt in case of success or busy
294                  * indication. */
295                 if ((ret == 0) || (ret == -EBUSY))
296                         return ret;
297         }
298         /* nop command failed on this path. */
299         CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel "
300                       "0.%x.%04x, lpm %02X, became 'not operational'\n",
301                       cdev->private->devno, sch->schid.ssid,
302                       sch->schid.sch_no, cdev->private->imask);
303         return ret;
304 }
305
306
307 /*
308  * Called from interrupt context to check if a valid answer
309  * to Set Path Group ID was received.
310  */
311 static int
312 __ccw_device_check_pgid(struct ccw_device *cdev)
313 {
314         struct subchannel *sch;
315         struct irb *irb;
316
317         sch = to_subchannel(cdev->dev.parent);
318         irb = &cdev->private->irb;
319         if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
320                 return -ETIME;
321         if (irb->esw.esw0.erw.cons) {
322                 if (irb->ecw[0] & SNS0_CMD_REJECT)
323                         return -EOPNOTSUPP;
324                 /* Hmm, whatever happened, try again. */
325                 CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, "
326                               "cnt %02d, "
327                               "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
328                               cdev->private->ssid,
329                               cdev->private->devno, irb->esw.esw0.erw.scnt,
330                               irb->ecw[0], irb->ecw[1],
331                               irb->ecw[2], irb->ecw[3],
332                               irb->ecw[4], irb->ecw[5],
333                               irb->ecw[6], irb->ecw[7]);
334                 return -EAGAIN;
335         }
336         if (irb->scsw.cc == 3) {
337                 CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel 0.%x.%04x,"
338                               " lpm %02X, became 'not operational'\n",
339                               cdev->private->devno, sch->schid.ssid,
340                               sch->schid.sch_no, cdev->private->imask);
341                 return -EACCES;
342         }
343         return 0;
344 }
345
346 /*
347  * Called from interrupt context to check the path status after a nop has
348  * been send.
349  */
350 static int __ccw_device_check_nop(struct ccw_device *cdev)
351 {
352         struct subchannel *sch;
353         struct irb *irb;
354
355         sch = to_subchannel(cdev->dev.parent);
356         irb = &cdev->private->irb;
357         if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
358                 return -ETIME;
359         if (irb->scsw.cc == 3) {
360                 CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel 0.%x.%04x,"
361                               " lpm %02X, became 'not operational'\n",
362                               cdev->private->devno, sch->schid.ssid,
363                               sch->schid.sch_no, cdev->private->imask);
364                 return -EACCES;
365         }
366         return 0;
367 }
368
369 static void
370 __ccw_device_verify_start(struct ccw_device *cdev)
371 {
372         struct subchannel *sch;
373         __u8 func;
374         int ret;
375
376         sch = to_subchannel(cdev->dev.parent);
377         /* Repeat for all paths. */
378         for (; cdev->private->imask; cdev->private->imask >>= 1,
379                                      cdev->private->iretry = 5) {
380                 if ((cdev->private->imask & sch->schib.pmcw.pam) == 0)
381                         /* Path not available, try next. */
382                         continue;
383                 if (cdev->private->options.pgroup) {
384                         if (sch->opm & cdev->private->imask)
385                                 func = SPID_FUNC_ESTABLISH;
386                         else
387                                 func = SPID_FUNC_RESIGN;
388                         ret = __ccw_device_do_pgid(cdev, func);
389                 } else
390                         ret = __ccw_device_do_nop(cdev);
391                 /* We expect an interrupt in case of success or busy
392                  * indication. */
393                 if (ret == 0 || ret == -EBUSY)
394                         return;
395                 /* Permanent path failure, try next. */
396         }
397         /* Done with all paths. */
398         ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV);
399 }
400                 
401 /*
402  * Got interrupt for Set Path Group ID.
403  */
404 void
405 ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
406 {
407         struct subchannel *sch;
408         struct irb *irb;
409         int ret;
410
411         irb = (struct irb *) __LC_IRB;
412
413         if (irb->scsw.stctl ==
414             (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
415                 if (__ccw_device_should_retry(&irb->scsw))
416                         __ccw_device_verify_start(cdev);
417                 return;
418         }
419         if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
420                 return;
421         sch = to_subchannel(cdev->dev.parent);
422         if (cdev->private->options.pgroup)
423                 ret = __ccw_device_check_pgid(cdev);
424         else
425                 ret = __ccw_device_check_nop(cdev);
426         memset(&cdev->private->irb, 0, sizeof(struct irb));
427
428         switch (ret) {
429         /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
430         case 0:
431                 /* Path verification ccw finished successfully, update lpm. */
432                 sch->vpm |= sch->opm & cdev->private->imask;
433                 /* Go on with next path. */
434                 cdev->private->imask >>= 1;
435                 cdev->private->iretry = 5;
436                 __ccw_device_verify_start(cdev);
437                 break;
438         case -EOPNOTSUPP:
439                 /*
440                  * One of those strange devices which claim to be able
441                  * to do multipathing but not for Set Path Group ID.
442                  */
443                 if (cdev->private->flags.pgid_single)
444                         cdev->private->options.pgroup = 0;
445                 else
446                         cdev->private->flags.pgid_single = 1;
447                 /* Retry */
448                 sch->vpm = 0;
449                 cdev->private->imask = 0x80;
450                 cdev->private->iretry = 5;
451                 /* fall through. */
452         case -EAGAIN:           /* Try again. */
453                 __ccw_device_verify_start(cdev);
454                 break;
455         case -ETIME:            /* Set path group id stopped by timeout. */
456                 ccw_device_verify_done(cdev, -ETIME);
457                 break;
458         case -EACCES:           /* channel is not operational. */
459                 cdev->private->imask >>= 1;
460                 cdev->private->iretry = 5;
461                 __ccw_device_verify_start(cdev);
462                 break;
463         }
464 }
465
466 void
467 ccw_device_verify_start(struct ccw_device *cdev)
468 {
469         struct subchannel *sch = to_subchannel(cdev->dev.parent);
470
471         cdev->private->flags.pgid_single = 0;
472         cdev->private->imask = 0x80;
473         cdev->private->iretry = 5;
474
475         /* Start with empty vpm. */
476         sch->vpm = 0;
477
478         /* Get current pam. */
479         if (stsch(sch->schid, &sch->schib)) {
480                 ccw_device_verify_done(cdev, -ENODEV);
481                 return;
482         }
483         __ccw_device_verify_start(cdev);
484 }
485
486 static void
487 __ccw_device_disband_start(struct ccw_device *cdev)
488 {
489         struct subchannel *sch;
490         int ret;
491
492         sch = to_subchannel(cdev->dev.parent);
493         while (cdev->private->imask != 0) {
494                 if (sch->lpm & cdev->private->imask) {
495                         ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND);
496                         if (ret == 0)
497                                 return;
498                 }
499                 cdev->private->iretry = 5;
500                 cdev->private->imask >>= 1;
501         }
502         ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
503 }
504
505 /*
506  * Got interrupt for Unset Path Group ID.
507  */
508 void
509 ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
510 {
511         struct subchannel *sch;
512         struct irb *irb;
513         int ret;
514
515         irb = (struct irb *) __LC_IRB;
516
517         if (irb->scsw.stctl ==
518             (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
519                 if (__ccw_device_should_retry(&irb->scsw))
520                         __ccw_device_disband_start(cdev);
521                 return;
522         }
523         if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
524                 return;
525         sch = to_subchannel(cdev->dev.parent);
526         ret = __ccw_device_check_pgid(cdev);
527         memset(&cdev->private->irb, 0, sizeof(struct irb));
528         switch (ret) {
529         /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
530         case 0:                 /* disband successful. */
531                 ccw_device_disband_done(cdev, ret);
532                 break;
533         case -EOPNOTSUPP:
534                 /*
535                  * One of those strange devices which claim to be able
536                  * to do multipathing but not for Unset Path Group ID.
537                  */
538                 cdev->private->flags.pgid_single = 1;
539                 /* fall through. */
540         case -EAGAIN:           /* Try again. */
541                 __ccw_device_disband_start(cdev);
542                 break;
543         case -ETIME:            /* Set path group id stopped by timeout. */
544                 ccw_device_disband_done(cdev, -ETIME);
545                 break;
546         case -EACCES:           /* channel is not operational. */
547                 cdev->private->imask >>= 1;
548                 cdev->private->iretry = 5;
549                 __ccw_device_disband_start(cdev);
550                 break;
551         }
552 }
553
554 void
555 ccw_device_disband_start(struct ccw_device *cdev)
556 {
557         cdev->private->flags.pgid_single = 0;
558         cdev->private->iretry = 5;
559         cdev->private->imask = 0x80;
560         __ccw_device_disband_start(cdev);
561 }