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