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