[PATCH] s390: convert /proc/cio_ignore
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / s390 / cio / device_pgid.c
CommitLineData
1da177e4
LT
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"
6810a2bc 25#include "ioasm.h"
1da177e4
LT
26
27/*
28 * Start Sense Path Group ID helper function. Used in ccw_device_recog
29 * and ccw_device_sense_pgid.
30 */
31static 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",
a8237fc4 62 cdev->private->devno, sch->schid.sch_no,
1da177e4
LT
63 cdev->private->imask);
64
65 }
66 cdev->private->imask >>= 1;
67 cdev->private->iretry = 5;
68 }
69 return ret;
70}
71
72void
73ccw_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 */
90static 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",
a8237fc4
CH
124 cdev->private->devno, sch->schid.sch_no,
125 sch->orb.lpm);
1da177e4
LT
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",
a8237fc4 131 cdev->private->devno, sch->schid.sch_no);
1da177e4
LT
132 return -EUSERS;
133 }
134 return 0;
135}
136
137/*
138 * Got interrupt for Sense Path Group ID.
139 */
140void
141ccw_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)
a28c6944 167 memcpy(&cdev->private->pgid, &css[0]->global_pgid,
1da177e4
LT
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 */
196static 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",
a8237fc4 241 cdev->private->devno, sch->schid.sch_no, cdev->private->imask);
1da177e4
LT
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 */
249static 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",
a8237fc4 275 cdev->private->devno, sch->schid.sch_no,
1da177e4
LT
276 cdev->private->imask);
277 return -EACCES;
278 }
279 return 0;
280}
281
282static 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 */
309void
310ccw_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
366void
367ccw_device_verify_start(struct ccw_device *cdev)
368{
6810a2bc
CH
369 struct subchannel *sch = to_subchannel(cdev->dev.parent);
370
1da177e4
LT
371 cdev->private->flags.pgid_single = 0;
372 cdev->private->iretry = 5;
6810a2bc
CH
373 /*
374 * Update sch->lpm with current values to catch paths becoming
375 * available again.
376 */
a8237fc4 377 if (stsch(sch->schid, &sch->schib)) {
6810a2bc
CH
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;
1da177e4
LT
385 __ccw_device_verify_start(cdev);
386}
387
388static 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 */
410void
411ccw_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
457void
458ccw_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}