aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/device_id.c
diff options
context:
space:
mode:
authorPeter Oberparleiter <peter.oberparleiter@de.ibm.com>2009-12-07 06:51:26 -0500
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2009-12-07 06:51:31 -0500
commit39f5360b3d68a8e96d280481d9c442e7c005c317 (patch)
tree47d22cf70fffc21959e0728c06279913a863c028 /drivers/s390/cio/device_id.c
parente1f0fbd655539b0093738f58d57db83a0ac2dd6c (diff)
[S390] cio: use ccw request infrastructure for sense id
Use the newly introduced ccw request infrastructure to implement the sense id operation. Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/device_id.c')
-rw-r--r--drivers/s390/cio/device_id.c375
1 files changed, 137 insertions, 238 deletions
diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c
index 1bdaa614e34f..4728644ed85c 100644
--- a/drivers/s390/cio/device_id.c
+++ b/drivers/s390/cio/device_id.c
@@ -1,40 +1,39 @@
1/* 1/*
2 * drivers/s390/cio/device_id.c 2 * CCW device SENSE ID I/O handling.
3 * 3 *
4 * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, 4 * Copyright IBM Corp. 2002,2009
5 * IBM Corporation 5 * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
6 * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) 6 * Martin Schwidefsky <schwidefsky@de.ibm.com>
7 * Martin Schwidefsky (schwidefsky@de.ibm.com) 7 * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
8 *
9 * Sense ID functions.
10 */ 8 */
11 9
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/kernel.h> 10#include <linux/kernel.h>
15 11#include <linux/string.h>
12#include <linux/types.h>
13#include <linux/errno.h>
16#include <asm/ccwdev.h> 14#include <asm/ccwdev.h>
17#include <asm/delay.h> 15#include <asm/setup.h>
18#include <asm/cio.h> 16#include <asm/cio.h>
19#include <asm/lowcore.h>
20#include <asm/diag.h> 17#include <asm/diag.h>
21 18
22#include "cio.h" 19#include "cio.h"
23#include "cio_debug.h" 20#include "cio_debug.h"
24#include "css.h"
25#include "device.h" 21#include "device.h"
26#include "ioasm.h"
27#include "io_sch.h" 22#include "io_sch.h"
28 23
24#define SENSE_ID_RETRIES 5
25#define SENSE_ID_TIMEOUT (10 * HZ)
26#define SENSE_ID_MIN_LEN 4
27#define SENSE_ID_BASIC_LEN 7
28
29/** 29/**
30 * vm_vdev_to_cu_type - Convert vm virtual device into control unit type 30 * diag210_to_senseid - convert diag 0x210 data to sense id information
31 * for certain devices. 31 * @senseid: sense id
32 * @class: virtual device class 32 * @diag: diag 0x210 data
33 * @type: virtual device type
34 * 33 *
35 * Returns control unit type if a match was made or %0xffff otherwise. 34 * Return 0 on success, non-zero otherwise.
36 */ 35 */
37static int vm_vdev_to_cu_type(int class, int type) 36static int diag210_to_senseid(struct senseid *senseid, struct diag210 *diag)
38{ 37{
39 static struct { 38 static struct {
40 int class, type, cu_type; 39 int class, type, cu_type;
@@ -71,253 +70,153 @@ static int vm_vdev_to_cu_type(int class, int type)
71 }; 70 };
72 int i; 71 int i;
73 72
74 for (i = 0; i < ARRAY_SIZE(vm_devices); i++) 73 /* Special case for osa devices. */
75 if (class == vm_devices[i].class && type == vm_devices[i].type) 74 if (diag->vrdcvcla == 0x02 && diag->vrdcvtyp == 0x20) {
76 return vm_devices[i].cu_type; 75 senseid->cu_type = 0x3088;
76 senseid->cu_model = 0x60;
77 senseid->reserved = 0xff;
78 return 0;
79 }
80 for (i = 0; i < ARRAY_SIZE(vm_devices); i++) {
81 if (diag->vrdcvcla == vm_devices[i].class &&
82 diag->vrdcvtyp == vm_devices[i].type) {
83 senseid->cu_type = vm_devices[i].cu_type;
84 senseid->reserved = 0xff;
85 return 0;
86 }
87 }
77 88
78 return 0xffff; 89 return -ENODEV;
79} 90}
80 91
81/** 92/**
82 * diag_get_dev_info - retrieve device information via DIAG X'210' 93 * diag_get_dev_info - retrieve device information via diag 0x210
83 * @devno: device number 94 * @cdev: ccw device
84 * @ps: pointer to sense ID data area
85 * 95 *
86 * Returns zero on success, non-zero otherwise. 96 * Returns zero on success, non-zero otherwise.
87 */ 97 */
88static int diag_get_dev_info(u16 devno, struct senseid *ps) 98static int diag210_get_dev_info(struct ccw_device *cdev)
89{ 99{
100 struct ccw_dev_id *dev_id = &cdev->private->dev_id;
101 struct senseid *senseid = &cdev->private->senseid;
90 struct diag210 diag_data; 102 struct diag210 diag_data;
91 int ccode; 103 int rc;
92 104
93 CIO_TRACE_EVENT (4, "VMvdinf"); 105 if (dev_id->ssid != 0)
94 106 return -ENODEV;
95 diag_data = (struct diag210) { 107 memset(&diag_data, 0, sizeof(diag_data));
96 .vrdcdvno = devno, 108 diag_data.vrdcdvno = dev_id->devno;
97 .vrdclen = sizeof (diag_data), 109 diag_data.vrdclen = sizeof(diag_data);
98 }; 110 rc = diag210(&diag_data);
99 111 CIO_TRACE_EVENT(4, "diag210");
100 ccode = diag210 (&diag_data); 112 CIO_HEX_EVENT(4, &rc, sizeof(rc));
101 if ((ccode == 0) || (ccode == 2)) { 113 CIO_HEX_EVENT(4, &diag_data, sizeof(diag_data));
102 ps->reserved = 0xff; 114 if (rc != 0 && rc != 2)
103 115 goto err_failed;
104 /* Special case for osa devices. */ 116 if (diag210_to_senseid(senseid, &diag_data))
105 if (diag_data.vrdcvcla == 0x02 && diag_data.vrdcvtyp == 0x20) { 117 goto err_unknown;
106 ps->cu_type = 0x3088; 118 return 0;
107 ps->cu_model = 0x60; 119
108 return 0; 120err_unknown:
109 } 121 CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: unknown diag210 data\n",
110 ps->cu_type = vm_vdev_to_cu_type(diag_data.vrdcvcla, 122 dev_id->ssid, dev_id->devno);
111 diag_data.vrdcvtyp); 123 return -ENODEV;
112 if (ps->cu_type != 0xffff) 124err_failed:
113 return 0; 125 CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: diag210 failed (rc=%d)\n",
114 } 126 dev_id->ssid, dev_id->devno, rc);
115
116 CIO_MSG_EVENT(0, "DIAG X'210' for device %04X returned (cc = %d):"
117 "vdev class : %02X, vdev type : %04X \n ... "
118 "rdev class : %02X, rdev type : %04X, "
119 "rdev model: %02X\n",
120 devno, ccode,
121 diag_data.vrdcvcla, diag_data.vrdcvtyp,
122 diag_data.vrdcrccl, diag_data.vrdccrty,
123 diag_data.vrdccrmd);
124
125 return -ENODEV; 127 return -ENODEV;
126} 128}
127 129
128/* 130/*
129 * Start Sense ID helper function. 131 * Initialize SENSE ID data.
130 * Try to obtain the 'control unit'/'device type' information
131 * associated with the subchannel.
132 */ 132 */
133static int 133static void snsid_init(struct ccw_device *cdev)
134__ccw_device_sense_id_start(struct ccw_device *cdev)
135{
136 struct subchannel *sch;
137 struct ccw1 *ccw;
138 int ret;
139
140 sch = to_subchannel(cdev->dev.parent);
141 /* Setup sense channel program. */
142 ccw = cdev->private->iccws;
143 ccw->cmd_code = CCW_CMD_SENSE_ID;
144 ccw->cda = (__u32) __pa (&cdev->private->senseid);
145 ccw->count = sizeof (struct senseid);
146 ccw->flags = CCW_FLAG_SLI;
147
148 /* Reset device status. */
149 memset(&cdev->private->irb, 0, sizeof(struct irb));
150
151 /* Try on every path. */
152 ret = -ENODEV;
153 while (cdev->private->imask != 0) {
154 cdev->private->senseid.cu_type = 0xFFFF;
155 if ((sch->opm & cdev->private->imask) != 0 &&
156 cdev->private->iretry > 0) {
157 cdev->private->iretry--;
158 /* Reset internal retry indication. */
159 cdev->private->flags.intretry = 0;
160 ret = cio_start (sch, cdev->private->iccws,
161 cdev->private->imask);
162 /* ret is 0, -EBUSY, -EACCES or -ENODEV */
163 if (ret != -EACCES)
164 return ret;
165 }
166 cdev->private->imask >>= 1;
167 cdev->private->iretry = 5;
168 }
169 return ret;
170}
171
172void
173ccw_device_sense_id_start(struct ccw_device *cdev)
174{ 134{
175 int ret; 135 cdev->private->flags.esid = 0;
176 136 memset(&cdev->private->senseid, 0, sizeof(cdev->private->senseid));
177 memset (&cdev->private->senseid, 0, sizeof (struct senseid)); 137 cdev->private->senseid.cu_type = 0xffff;
178 cdev->private->imask = 0x80;
179 cdev->private->iretry = 5;
180 ret = __ccw_device_sense_id_start(cdev);
181 if (ret && ret != -EBUSY)
182 ccw_device_sense_id_done(cdev, ret);
183} 138}
184 139
185/* 140/*
186 * Called from interrupt context to check if a valid answer 141 * Check for complete SENSE ID data.
187 * to Sense ID was received.
188 */ 142 */
189static int 143static int snsid_check(struct ccw_device *cdev, void *data)
190ccw_device_check_sense_id(struct ccw_device *cdev)
191{ 144{
192 struct subchannel *sch; 145 struct cmd_scsw *scsw = &cdev->private->irb.scsw.cmd;
193 struct irb *irb; 146 int len = sizeof(struct senseid) - scsw->count;
194 147
195 sch = to_subchannel(cdev->dev.parent); 148 /* Check for incomplete SENSE ID data. */
196 irb = &cdev->private->irb; 149 if (len < SENSE_ID_MIN_LEN)
197 150 goto out_restart;
198 /* Check the error cases. */ 151 if (cdev->private->senseid.cu_type == 0xffff)
199 if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { 152 goto out_restart;
200 /* Retry Sense ID if requested. */ 153 /* Check for incompatible SENSE ID data. */
201 if (cdev->private->flags.intretry) { 154 if (cdev->private->senseid.reserved != 0xff)
202 cdev->private->flags.intretry = 0;
203 return -EAGAIN;
204 }
205 return -ETIME;
206 }
207 if (irb->esw.esw0.erw.cons && (irb->ecw[0] & SNS0_CMD_REJECT)) {
208 /*
209 * if the device doesn't support the SenseID
210 * command further retries wouldn't help ...
211 * NB: We don't check here for intervention required like we
212 * did before, because tape devices with no tape inserted
213 * may present this status *in conjunction with* the
214 * sense id information. So, for intervention required,
215 * we use the "whack it until it talks" strategy...
216 */
217 CIO_MSG_EVENT(0, "SenseID : device %04x on Subchannel "
218 "0.%x.%04x reports cmd reject\n",
219 cdev->private->dev_id.devno, sch->schid.ssid,
220 sch->schid.sch_no);
221 return -EOPNOTSUPP; 155 return -EOPNOTSUPP;
222 } 156 /* Check for extended-identification information. */
223 if (irb->esw.esw0.erw.cons) { 157 if (len > SENSE_ID_BASIC_LEN)
224 CIO_MSG_EVENT(2, "SenseID : UC on dev 0.%x.%04x, " 158 cdev->private->flags.esid = 1;
225 "lpum %02X, cnt %02d, sns :" 159 return 0;
226 " %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
227 cdev->private->dev_id.ssid,
228 cdev->private->dev_id.devno,
229 irb->esw.esw0.sublog.lpum,
230 irb->esw.esw0.erw.scnt,
231 irb->ecw[0], irb->ecw[1],
232 irb->ecw[2], irb->ecw[3],
233 irb->ecw[4], irb->ecw[5],
234 irb->ecw[6], irb->ecw[7]);
235 return -EAGAIN;
236 }
237 if (irb->scsw.cmd.cc == 3) {
238 u8 lpm;
239 160
240 lpm = to_io_private(sch)->orb.cmd.lpm; 161out_restart:
241 if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0) 162 snsid_init(cdev);
242 CIO_MSG_EVENT(4, "SenseID : path %02X for device %04x "
243 "on subchannel 0.%x.%04x is "
244 "'not operational'\n", lpm,
245 cdev->private->dev_id.devno,
246 sch->schid.ssid, sch->schid.sch_no);
247 return -EACCES;
248 }
249
250 /* Did we get a proper answer ? */
251 if (irb->scsw.cmd.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF &&
252 cdev->private->senseid.reserved == 0xFF) {
253 if (irb->scsw.cmd.count < sizeof(struct senseid) - 8)
254 cdev->private->flags.esid = 1;
255 return 0; /* Success */
256 }
257
258 /* Hmm, whatever happened, try again. */
259 CIO_MSG_EVENT(2, "SenseID : start_IO() for device %04x on "
260 "subchannel 0.%x.%04x returns status %02X%02X\n",
261 cdev->private->dev_id.devno, sch->schid.ssid,
262 sch->schid.sch_no,
263 irb->scsw.cmd.dstat, irb->scsw.cmd.cstat);
264 return -EAGAIN; 163 return -EAGAIN;
265} 164}
266 165
267/* 166/*
268 * Got interrupt for Sense ID. 167 * Process SENSE ID request result.
269 */ 168 */
270void 169static void snsid_callback(struct ccw_device *cdev, void *data, int rc)
271ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event)
272{ 170{
273 struct subchannel *sch; 171 struct ccw_dev_id *id = &cdev->private->dev_id;
274 struct irb *irb; 172 struct senseid *senseid = &cdev->private->senseid;
275 int ret; 173 int vm = 0;
276 174
277 sch = to_subchannel(cdev->dev.parent); 175 if (rc && MACHINE_IS_VM) {
278 irb = (struct irb *) __LC_IRB; 176 /* Try diag 0x210 fallback on z/VM. */
279 /* Retry sense id, if needed. */ 177 snsid_init(cdev);
280 if (irb->scsw.cmd.stctl == 178 if (diag210_get_dev_info(cdev) == 0) {
281 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { 179 rc = 0;
282 if ((irb->scsw.cmd.cc == 1) || !irb->scsw.cmd.actl) { 180 vm = 1;
283 ret = __ccw_device_sense_id_start(cdev);
284 if (ret && ret != -EBUSY)
285 ccw_device_sense_id_done(cdev, ret);
286 } 181 }
287 return;
288 } 182 }
289 if (ccw_device_accumulate_and_sense(cdev, irb) != 0) 183 CIO_MSG_EVENT(2, "snsid: device 0.%x.%04x: rc=%d %04x/%02x "
290 return; 184 "%04x/%02x%s\n", id->ssid, id->devno, rc,
291 ret = ccw_device_check_sense_id(cdev); 185 senseid->cu_type, senseid->cu_model, senseid->dev_type,
292 memset(&cdev->private->irb, 0, sizeof(struct irb)); 186 senseid->dev_model, vm ? " (diag210)" : "");
293 switch (ret) { 187 ccw_device_sense_id_done(cdev, rc);
294 /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN or -EACCES */ 188}
295 case 0: /* Sense id succeeded. */
296 case -ETIME: /* Sense id stopped by timeout. */
297 ccw_device_sense_id_done(cdev, ret);
298 break;
299 case -EACCES: /* channel is not operational. */
300 sch->lpm &= ~cdev->private->imask;
301 cdev->private->imask >>= 1;
302 cdev->private->iretry = 5;
303 /* fall through. */
304 case -EAGAIN: /* try again. */
305 ret = __ccw_device_sense_id_start(cdev);
306 if (ret == 0 || ret == -EBUSY)
307 break;
308 /* fall through. */
309 default: /* Sense ID failed. Try asking VM. */
310 if (MACHINE_IS_VM)
311 ret = diag_get_dev_info(cdev->private->dev_id.devno,
312 &cdev->private->senseid);
313 else
314 /*
315 * If we can't couldn't identify the device type we
316 * consider the device "not operational".
317 */
318 ret = -ENODEV;
319 189
320 ccw_device_sense_id_done(cdev, ret); 190/**
321 break; 191 * ccw_device_sense_id_start - perform SENSE ID
322 } 192 * @cdev: ccw device
193 *
194 * Execute a SENSE ID channel program on @cdev to update its sense id
195 * information. When finished, call ccw_device_sense_id_done with a
196 * return code specifying the result.
197 */
198void ccw_device_sense_id_start(struct ccw_device *cdev)
199{
200 struct subchannel *sch = to_subchannel(cdev->dev.parent);
201 struct ccw_request *req = &cdev->private->req;
202 struct ccw1 *cp = cdev->private->iccws;
203
204 CIO_TRACE_EVENT(4, "snsid");
205 CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
206 /* Data setup. */
207 snsid_init(cdev);
208 /* Channel program setup. */
209 cp->cmd_code = CCW_CMD_SENSE_ID;
210 cp->cda = (u32) (addr_t) &cdev->private->senseid;
211 cp->count = sizeof(struct senseid);
212 cp->flags = CCW_FLAG_SLI;
213 /* Request setup. */
214 memset(req, 0, sizeof(*req));
215 req->cp = cp;
216 req->timeout = SENSE_ID_TIMEOUT;
217 req->maxretries = SENSE_ID_RETRIES;
218 req->lpm = sch->schib.pmcw.pam & sch->opm;
219 req->check = snsid_check;
220 req->callback = snsid_callback;
221 ccw_request_start(cdev);
323} 222}