2 * CCW device SENSE ID I/O handling.
4 * Copyright IBM Corp. 2002, 2009
5 * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
6 * Martin Schwidefsky <schwidefsky@de.ibm.com>
7 * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
10 #include <linux/kernel.h>
11 #include <linux/string.h>
12 #include <linux/types.h>
13 #include <linux/errno.h>
14 #include <asm/ccwdev.h>
15 #include <asm/setup.h>
20 #include "cio_debug.h"
24 #define SENSE_ID_RETRIES 256
25 #define SENSE_ID_TIMEOUT (10 * HZ)
26 #define SENSE_ID_MIN_LEN 4
27 #define SENSE_ID_BASIC_LEN 7
30 * diag210_to_senseid - convert diag 0x210 data to sense id information
32 * @diag: diag 0x210 data
34 * Return 0 on success, non-zero otherwise.
36 static int diag210_to_senseid(struct senseid
*senseid
, struct diag210
*diag
)
39 int class, type
, cu_type
;
41 { 0x08, 0x01, 0x3480 },
42 { 0x08, 0x02, 0x3430 },
43 { 0x08, 0x10, 0x3420 },
44 { 0x08, 0x42, 0x3424 },
45 { 0x08, 0x44, 0x9348 },
46 { 0x08, 0x81, 0x3490 },
47 { 0x08, 0x82, 0x3422 },
48 { 0x10, 0x41, 0x1403 },
49 { 0x10, 0x42, 0x3211 },
50 { 0x10, 0x43, 0x3203 },
51 { 0x10, 0x45, 0x3800 },
52 { 0x10, 0x47, 0x3262 },
53 { 0x10, 0x48, 0x3820 },
54 { 0x10, 0x49, 0x3800 },
55 { 0x10, 0x4a, 0x4245 },
56 { 0x10, 0x4b, 0x4248 },
57 { 0x10, 0x4d, 0x3800 },
58 { 0x10, 0x4e, 0x3820 },
59 { 0x10, 0x4f, 0x3820 },
60 { 0x10, 0x82, 0x2540 },
61 { 0x10, 0x84, 0x3525 },
62 { 0x20, 0x81, 0x2501 },
63 { 0x20, 0x82, 0x2540 },
64 { 0x20, 0x84, 0x3505 },
65 { 0x40, 0x01, 0x3278 },
66 { 0x40, 0x04, 0x3277 },
67 { 0x40, 0x80, 0x2250 },
68 { 0x40, 0xc0, 0x5080 },
69 { 0x80, 0x00, 0x3215 },
73 /* Special case for osa devices. */
74 if (diag
->vrdcvcla
== 0x02 && diag
->vrdcvtyp
== 0x20) {
75 senseid
->cu_type
= 0x3088;
76 senseid
->cu_model
= 0x60;
77 senseid
->reserved
= 0xff;
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;
93 * diag_get_dev_info - retrieve device information via diag 0x210
96 * Returns zero on success, non-zero otherwise.
98 static int diag210_get_dev_info(struct ccw_device
*cdev
)
100 struct ccw_dev_id
*dev_id
= &cdev
->private->dev_id
;
101 struct senseid
*senseid
= &cdev
->private->senseid
;
102 struct diag210 diag_data
;
105 if (dev_id
->ssid
!= 0)
107 memset(&diag_data
, 0, sizeof(diag_data
));
108 diag_data
.vrdcdvno
= dev_id
->devno
;
109 diag_data
.vrdclen
= sizeof(diag_data
);
110 rc
= diag210(&diag_data
);
111 CIO_TRACE_EVENT(4, "diag210");
112 CIO_HEX_EVENT(4, &rc
, sizeof(rc
));
113 CIO_HEX_EVENT(4, &diag_data
, sizeof(diag_data
));
114 if (rc
!= 0 && rc
!= 2)
116 if (diag210_to_senseid(senseid
, &diag_data
))
121 CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: unknown diag210 data\n",
122 dev_id
->ssid
, dev_id
->devno
);
125 CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: diag210 failed (rc=%d)\n",
126 dev_id
->ssid
, dev_id
->devno
, rc
);
131 * Initialize SENSE ID data.
133 static void snsid_init(struct ccw_device
*cdev
)
135 cdev
->private->flags
.esid
= 0;
136 memset(&cdev
->private->senseid
, 0, sizeof(cdev
->private->senseid
));
137 cdev
->private->senseid
.cu_type
= 0xffff;
141 * Check for complete SENSE ID data.
143 static int snsid_check(struct ccw_device
*cdev
, void *data
)
145 struct cmd_scsw
*scsw
= &cdev
->private->irb
.scsw
.cmd
;
146 int len
= sizeof(struct senseid
) - scsw
->count
;
148 /* Check for incomplete SENSE ID data. */
149 if (len
< SENSE_ID_MIN_LEN
)
151 if (cdev
->private->senseid
.cu_type
== 0xffff)
153 /* Check for incompatible SENSE ID data. */
154 if (cdev
->private->senseid
.reserved
!= 0xff)
156 /* Check for extended-identification information. */
157 if (len
> SENSE_ID_BASIC_LEN
)
158 cdev
->private->flags
.esid
= 1;
167 * Process SENSE ID request result.
169 static void snsid_callback(struct ccw_device
*cdev
, void *data
, int rc
)
171 struct ccw_dev_id
*id
= &cdev
->private->dev_id
;
172 struct senseid
*senseid
= &cdev
->private->senseid
;
175 if (rc
&& MACHINE_IS_VM
) {
176 /* Try diag 0x210 fallback on z/VM. */
178 if (diag210_get_dev_info(cdev
) == 0) {
183 CIO_MSG_EVENT(2, "snsid: device 0.%x.%04x: rc=%d %04x/%02x "
184 "%04x/%02x%s\n", id
->ssid
, id
->devno
, rc
,
185 senseid
->cu_type
, senseid
->cu_model
, senseid
->dev_type
,
186 senseid
->dev_model
, vm
? " (diag210)" : "");
187 ccw_device_sense_id_done(cdev
, rc
);
191 * ccw_device_sense_id_start - perform SENSE ID
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.
198 void ccw_device_sense_id_start(struct ccw_device
*cdev
)
200 struct subchannel
*sch
= to_subchannel(cdev
->dev
.parent
);
201 struct ccw_request
*req
= &cdev
->private->req
;
202 struct ccw1
*cp
= cdev
->private->iccws
;
204 CIO_TRACE_EVENT(4, "snsid");
205 CIO_HEX_EVENT(4, &cdev
->private->dev_id
, sizeof(cdev
->private->dev_id
));
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
;
214 memset(req
, 0, sizeof(*req
));
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
);