Merge branch 'work'
[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/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 }