Merge branch 'upstream' of git://lost.foo-projects.org/~ahkok/git/netdev-2.6 into...
[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  * 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                                       "0.%x.%04x, lpm %02X, became 'not "
60                                       "operational'\n",
61                                       cdev->private->devno, sch->schid.ssid,
62                                       sch->schid.sch_no, 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 0.%x.%04x, unit check, "
109                               "lpum %02X, cnt %02d, sns : "
110                               "%02X%02X%02X%02X %02X%02X%02X%02X ...\n",
111                               cdev->private->ssid, 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 0.%x.%04x,"
122                               " lpm %02X, became 'not operational'\n",
123                               cdev->private->devno, sch->schid.ssid,
124                               sch->schid.sch_no, sch->orb.lpm);
125                 return -EACCES;
126         }
127         if (cdev->private->pgid.inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
128                 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
129                               "is reserved by someone else\n",
130                               cdev->private->devno, sch->schid.ssid,
131                               sch->schid.sch_no);
132                 return -EUSERS;
133         }
134         return 0;
135 }
136
137 /*
138  * Got interrupt for Sense Path Group ID.
139  */
140 void
141 ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
142 {
143         struct subchannel *sch;
144         struct irb *irb;
145         int ret;
146
147         irb = (struct irb *) __LC_IRB;
148         /* Retry sense pgid for cc=1. */
149         if (irb->scsw.stctl ==
150             (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
151                 if (irb->scsw.cc == 1) {
152                         ret = __ccw_device_sense_pgid_start(cdev);
153                         if (ret && ret != -EBUSY)
154                                 ccw_device_sense_pgid_done(cdev, ret);
155                 }
156                 return;
157         }
158         if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
159                 return;
160         sch = to_subchannel(cdev->dev.parent);
161         ret = __ccw_device_check_sense_pgid(cdev);
162         memset(&cdev->private->irb, 0, sizeof(struct irb));
163         switch (ret) {
164         /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
165         case 0:                 /* Sense Path Group ID successful. */
166                 if (cdev->private->pgid.inf.ps.state1 == SNID_STATE1_RESET)
167                         memcpy(&cdev->private->pgid, &css[0]->global_pgid,
168                                sizeof(struct pgid));
169                 ccw_device_sense_pgid_done(cdev, 0);
170                 break;
171         case -EOPNOTSUPP:       /* Sense Path Group ID not supported */
172                 ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP);
173                 break;
174         case -ETIME:            /* Sense path group id stopped by timeout. */
175                 ccw_device_sense_pgid_done(cdev, -ETIME);
176                 break;
177         case -EACCES:           /* channel is not operational. */
178                 sch->lpm &= ~cdev->private->imask;
179                 cdev->private->imask >>= 1;
180                 cdev->private->iretry = 5;
181                 /* Fall through. */
182         case -EAGAIN:           /* Try again. */
183                 ret = __ccw_device_sense_pgid_start(cdev);
184                 if (ret != 0 && ret != -EBUSY)
185                         ccw_device_sense_pgid_done(cdev, -ENODEV);
186                 break;
187         case -EUSERS:           /* device is reserved for someone else. */
188                 ccw_device_sense_pgid_done(cdev, -EUSERS);
189                 break;
190         }
191 }
192
193 /*
194  * Path Group ID helper function.
195  */
196 static int
197 __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
198 {
199         struct subchannel *sch;
200         struct ccw1 *ccw;
201         int ret;
202
203         sch = to_subchannel(cdev->dev.parent);
204
205         /* Setup sense path group id channel program. */
206         cdev->private->pgid.inf.fc = func;
207         ccw = cdev->private->iccws;
208         if (!cdev->private->flags.pgid_single) {
209                 cdev->private->pgid.inf.fc |= SPID_FUNC_MULTI_PATH;
210                 ccw->cmd_code = CCW_CMD_SUSPEND_RECONN;
211                 ccw->cda = 0;
212                 ccw->count = 0;
213                 ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC;
214                 ccw++;
215         } else
216                 cdev->private->pgid.inf.fc |= SPID_FUNC_SINGLE_PATH;
217
218         ccw->cmd_code = CCW_CMD_SET_PGID;
219         ccw->cda = (__u32) __pa (&cdev->private->pgid);
220         ccw->count = sizeof (struct pgid);
221         ccw->flags = CCW_FLAG_SLI;
222
223         /* Reset device status. */
224         memset(&cdev->private->irb, 0, sizeof(struct irb));
225
226         /* Try multiple times. */
227         ret = -ENODEV;
228         if (cdev->private->iretry > 0) {
229                 cdev->private->iretry--;
230                 ret = cio_start (sch, cdev->private->iccws,
231                                  cdev->private->imask);
232                 /* ret is 0, -EBUSY, -EACCES or -ENODEV */
233                 if ((ret != -EACCES) && (ret != -ENODEV))
234                         return ret;
235         }
236         /* PGID command failed on this path. Switch it off. */
237         sch->lpm &= ~cdev->private->imask;
238         sch->vpm &= ~cdev->private->imask;
239         CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
240                       "0.%x.%04x, lpm %02X, became 'not operational'\n",
241                       cdev->private->devno, sch->schid.ssid,
242                       sch->schid.sch_no, cdev->private->imask);
243         return ret;
244 }
245
246 /*
247  * Called from interrupt context to check if a valid answer
248  * to Set Path Group ID was received.
249  */
250 static int
251 __ccw_device_check_pgid(struct ccw_device *cdev)
252 {
253         struct subchannel *sch;
254         struct irb *irb;
255
256         sch = to_subchannel(cdev->dev.parent);
257         irb = &cdev->private->irb;
258         if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
259                 return -ETIME;
260         if (irb->esw.esw0.erw.cons) {
261                 if (irb->ecw[0] & SNS0_CMD_REJECT)
262                         return -EOPNOTSUPP;
263                 /* Hmm, whatever happened, try again. */
264                 CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, "
265                               "cnt %02d, "
266                               "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
267                               cdev->private->ssid,
268                               cdev->private->devno, irb->esw.esw0.erw.scnt,
269                               irb->ecw[0], irb->ecw[1],
270                               irb->ecw[2], irb->ecw[3],
271                               irb->ecw[4], irb->ecw[5],
272                               irb->ecw[6], irb->ecw[7]);
273                 return -EAGAIN;
274         }
275         if (irb->scsw.cc == 3) {
276                 CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel 0.%x.%04x,"
277                               " lpm %02X, became 'not operational'\n",
278                               cdev->private->devno, sch->schid.ssid,
279                               sch->schid.sch_no, cdev->private->imask);
280                 return -EACCES;
281         }
282         return 0;
283 }
284
285 static void
286 __ccw_device_verify_start(struct ccw_device *cdev)
287 {
288         struct subchannel *sch;
289         __u8 imask, func;
290         int ret;
291
292         sch = to_subchannel(cdev->dev.parent);
293         while (sch->vpm != sch->lpm) {
294                 /* Find first unequal bit in vpm vs. lpm */
295                 for (imask = 0x80; imask != 0; imask >>= 1)
296                         if ((sch->vpm & imask) != (sch->lpm & imask))
297                                 break;
298                 cdev->private->imask = imask;
299                 func = (sch->vpm & imask) ?
300                         SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH;
301                 ret = __ccw_device_do_pgid(cdev, func);
302                 if (ret == 0 || ret == -EBUSY)
303                         return;
304                 cdev->private->iretry = 5;
305         }
306         ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
307 }
308                 
309 /*
310  * Got interrupt for Set Path Group ID.
311  */
312 void
313 ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
314 {
315         struct subchannel *sch;
316         struct irb *irb;
317         int ret;
318
319         irb = (struct irb *) __LC_IRB;
320         /* Retry set pgid for cc=1. */
321         if (irb->scsw.stctl ==
322             (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
323                 if (irb->scsw.cc == 1)
324                         __ccw_device_verify_start(cdev);
325                 return;
326         }
327         if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
328                 return;
329         sch = to_subchannel(cdev->dev.parent);
330         ret = __ccw_device_check_pgid(cdev);
331         memset(&cdev->private->irb, 0, sizeof(struct irb));
332         switch (ret) {
333         /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
334         case 0:
335                 /* Establish or Resign Path Group done. Update vpm. */
336                 if ((sch->lpm & cdev->private->imask) != 0)
337                         sch->vpm |= cdev->private->imask;
338                 else
339                         sch->vpm &= ~cdev->private->imask;
340                 cdev->private->iretry = 5;
341                 __ccw_device_verify_start(cdev);
342                 break;
343         case -EOPNOTSUPP:
344                 /*
345                  * One of those strange devices which claim to be able
346                  * to do multipathing but not for Set Path Group ID.
347                  */
348                 if (cdev->private->flags.pgid_single) {
349                         ccw_device_verify_done(cdev, -EOPNOTSUPP);
350                         break;
351                 }
352                 cdev->private->flags.pgid_single = 1;
353                 /* fall through. */
354         case -EAGAIN:           /* Try again. */
355                 __ccw_device_verify_start(cdev);
356                 break;
357         case -ETIME:            /* Set path group id stopped by timeout. */
358                 ccw_device_verify_done(cdev, -ETIME);
359                 break;
360         case -EACCES:           /* channel is not operational. */
361                 sch->lpm &= ~cdev->private->imask;
362                 sch->vpm &= ~cdev->private->imask;
363                 cdev->private->iretry = 5;
364                 __ccw_device_verify_start(cdev);
365                 break;
366         }
367 }
368
369 void
370 ccw_device_verify_start(struct ccw_device *cdev)
371 {
372         struct subchannel *sch = to_subchannel(cdev->dev.parent);
373
374         cdev->private->flags.pgid_single = 0;
375         cdev->private->iretry = 5;
376         /*
377          * Update sch->lpm with current values to catch paths becoming
378          * available again.
379          */
380         if (stsch(sch->schid, &sch->schib)) {
381                 ccw_device_verify_done(cdev, -ENODEV);
382                 return;
383         }
384         sch->lpm = sch->schib.pmcw.pim &
385                 sch->schib.pmcw.pam &
386                 sch->schib.pmcw.pom &
387                 sch->opm;
388         __ccw_device_verify_start(cdev);
389 }
390
391 static void
392 __ccw_device_disband_start(struct ccw_device *cdev)
393 {
394         struct subchannel *sch;
395         int ret;
396
397         sch = to_subchannel(cdev->dev.parent);
398         while (cdev->private->imask != 0) {
399                 if (sch->lpm & cdev->private->imask) {
400                         ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND);
401                         if (ret == 0)
402                                 return;
403                 }
404                 cdev->private->iretry = 5;
405                 cdev->private->imask >>= 1;
406         }
407         ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
408 }
409
410 /*
411  * Got interrupt for Unset Path Group ID.
412  */
413 void
414 ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
415 {
416         struct subchannel *sch;
417         struct irb *irb;
418         int ret;
419
420         irb = (struct irb *) __LC_IRB;
421         /* Retry set pgid for cc=1. */
422         if (irb->scsw.stctl ==
423             (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
424                 if (irb->scsw.cc == 1)
425                         __ccw_device_disband_start(cdev);
426                 return;
427         }
428         if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
429                 return;
430         sch = to_subchannel(cdev->dev.parent);
431         ret = __ccw_device_check_pgid(cdev);
432         memset(&cdev->private->irb, 0, sizeof(struct irb));
433         switch (ret) {
434         /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
435         case 0:                 /* disband successful. */
436                 sch->vpm = 0;
437                 ccw_device_disband_done(cdev, ret);
438                 break;
439         case -EOPNOTSUPP:
440                 /*
441                  * One of those strange devices which claim to be able
442                  * to do multipathing but not for Unset Path Group ID.
443                  */
444                 cdev->private->flags.pgid_single = 1;
445                 /* fall through. */
446         case -EAGAIN:           /* Try again. */
447                 __ccw_device_disband_start(cdev);
448                 break;
449         case -ETIME:            /* Set path group id stopped by timeout. */
450                 ccw_device_disband_done(cdev, -ETIME);
451                 break;
452         case -EACCES:           /* channel is not operational. */
453                 cdev->private->imask >>= 1;
454                 cdev->private->iretry = 5;
455                 __ccw_device_disband_start(cdev);
456                 break;
457         }
458 }
459
460 void
461 ccw_device_disband_start(struct ccw_device *cdev)
462 {
463         cdev->private->flags.pgid_single = 0;
464         cdev->private->iretry = 5;
465         cdev->private->imask = 0x80;
466         __ccw_device_disband_start(cdev);
467 }