diff options
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/cio/device.h | 1 | ||||
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 66 | ||||
-rw-r--r-- | drivers/s390/cio/device_id.c | 375 |
3 files changed, 154 insertions, 288 deletions
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index f3c8a2a8c3a4..2815798c4165 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h | |||
@@ -108,7 +108,6 @@ void ccw_request_notoper(struct ccw_device *cdev); | |||
108 | 108 | ||
109 | /* Function prototypes for sense id stuff. */ | 109 | /* Function prototypes for sense id stuff. */ |
110 | void ccw_device_sense_id_start(struct ccw_device *); | 110 | void ccw_device_sense_id_start(struct ccw_device *); |
111 | void ccw_device_sense_id_irq(struct ccw_device *, enum dev_event); | ||
112 | void ccw_device_sense_id_done(struct ccw_device *, int); | 111 | void ccw_device_sense_id_done(struct ccw_device *, int); |
113 | 112 | ||
114 | /* Function prototypes for path grouping stuff. */ | 113 | /* Function prototypes for path grouping stuff. */ |
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index c397442ab3d8..6247d07d395e 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c | |||
@@ -229,7 +229,6 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) | |||
229 | 229 | ||
230 | sch = to_subchannel(cdev->dev.parent); | 230 | sch = to_subchannel(cdev->dev.parent); |
231 | 231 | ||
232 | ccw_device_set_timeout(cdev, 0); | ||
233 | cio_disable_subchannel(sch); | 232 | cio_disable_subchannel(sch); |
234 | /* | 233 | /* |
235 | * Now that we tried recognition, we have performed device selection | 234 | * Now that we tried recognition, we have performed device selection |
@@ -263,22 +262,10 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) | |||
263 | } | 262 | } |
264 | switch (state) { | 263 | switch (state) { |
265 | case DEV_STATE_NOT_OPER: | 264 | case DEV_STATE_NOT_OPER: |
266 | CIO_MSG_EVENT(2, "SenseID : unknown device %04x on " | ||
267 | "subchannel 0.%x.%04x\n", | ||
268 | cdev->private->dev_id.devno, | ||
269 | sch->schid.ssid, sch->schid.sch_no); | ||
270 | break; | 265 | break; |
271 | case DEV_STATE_OFFLINE: | 266 | case DEV_STATE_OFFLINE: |
272 | if (!cdev->online) { | 267 | if (!cdev->online) { |
273 | ccw_device_update_sense_data(cdev); | 268 | ccw_device_update_sense_data(cdev); |
274 | /* Issue device info message. */ | ||
275 | CIO_MSG_EVENT(4, "SenseID : device 0.%x.%04x reports: " | ||
276 | "CU Type/Mod = %04X/%02X, Dev Type/Mod " | ||
277 | "= %04X/%02X\n", | ||
278 | cdev->private->dev_id.ssid, | ||
279 | cdev->private->dev_id.devno, | ||
280 | cdev->id.cu_type, cdev->id.cu_model, | ||
281 | cdev->id.dev_type, cdev->id.dev_model); | ||
282 | break; | 269 | break; |
283 | } | 270 | } |
284 | cdev->private->state = DEV_STATE_OFFLINE; | 271 | cdev->private->state = DEV_STATE_OFFLINE; |
@@ -293,10 +280,6 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) | |||
293 | } | 280 | } |
294 | return; | 281 | return; |
295 | case DEV_STATE_BOXED: | 282 | case DEV_STATE_BOXED: |
296 | CIO_MSG_EVENT(0, "SenseID : boxed device %04x on " | ||
297 | " subchannel 0.%x.%04x\n", | ||
298 | cdev->private->dev_id.devno, | ||
299 | sch->schid.ssid, sch->schid.sch_no); | ||
300 | if (cdev->id.cu_type != 0) { /* device was recognized before */ | 283 | if (cdev->id.cu_type != 0) { /* device was recognized before */ |
301 | cdev->private->flags.recog_done = 1; | 284 | cdev->private->flags.recog_done = 1; |
302 | cdev->private->state = DEV_STATE_BOXED; | 285 | cdev->private->state = DEV_STATE_BOXED; |
@@ -520,27 +503,25 @@ void ccw_device_recognition(struct ccw_device *cdev) | |||
520 | } | 503 | } |
521 | 504 | ||
522 | /* | 505 | /* |
523 | * Handle timeout in device recognition. | 506 | * Handle events for states that use the ccw request infrastructure. |
524 | */ | 507 | */ |
525 | static void | 508 | static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e) |
526 | ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event) | ||
527 | { | 509 | { |
528 | int ret; | 510 | switch (e) { |
529 | 511 | case DEV_EVENT_NOTOPER: | |
530 | ret = ccw_device_cancel_halt_clear(cdev); | 512 | ccw_request_notoper(cdev); |
531 | switch (ret) { | ||
532 | case 0: | ||
533 | ccw_device_recog_done(cdev, DEV_STATE_BOXED); | ||
534 | break; | 513 | break; |
535 | case -ENODEV: | 514 | case DEV_EVENT_INTERRUPT: |
536 | ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER); | 515 | ccw_request_handler(cdev); |
516 | break; | ||
517 | case DEV_EVENT_TIMEOUT: | ||
518 | ccw_request_timeout(cdev); | ||
537 | break; | 519 | break; |
538 | default: | 520 | default: |
539 | ccw_device_set_timeout(cdev, 3*HZ); | 521 | break; |
540 | } | 522 | } |
541 | } | 523 | } |
542 | 524 | ||
543 | |||
544 | void | 525 | void |
545 | ccw_device_verify_done(struct ccw_device *cdev, int err) | 526 | ccw_device_verify_done(struct ccw_device *cdev, int err) |
546 | { | 527 | { |
@@ -713,15 +694,6 @@ ccw_device_onoff_timeout(struct ccw_device *cdev, enum dev_event dev_event) | |||
713 | } | 694 | } |
714 | 695 | ||
715 | /* | 696 | /* |
716 | * Handle not oper event in device recognition. | ||
717 | */ | ||
718 | static void | ||
719 | ccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event) | ||
720 | { | ||
721 | ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER); | ||
722 | } | ||
723 | |||
724 | /* | ||
725 | * Handle not operational event in non-special state. | 697 | * Handle not operational event in non-special state. |
726 | */ | 698 | */ |
727 | static void ccw_device_generic_notoper(struct ccw_device *cdev, | 699 | static void ccw_device_generic_notoper(struct ccw_device *cdev, |
@@ -1015,10 +987,6 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event) | |||
1015 | if (cio_enable_subchannel(sch, (u32)(addr_t)sch) != 0) | 987 | if (cio_enable_subchannel(sch, (u32)(addr_t)sch) != 0) |
1016 | /* Couldn't enable the subchannel for i/o. Sick device. */ | 988 | /* Couldn't enable the subchannel for i/o. Sick device. */ |
1017 | return; | 989 | return; |
1018 | |||
1019 | /* After 60s the device recognition is considered to have failed. */ | ||
1020 | ccw_device_set_timeout(cdev, 60*HZ); | ||
1021 | |||
1022 | cdev->private->state = DEV_STATE_DISCONNECTED_SENSE_ID; | 990 | cdev->private->state = DEV_STATE_DISCONNECTED_SENSE_ID; |
1023 | ccw_device_sense_id_start(cdev); | 991 | ccw_device_sense_id_start(cdev); |
1024 | } | 992 | } |
@@ -1141,9 +1109,9 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { | |||
1141 | [DEV_EVENT_VERIFY] = ccw_device_nop, | 1109 | [DEV_EVENT_VERIFY] = ccw_device_nop, |
1142 | }, | 1110 | }, |
1143 | [DEV_STATE_SENSE_ID] = { | 1111 | [DEV_STATE_SENSE_ID] = { |
1144 | [DEV_EVENT_NOTOPER] = ccw_device_recog_notoper, | 1112 | [DEV_EVENT_NOTOPER] = ccw_device_request_event, |
1145 | [DEV_EVENT_INTERRUPT] = ccw_device_sense_id_irq, | 1113 | [DEV_EVENT_INTERRUPT] = ccw_device_request_event, |
1146 | [DEV_EVENT_TIMEOUT] = ccw_device_recog_timeout, | 1114 | [DEV_EVENT_TIMEOUT] = ccw_device_request_event, |
1147 | [DEV_EVENT_VERIFY] = ccw_device_nop, | 1115 | [DEV_EVENT_VERIFY] = ccw_device_nop, |
1148 | }, | 1116 | }, |
1149 | [DEV_STATE_OFFLINE] = { | 1117 | [DEV_STATE_OFFLINE] = { |
@@ -1209,9 +1177,9 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { | |||
1209 | [DEV_EVENT_VERIFY] = ccw_device_start_id, | 1177 | [DEV_EVENT_VERIFY] = ccw_device_start_id, |
1210 | }, | 1178 | }, |
1211 | [DEV_STATE_DISCONNECTED_SENSE_ID] = { | 1179 | [DEV_STATE_DISCONNECTED_SENSE_ID] = { |
1212 | [DEV_EVENT_NOTOPER] = ccw_device_recog_notoper, | 1180 | [DEV_EVENT_NOTOPER] = ccw_device_request_event, |
1213 | [DEV_EVENT_INTERRUPT] = ccw_device_sense_id_irq, | 1181 | [DEV_EVENT_INTERRUPT] = ccw_device_request_event, |
1214 | [DEV_EVENT_TIMEOUT] = ccw_device_recog_timeout, | 1182 | [DEV_EVENT_TIMEOUT] = ccw_device_request_event, |
1215 | [DEV_EVENT_VERIFY] = ccw_device_nop, | 1183 | [DEV_EVENT_VERIFY] = ccw_device_nop, |
1216 | }, | 1184 | }, |
1217 | [DEV_STATE_CMFCHANGE] = { | 1185 | [DEV_STATE_CMFCHANGE] = { |
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 | */ |
37 | static int vm_vdev_to_cu_type(int class, int type) | 36 | static 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 | */ |
88 | static int diag_get_dev_info(u16 devno, struct senseid *ps) | 98 | static 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; | 120 | err_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) | 124 | err_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 | */ |
133 | static int | 133 | static 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 | |||
172 | void | ||
173 | ccw_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 | */ |
189 | static int | 143 | static int snsid_check(struct ccw_device *cdev, void *data) |
190 | ccw_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; | 161 | out_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 | */ |
270 | void | 169 | static void snsid_callback(struct ccw_device *cdev, void *data, int rc) |
271 | ccw_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 | */ | ||
198 | void 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 | } |