]> Pileus Git - ~andy/linux/blob - drivers/s390/cio/device_pgid.c
[PATCH] s390: introduce struct subchannel_id
[~andy/linux] / 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(cohuck@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                                       "%04x, lpm %02X, became 'not "
61                                       "operational'\n",
62                                       cdev->private->devno, sch->schid.sch_no,
63                                       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 %04x, unit check, "
110                               "lpum %02X, cnt %02d, sns : "
111                               "%02X%02X%02X%02X %02X%02X%02X%02X ...\n",
112                               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 "
123                               "%04x, lpm %02X, became 'not operational'\n",
124                               cdev->private->devno, sch->schid.sch_no,
125                               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 %04x "
130                               "is reserved by someone else\n",
131                               cdev->private->devno, 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, &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                       "%04x, lpm %02X, became 'not operational'\n",
241                       cdev->private->devno, sch->schid.sch_no, cdev->private->imask);
242         return ret;
243 }
244
245 /*
246  * Called from interrupt context to check if a valid answer
247  * to Set Path Group ID was received.
248  */
249 static int
250 __ccw_device_check_pgid(struct ccw_device *cdev)
251 {
252         struct subchannel *sch;
253         struct irb *irb;
254
255         sch = to_subchannel(cdev->dev.parent);
256         irb = &cdev->private->irb;
257         if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
258                 return -ETIME;
259         if (irb->esw.esw0.erw.cons) {
260                 if (irb->ecw[0] & SNS0_CMD_REJECT)
261                         return -EOPNOTSUPP;
262                 /* Hmm, whatever happened, try again. */
263                 CIO_MSG_EVENT(2, "SPID - device %04x, unit check, cnt %02d, "
264                               "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
265                               cdev->private->devno, irb->esw.esw0.erw.scnt,
266                               irb->ecw[0], irb->ecw[1],
267                               irb->ecw[2], irb->ecw[3],
268                               irb->ecw[4], irb->ecw[5],
269                               irb->ecw[6], irb->ecw[7]);
270                 return -EAGAIN;
271         }
272         if (irb->scsw.cc == 3) {
273                 CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
274                               "%04x, lpm %02X, became 'not operational'\n",
275                               cdev->private->devno, sch->schid.sch_no,
276                               cdev->private->imask);
277                 return -EACCES;
278         }
279         return 0;
280 }
281
282 static void
283 __ccw_device_verify_start(struct ccw_device *cdev)
284 {
285         struct subchannel *sch;
286         __u8 imask, func;
287         int ret;
288
289         sch = to_subchannel(cdev->dev.parent);
290         while (sch->vpm != sch->lpm) {
291                 /* Find first unequal bit in vpm vs. lpm */
292                 for (imask = 0x80; imask != 0; imask >>= 1)
293                         if ((sch->vpm & imask) != (sch->lpm & imask))
294                                 break;
295                 cdev->private->imask = imask;
296                 func = (sch->vpm & imask) ?
297                         SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH;
298                 ret = __ccw_device_do_pgid(cdev, func);
299                 if (ret == 0 || ret == -EBUSY)
300                         return;
301                 cdev->private->iretry = 5;
302         }
303         ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
304 }
305                 
306 /*
307  * Got interrupt for Set Path Group ID.
308  */
309 void
310 ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
311 {
312         struct subchannel *sch;
313         struct irb *irb;
314         int ret;
315
316         irb = (struct irb *) __LC_IRB;
317         /* Retry set pgid for cc=1. */
318         if (irb->scsw.stctl ==
319             (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
320                 if (irb->scsw.cc == 1)
321                         __ccw_device_verify_start(cdev);
322                 return;
323         }
324         if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
325                 return;
326         sch = to_subchannel(cdev->dev.parent);
327         ret = __ccw_device_check_pgid(cdev);
328         memset(&cdev->private->irb, 0, sizeof(struct irb));
329         switch (ret) {
330         /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
331         case 0:
332                 /* Establish or Resign Path Group done. Update vpm. */
333                 if ((sch->lpm & cdev->private->imask) != 0)
334                         sch->vpm |= cdev->private->imask;
335                 else
336                         sch->vpm &= ~cdev->private->imask;
337                 cdev->private->iretry = 5;
338                 __ccw_device_verify_start(cdev);
339                 break;
340         case -EOPNOTSUPP:
341                 /*
342                  * One of those strange devices which claim to be able
343                  * to do multipathing but not for Set Path Group ID.
344                  */
345                 if (cdev->private->flags.pgid_single) {
346                         ccw_device_verify_done(cdev, -EOPNOTSUPP);
347                         break;
348                 }
349                 cdev->private->flags.pgid_single = 1;
350                 /* fall through. */
351         case -EAGAIN:           /* Try again. */
352                 __ccw_device_verify_start(cdev);
353                 break;
354         case -ETIME:            /* Set path group id stopped by timeout. */
355                 ccw_device_verify_done(cdev, -ETIME);
356                 break;
357         case -EACCES:           /* channel is not operational. */
358                 sch->lpm &= ~cdev->private->imask;
359                 sch->vpm &= ~cdev->private->imask;
360                 cdev->private->iretry = 5;
361                 __ccw_device_verify_start(cdev);
362                 break;
363         }
364 }
365
366 void
367 ccw_device_verify_start(struct ccw_device *cdev)
368 {
369         struct subchannel *sch = to_subchannel(cdev->dev.parent);
370
371         cdev->private->flags.pgid_single = 0;
372         cdev->private->iretry = 5;
373         /*
374          * Update sch->lpm with current values to catch paths becoming
375          * available again.
376          */
377         if (stsch(sch->schid, &sch->schib)) {
378                 ccw_device_verify_done(cdev, -ENODEV);
379                 return;
380         }
381         sch->lpm = sch->schib.pmcw.pim &
382                 sch->schib.pmcw.pam &
383                 sch->schib.pmcw.pom &
384                 sch->opm;
385         __ccw_device_verify_start(cdev);
386 }
387
388 static void
389 __ccw_device_disband_start(struct ccw_device *cdev)
390 {
391         struct subchannel *sch;
392         int ret;
393
394         sch = to_subchannel(cdev->dev.parent);
395         while (cdev->private->imask != 0) {
396                 if (sch->lpm & cdev->private->imask) {
397                         ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND);
398                         if (ret == 0)
399                                 return;
400                 }
401                 cdev->private->iretry = 5;
402                 cdev->private->imask >>= 1;
403         }
404         ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
405 }
406
407 /*
408  * Got interrupt for Unset Path Group ID.
409  */
410 void
411 ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
412 {
413         struct subchannel *sch;
414         struct irb *irb;
415         int ret;
416
417         irb = (struct irb *) __LC_IRB;
418         /* Retry set pgid for cc=1. */
419         if (irb->scsw.stctl ==
420             (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
421                 if (irb->scsw.cc == 1)
422                         __ccw_device_disband_start(cdev);
423                 return;
424         }
425         if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
426                 return;
427         sch = to_subchannel(cdev->dev.parent);
428         ret = __ccw_device_check_pgid(cdev);
429         memset(&cdev->private->irb, 0, sizeof(struct irb));
430         switch (ret) {
431         /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
432         case 0:                 /* disband successful. */
433                 sch->vpm = 0;
434                 ccw_device_disband_done(cdev, ret);
435                 break;
436         case -EOPNOTSUPP:
437                 /*
438                  * One of those strange devices which claim to be able
439                  * to do multipathing but not for Unset Path Group ID.
440                  */
441                 cdev->private->flags.pgid_single = 1;
442                 /* fall through. */
443         case -EAGAIN:           /* Try again. */
444                 __ccw_device_disband_start(cdev);
445                 break;
446         case -ETIME:            /* Set path group id stopped by timeout. */
447                 ccw_device_disband_done(cdev, -ETIME);
448                 break;
449         case -EACCES:           /* channel is not operational. */
450                 cdev->private->imask >>= 1;
451                 cdev->private->iretry = 5;
452                 __ccw_device_disband_start(cdev);
453                 break;
454         }
455 }
456
457 void
458 ccw_device_disband_start(struct ccw_device *cdev)
459 {
460         cdev->private->flags.pgid_single = 0;
461         cdev->private->iretry = 5;
462         cdev->private->imask = 0x80;
463         __ccw_device_disband_start(cdev);
464 }