1 // SPDX-License-Identifier: GPL-2.0
3 * CCW device SENSE ID I/O handling.
5 * Copyright IBM Corp. 2002, 2009
6 * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
7 * Martin Schwidefsky <schwidefsky@de.ibm.com>
8 * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
11 #include <linux/kernel.h>
12 #include <linux/string.h>
13 #include <linux/types.h>
14 #include <linux/errno.h>
15 #include <asm/ccwdev.h>
16 #include <asm/setup.h>
21 #include "cio_debug.h"
25 #define SENSE_ID_RETRIES 256
26 #define SENSE_ID_TIMEOUT (10 * HZ)
27 #define SENSE_ID_MIN_LEN 4
28 #define SENSE_ID_BASIC_LEN 7
31 * diag210_to_senseid - convert diag 0x210 data to sense id information
33 * @diag: diag 0x210 data
35 * Return 0 on success, non-zero otherwise.
37 static int diag210_to_senseid(struct senseid
*senseid
, struct diag210
*diag
)
40 int class, type
, cu_type
;
42 { 0x08, 0x01, 0x3480 },
43 { 0x08, 0x02, 0x3430 },
44 { 0x08, 0x10, 0x3420 },
45 { 0x08, 0x42, 0x3424 },
46 { 0x08, 0x44, 0x9348 },
47 { 0x08, 0x81, 0x3490 },
48 { 0x08, 0x82, 0x3422 },
49 { 0x10, 0x41, 0x1403 },
50 { 0x10, 0x42, 0x3211 },
51 { 0x10, 0x43, 0x3203 },
52 { 0x10, 0x45, 0x3800 },
53 { 0x10, 0x47, 0x3262 },
54 { 0x10, 0x48, 0x3820 },
55 { 0x10, 0x49, 0x3800 },
56 { 0x10, 0x4a, 0x4245 },
57 { 0x10, 0x4b, 0x4248 },
58 { 0x10, 0x4d, 0x3800 },
59 { 0x10, 0x4e, 0x3820 },
60 { 0x10, 0x4f, 0x3820 },
61 { 0x10, 0x82, 0x2540 },
62 { 0x10, 0x84, 0x3525 },
63 { 0x20, 0x81, 0x2501 },
64 { 0x20, 0x82, 0x2540 },
65 { 0x20, 0x84, 0x3505 },
66 { 0x40, 0x01, 0x3278 },
67 { 0x40, 0x04, 0x3277 },
68 { 0x40, 0x80, 0x2250 },
69 { 0x40, 0xc0, 0x5080 },
70 { 0x80, 0x00, 0x3215 },
74 /* Special case for osa devices. */
75 if (diag
->vrdcvcla
== 0x02 && diag
->vrdcvtyp
== 0x20) {
76 senseid
->cu_type
= 0x3088;
77 senseid
->cu_model
= 0x60;
78 senseid
->reserved
= 0xff;
81 for (i
= 0; i
< ARRAY_SIZE(vm_devices
); i
++) {
82 if (diag
->vrdcvcla
== vm_devices
[i
].class &&
83 diag
->vrdcvtyp
== vm_devices
[i
].type
) {
84 senseid
->cu_type
= vm_devices
[i
].cu_type
;
85 senseid
->reserved
= 0xff;
94 * diag_get_dev_info - retrieve device information via diag 0x210
97 * Returns zero on success, non-zero otherwise.
99 static int diag210_get_dev_info(struct ccw_device
*cdev
)
101 struct ccw_dev_id
*dev_id
= &cdev
->private->dev_id
;
102 struct senseid
*senseid
= &cdev
->private->senseid
;
103 struct diag210 diag_data
;
106 if (dev_id
->ssid
!= 0)
108 memset(&diag_data
, 0, sizeof(diag_data
));
109 diag_data
.vrdcdvno
= dev_id
->devno
;
110 diag_data
.vrdclen
= sizeof(diag_data
);
111 rc
= diag210(&diag_data
);
112 CIO_TRACE_EVENT(4, "diag210");
113 CIO_HEX_EVENT(4, &rc
, sizeof(rc
));
114 CIO_HEX_EVENT(4, &diag_data
, sizeof(diag_data
));
115 if (rc
!= 0 && rc
!= 2)
117 if (diag210_to_senseid(senseid
, &diag_data
))
122 CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: unknown diag210 data\n",
123 dev_id
->ssid
, dev_id
->devno
);
126 CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: diag210 failed (rc=%d)\n",
127 dev_id
->ssid
, dev_id
->devno
, rc
);
132 * Initialize SENSE ID data.
134 static void snsid_init(struct ccw_device
*cdev
)
136 cdev
->private->flags
.esid
= 0;
137 memset(&cdev
->private->senseid
, 0, sizeof(cdev
->private->senseid
));
138 cdev
->private->senseid
.cu_type
= 0xffff;
142 * Check for complete SENSE ID data.
144 static int snsid_check(struct ccw_device
*cdev
, void *data
)
146 struct cmd_scsw
*scsw
= &cdev
->private->irb
.scsw
.cmd
;
147 int len
= sizeof(struct senseid
) - scsw
->count
;
149 /* Check for incomplete SENSE ID data. */
150 if (len
< SENSE_ID_MIN_LEN
)
152 if (cdev
->private->senseid
.cu_type
== 0xffff)
154 /* Check for incompatible SENSE ID data. */
155 if (cdev
->private->senseid
.reserved
!= 0xff)
157 /* Check for extended-identification information. */
158 if (len
> SENSE_ID_BASIC_LEN
)
159 cdev
->private->flags
.esid
= 1;
168 * Process SENSE ID request result.
170 static void snsid_callback(struct ccw_device
*cdev
, void *data
, int rc
)
172 struct ccw_dev_id
*id
= &cdev
->private->dev_id
;
173 struct senseid
*senseid
= &cdev
->private->senseid
;
176 if (rc
&& MACHINE_IS_VM
) {
177 /* Try diag 0x210 fallback on z/VM. */
179 if (diag210_get_dev_info(cdev
) == 0) {
184 CIO_MSG_EVENT(2, "snsid: device 0.%x.%04x: rc=%d %04x/%02x "
185 "%04x/%02x%s\n", id
->ssid
, id
->devno
, rc
,
186 senseid
->cu_type
, senseid
->cu_model
, senseid
->dev_type
,
187 senseid
->dev_model
, vm
? " (diag210)" : "");
188 ccw_device_sense_id_done(cdev
, rc
);
192 * ccw_device_sense_id_start - perform SENSE ID
195 * Execute a SENSE ID channel program on @cdev to update its sense id
196 * information. When finished, call ccw_device_sense_id_done with a
197 * return code specifying the result.
199 void ccw_device_sense_id_start(struct ccw_device
*cdev
)
201 struct subchannel
*sch
= to_subchannel(cdev
->dev
.parent
);
202 struct ccw_request
*req
= &cdev
->private->req
;
203 struct ccw1
*cp
= cdev
->private->iccws
;
205 CIO_TRACE_EVENT(4, "snsid");
206 CIO_HEX_EVENT(4, &cdev
->private->dev_id
, sizeof(cdev
->private->dev_id
));
209 /* Channel program setup. */
210 cp
->cmd_code
= CCW_CMD_SENSE_ID
;
211 cp
->cda
= (u32
) (addr_t
) &cdev
->private->senseid
;
212 cp
->count
= sizeof(struct senseid
);
213 cp
->flags
= CCW_FLAG_SLI
;
215 memset(req
, 0, sizeof(*req
));
217 req
->timeout
= SENSE_ID_TIMEOUT
;
218 req
->maxretries
= SENSE_ID_RETRIES
;
219 req
->lpm
= sch
->schib
.pmcw
.pam
& sch
->opm
;
220 req
->check
= snsid_check
;
221 req
->callback
= snsid_callback
;
222 ccw_request_start(cdev
);