diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/s390/cio/device_status.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/s390/cio/device_status.c')
-rw-r--r-- | drivers/s390/cio/device_status.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c new file mode 100644 index 000000000000..4ab2e0d95009 --- /dev/null +++ b/drivers/s390/cio/device_status.c | |||
@@ -0,0 +1,385 @@ | |||
1 | /* | ||
2 | * drivers/s390/cio/device_status.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 | * Status accumulation and basic sense 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 | |||
19 | #include "cio.h" | ||
20 | #include "cio_debug.h" | ||
21 | #include "css.h" | ||
22 | #include "device.h" | ||
23 | #include "ioasm.h" | ||
24 | |||
25 | /* | ||
26 | * Check for any kind of channel or interface control check but don't | ||
27 | * issue the message for the console device | ||
28 | */ | ||
29 | static inline void | ||
30 | ccw_device_msg_control_check(struct ccw_device *cdev, struct irb *irb) | ||
31 | { | ||
32 | if (!(irb->scsw.cstat & (SCHN_STAT_CHN_DATA_CHK | | ||
33 | SCHN_STAT_CHN_CTRL_CHK | | ||
34 | SCHN_STAT_INTF_CTRL_CHK))) | ||
35 | return; | ||
36 | |||
37 | CIO_MSG_EVENT(0, "Channel-Check or Interface-Control-Check " | ||
38 | "received" | ||
39 | " ... device %04X on subchannel %04X, dev_stat " | ||
40 | ": %02X sch_stat : %02X\n", | ||
41 | cdev->private->devno, cdev->private->irq, | ||
42 | cdev->private->irb.scsw.dstat, | ||
43 | cdev->private->irb.scsw.cstat); | ||
44 | |||
45 | if (irb->scsw.cc != 3) { | ||
46 | char dbf_text[15]; | ||
47 | |||
48 | sprintf(dbf_text, "chk%x", cdev->private->irq); | ||
49 | CIO_TRACE_EVENT(0, dbf_text); | ||
50 | CIO_HEX_EVENT(0, &cdev->private->irb, sizeof (struct irb)); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | /* | ||
55 | * Some paths became not operational (pno bit in scsw is set). | ||
56 | */ | ||
57 | static void | ||
58 | ccw_device_path_notoper(struct ccw_device *cdev) | ||
59 | { | ||
60 | struct subchannel *sch; | ||
61 | |||
62 | sch = to_subchannel(cdev->dev.parent); | ||
63 | stsch (sch->irq, &sch->schib); | ||
64 | |||
65 | CIO_MSG_EVENT(0, "%s(%04x) - path(s) %02x are " | ||
66 | "not operational \n", __FUNCTION__, sch->irq, | ||
67 | sch->schib.pmcw.pnom); | ||
68 | |||
69 | sch->lpm &= ~sch->schib.pmcw.pnom; | ||
70 | if (cdev->private->options.pgroup) | ||
71 | cdev->private->flags.doverify = 1; | ||
72 | } | ||
73 | |||
74 | /* | ||
75 | * Copy valid bits from the extended control word to device irb. | ||
76 | */ | ||
77 | static inline void | ||
78 | ccw_device_accumulate_ecw(struct ccw_device *cdev, struct irb *irb) | ||
79 | { | ||
80 | /* | ||
81 | * Copy extended control bit if it is valid... yes there | ||
82 | * are condition that have to be met for the extended control | ||
83 | * bit to have meaning. Sick. | ||
84 | */ | ||
85 | cdev->private->irb.scsw.ectl = 0; | ||
86 | if ((irb->scsw.stctl & SCSW_STCTL_ALERT_STATUS) && | ||
87 | !(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS)) | ||
88 | cdev->private->irb.scsw.ectl = irb->scsw.ectl; | ||
89 | /* Check if extended control word is valid. */ | ||
90 | if (!cdev->private->irb.scsw.ectl) | ||
91 | return; | ||
92 | /* Copy concurrent sense / model dependent information. */ | ||
93 | memcpy (&cdev->private->irb.ecw, irb->ecw, sizeof (irb->ecw)); | ||
94 | } | ||
95 | |||
96 | /* | ||
97 | * Check if extended status word is valid. | ||
98 | */ | ||
99 | static inline int | ||
100 | ccw_device_accumulate_esw_valid(struct irb *irb) | ||
101 | { | ||
102 | if (!irb->scsw.eswf && irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) | ||
103 | return 0; | ||
104 | if (irb->scsw.stctl == | ||
105 | (SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) && | ||
106 | !(irb->scsw.actl & SCSW_ACTL_SUSPENDED)) | ||
107 | return 0; | ||
108 | return 1; | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * Copy valid bits from the extended status word to device irb. | ||
113 | */ | ||
114 | static inline void | ||
115 | ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb) | ||
116 | { | ||
117 | struct irb *cdev_irb; | ||
118 | struct sublog *cdev_sublog, *sublog; | ||
119 | |||
120 | if (!ccw_device_accumulate_esw_valid(irb)) | ||
121 | return; | ||
122 | |||
123 | cdev_irb = &cdev->private->irb; | ||
124 | |||
125 | /* Copy last path used mask. */ | ||
126 | cdev_irb->esw.esw1.lpum = irb->esw.esw1.lpum; | ||
127 | |||
128 | /* Copy subchannel logout information if esw is of format 0. */ | ||
129 | if (irb->scsw.eswf) { | ||
130 | cdev_sublog = &cdev_irb->esw.esw0.sublog; | ||
131 | sublog = &irb->esw.esw0.sublog; | ||
132 | /* Copy extended status flags. */ | ||
133 | cdev_sublog->esf = sublog->esf; | ||
134 | /* | ||
135 | * Copy fields that have a meaning for channel data check | ||
136 | * channel control check and interface control check. | ||
137 | */ | ||
138 | if (irb->scsw.cstat & (SCHN_STAT_CHN_DATA_CHK | | ||
139 | SCHN_STAT_CHN_CTRL_CHK | | ||
140 | SCHN_STAT_INTF_CTRL_CHK)) { | ||
141 | /* Copy ancillary report bit. */ | ||
142 | cdev_sublog->arep = sublog->arep; | ||
143 | /* Copy field-validity-flags. */ | ||
144 | cdev_sublog->fvf = sublog->fvf; | ||
145 | /* Copy storage access code. */ | ||
146 | cdev_sublog->sacc = sublog->sacc; | ||
147 | /* Copy termination code. */ | ||
148 | cdev_sublog->termc = sublog->termc; | ||
149 | /* Copy sequence code. */ | ||
150 | cdev_sublog->seqc = sublog->seqc; | ||
151 | } | ||
152 | /* Copy device status check. */ | ||
153 | cdev_sublog->devsc = sublog->devsc; | ||
154 | /* Copy secondary error. */ | ||
155 | cdev_sublog->serr = sublog->serr; | ||
156 | /* Copy i/o-error alert. */ | ||
157 | cdev_sublog->ioerr = sublog->ioerr; | ||
158 | /* Copy channel path timeout bit. */ | ||
159 | if (irb->scsw.cstat & SCHN_STAT_INTF_CTRL_CHK) | ||
160 | cdev_irb->esw.esw0.erw.cpt = irb->esw.esw0.erw.cpt; | ||
161 | /* Copy failing storage address validity flag. */ | ||
162 | cdev_irb->esw.esw0.erw.fsavf = irb->esw.esw0.erw.fsavf; | ||
163 | if (cdev_irb->esw.esw0.erw.fsavf) { | ||
164 | /* ... and copy the failing storage address. */ | ||
165 | memcpy(cdev_irb->esw.esw0.faddr, irb->esw.esw0.faddr, | ||
166 | sizeof (irb->esw.esw0.faddr)); | ||
167 | /* ... and copy the failing storage address format. */ | ||
168 | cdev_irb->esw.esw0.erw.fsaf = irb->esw.esw0.erw.fsaf; | ||
169 | } | ||
170 | /* Copy secondary ccw address validity bit. */ | ||
171 | cdev_irb->esw.esw0.erw.scavf = irb->esw.esw0.erw.scavf; | ||
172 | if (irb->esw.esw0.erw.scavf) | ||
173 | /* ... and copy the secondary ccw address. */ | ||
174 | cdev_irb->esw.esw0.saddr = irb->esw.esw0.saddr; | ||
175 | |||
176 | } | ||
177 | /* FIXME: DCTI for format 2? */ | ||
178 | |||
179 | /* Copy authorization bit. */ | ||
180 | cdev_irb->esw.esw0.erw.auth = irb->esw.esw0.erw.auth; | ||
181 | /* Copy path verification required flag. */ | ||
182 | cdev_irb->esw.esw0.erw.pvrf = irb->esw.esw0.erw.pvrf; | ||
183 | if (irb->esw.esw0.erw.pvrf && cdev->private->options.pgroup) | ||
184 | cdev->private->flags.doverify = 1; | ||
185 | /* Copy concurrent sense bit. */ | ||
186 | cdev_irb->esw.esw0.erw.cons = irb->esw.esw0.erw.cons; | ||
187 | if (irb->esw.esw0.erw.cons) | ||
188 | cdev_irb->esw.esw0.erw.scnt = irb->esw.esw0.erw.scnt; | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * Accumulate status from irb to devstat. | ||
193 | */ | ||
194 | void | ||
195 | ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb) | ||
196 | { | ||
197 | struct irb *cdev_irb; | ||
198 | |||
199 | /* | ||
200 | * Check if the status pending bit is set in stctl. | ||
201 | * If not, the remaining bit have no meaning and we must ignore them. | ||
202 | * The esw is not meaningful as well... | ||
203 | */ | ||
204 | if (!(irb->scsw.stctl & SCSW_STCTL_STATUS_PEND)) | ||
205 | return; | ||
206 | |||
207 | /* Check for channel checks and interface control checks. */ | ||
208 | ccw_device_msg_control_check(cdev, irb); | ||
209 | |||
210 | /* Check for path not operational. */ | ||
211 | if (irb->scsw.pno && irb->scsw.fctl != 0 && | ||
212 | (!(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS) || | ||
213 | (irb->scsw.actl & SCSW_ACTL_SUSPENDED))) | ||
214 | ccw_device_path_notoper(cdev); | ||
215 | |||
216 | /* | ||
217 | * Don't accumulate unsolicited interrupts. | ||
218 | */ | ||
219 | if ((irb->scsw.stctl == | ||
220 | (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) && | ||
221 | (!irb->scsw.cc)) | ||
222 | return; | ||
223 | |||
224 | cdev_irb = &cdev->private->irb; | ||
225 | |||
226 | /* Copy bits which are valid only for the start function. */ | ||
227 | if (irb->scsw.fctl & SCSW_FCTL_START_FUNC) { | ||
228 | /* Copy key. */ | ||
229 | cdev_irb->scsw.key = irb->scsw.key; | ||
230 | /* Copy suspend control bit. */ | ||
231 | cdev_irb->scsw.sctl = irb->scsw.sctl; | ||
232 | /* Accumulate deferred condition code. */ | ||
233 | cdev_irb->scsw.cc |= irb->scsw.cc; | ||
234 | /* Copy ccw format bit. */ | ||
235 | cdev_irb->scsw.fmt = irb->scsw.fmt; | ||
236 | /* Copy prefetch bit. */ | ||
237 | cdev_irb->scsw.pfch = irb->scsw.pfch; | ||
238 | /* Copy initial-status-interruption-control. */ | ||
239 | cdev_irb->scsw.isic = irb->scsw.isic; | ||
240 | /* Copy address limit checking control. */ | ||
241 | cdev_irb->scsw.alcc = irb->scsw.alcc; | ||
242 | /* Copy suppress suspend bit. */ | ||
243 | cdev_irb->scsw.ssi = irb->scsw.ssi; | ||
244 | } | ||
245 | |||
246 | /* Take care of the extended control bit and extended control word. */ | ||
247 | ccw_device_accumulate_ecw(cdev, irb); | ||
248 | |||
249 | /* Accumulate function control. */ | ||
250 | cdev_irb->scsw.fctl |= irb->scsw.fctl; | ||
251 | /* Copy activity control. */ | ||
252 | cdev_irb->scsw.actl= irb->scsw.actl; | ||
253 | /* Accumulate status control. */ | ||
254 | cdev_irb->scsw.stctl |= irb->scsw.stctl; | ||
255 | /* | ||
256 | * Copy ccw address if it is valid. This is a bit simplified | ||
257 | * but should be close enough for all practical purposes. | ||
258 | */ | ||
259 | if ((irb->scsw.stctl & SCSW_STCTL_PRIM_STATUS) || | ||
260 | ((irb->scsw.stctl == | ||
261 | (SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND)) && | ||
262 | (irb->scsw.actl & SCSW_ACTL_DEVACT) && | ||
263 | (irb->scsw.actl & SCSW_ACTL_SCHACT)) || | ||
264 | (irb->scsw.actl & SCSW_ACTL_SUSPENDED)) | ||
265 | cdev_irb->scsw.cpa = irb->scsw.cpa; | ||
266 | /* Accumulate device status, but not the device busy flag. */ | ||
267 | cdev_irb->scsw.dstat &= ~DEV_STAT_BUSY; | ||
268 | cdev_irb->scsw.dstat |= irb->scsw.dstat; | ||
269 | /* Accumulate subchannel status. */ | ||
270 | cdev_irb->scsw.cstat |= irb->scsw.cstat; | ||
271 | /* Copy residual count if it is valid. */ | ||
272 | if ((irb->scsw.stctl & SCSW_STCTL_PRIM_STATUS) && | ||
273 | (irb->scsw.cstat & ~(SCHN_STAT_PCI | SCHN_STAT_INCORR_LEN)) == 0) | ||
274 | cdev_irb->scsw.count = irb->scsw.count; | ||
275 | |||
276 | /* Take care of bits in the extended status word. */ | ||
277 | ccw_device_accumulate_esw(cdev, irb); | ||
278 | |||
279 | /* | ||
280 | * Check whether we must issue a SENSE CCW ourselves if there is no | ||
281 | * concurrent sense facility installed for the subchannel. | ||
282 | * No sense is required if no delayed sense is pending | ||
283 | * and we did not get a unit check without sense information. | ||
284 | * | ||
285 | * Note: We should check for ioinfo[irq]->flags.consns but VM | ||
286 | * violates the ESA/390 architecture and doesn't present an | ||
287 | * operand exception for virtual devices without concurrent | ||
288 | * sense facility available/supported when enabling the | ||
289 | * concurrent sense facility. | ||
290 | */ | ||
291 | if ((cdev_irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && | ||
292 | !(cdev_irb->esw.esw0.erw.cons)) | ||
293 | cdev->private->flags.dosense = 1; | ||
294 | } | ||
295 | |||
296 | /* | ||
297 | * Do a basic sense. | ||
298 | */ | ||
299 | int | ||
300 | ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb) | ||
301 | { | ||
302 | struct subchannel *sch; | ||
303 | |||
304 | sch = to_subchannel(cdev->dev.parent); | ||
305 | |||
306 | /* A sense is required, can we do it now ? */ | ||
307 | if ((irb->scsw.actl & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0) | ||
308 | /* | ||
309 | * we received an Unit Check but we have no final | ||
310 | * status yet, therefore we must delay the SENSE | ||
311 | * processing. We must not report this intermediate | ||
312 | * status to the device interrupt handler. | ||
313 | */ | ||
314 | return -EBUSY; | ||
315 | |||
316 | /* | ||
317 | * We have ending status but no sense information. Do a basic sense. | ||
318 | */ | ||
319 | sch = to_subchannel(cdev->dev.parent); | ||
320 | sch->sense_ccw.cmd_code = CCW_CMD_BASIC_SENSE; | ||
321 | sch->sense_ccw.cda = (__u32) __pa(cdev->private->irb.ecw); | ||
322 | sch->sense_ccw.count = SENSE_MAX_COUNT; | ||
323 | sch->sense_ccw.flags = CCW_FLAG_SLI; | ||
324 | |||
325 | return cio_start (sch, &sch->sense_ccw, 0xff); | ||
326 | } | ||
327 | |||
328 | /* | ||
329 | * Add information from basic sense to devstat. | ||
330 | */ | ||
331 | void | ||
332 | ccw_device_accumulate_basic_sense(struct ccw_device *cdev, struct irb *irb) | ||
333 | { | ||
334 | /* | ||
335 | * Check if the status pending bit is set in stctl. | ||
336 | * If not, the remaining bit have no meaning and we must ignore them. | ||
337 | * The esw is not meaningful as well... | ||
338 | */ | ||
339 | if (!(irb->scsw.stctl & SCSW_STCTL_STATUS_PEND)) | ||
340 | return; | ||
341 | |||
342 | /* Check for channel checks and interface control checks. */ | ||
343 | ccw_device_msg_control_check(cdev, irb); | ||
344 | |||
345 | /* Check for path not operational. */ | ||
346 | if (irb->scsw.pno && irb->scsw.fctl != 0 && | ||
347 | (!(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS) || | ||
348 | (irb->scsw.actl & SCSW_ACTL_SUSPENDED))) | ||
349 | ccw_device_path_notoper(cdev); | ||
350 | |||
351 | if (!(irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && | ||
352 | (irb->scsw.dstat & DEV_STAT_CHN_END)) { | ||
353 | cdev->private->irb.esw.esw0.erw.cons = 1; | ||
354 | cdev->private->flags.dosense = 0; | ||
355 | } | ||
356 | /* Check if path verification is required. */ | ||
357 | if (ccw_device_accumulate_esw_valid(irb) && | ||
358 | irb->esw.esw0.erw.pvrf && cdev->private->options.pgroup) | ||
359 | cdev->private->flags.doverify = 1; | ||
360 | } | ||
361 | |||
362 | /* | ||
363 | * This function accumulates the status into the private devstat and | ||
364 | * starts a basic sense if one is needed. | ||
365 | */ | ||
366 | int | ||
367 | ccw_device_accumulate_and_sense(struct ccw_device *cdev, struct irb *irb) | ||
368 | { | ||
369 | ccw_device_accumulate_irb(cdev, irb); | ||
370 | if ((irb->scsw.actl & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0) | ||
371 | return -EBUSY; | ||
372 | /* Check for basic sense. */ | ||
373 | if (cdev->private->flags.dosense && | ||
374 | !(irb->scsw.dstat & DEV_STAT_UNIT_CHECK)) { | ||
375 | cdev->private->irb.esw.esw0.erw.cons = 1; | ||
376 | cdev->private->flags.dosense = 0; | ||
377 | return 0; | ||
378 | } | ||
379 | if (cdev->private->flags.dosense) { | ||
380 | ccw_device_do_sense(cdev, irb); | ||
381 | return -EBUSY; | ||
382 | } | ||
383 | return 0; | ||
384 | } | ||
385 | |||