File: /usr/src/linux/drivers/s390/block/dasd_diag.c
1 /*
2 * File...........: linux/drivers/s390/block/dasd_diag.c
3 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
4 * Based on.......: linux/drivers/s390/block/mdisk.c
5 * ...............: by Hartmunt Penner <hpenner@de.ibm.com>
6 * Bugreports.to..: <Linux390@de.ibm.com>
7 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
8
9 * History of changes
10 * 07/13/00 Added fixup sections for diagnoses ans saved some registers
11 * 07/14/00 fixed constraints in newly generated inline asm
12 * 10/05/00 adapted to 'new' DASD driver
13 * fixed return codes of dia250()
14 * fixed partition handling and HDIO_GETGEO
15 */
16
17 #include <linux/config.h>
18 #include <linux/stddef.h>
19 #include <linux/kernel.h>
20 #include <asm/debug.h>
21
22 #include <linux/slab.h>
23 #include <linux/hdreg.h> /* HDIO_GETGEO */
24 #include <linux/blk.h>
25 #include <asm/ccwcache.h>
26 #include <asm/dasd.h>
27
28 #include <asm/ebcdic.h>
29 #include <asm/io.h>
30 #include <asm/irq.h>
31 #include <asm/s390dyn.h>
32
33 #include "dasd_int.h"
34 #include "dasd_diag.h"
35
36 #ifdef PRINTK_HEADER
37 #undef PRINTK_HEADER
38 #endif /* PRINTK_HEADER */
39 #define PRINTK_HEADER DASD_NAME"(diag):"
40
41 dasd_discipline_t dasd_diag_discipline;
42
43 typedef struct
44 dasd_diag_private_t {
45 dasd_diag_characteristics_t rdc_data;
46 diag_rw_io_t iob;
47 diag_init_io_t iib;
48 unsigned long *label;
49 } dasd_diag_private_t;
50
51 static __inline__ int
52 dia210 (void *devchar)
53 {
54 int rc;
55
56 __asm__ __volatile__ (" diag %1,0,0x210\n"
57 "0: ipm %0\n"
58 " srl %0,28\n"
59 "1:\n"
60 ".section .fixup,\"ax\"\n"
61 "2: lhi %0,3\n"
62 " bras 1,3f\n"
63 " .long 1b\n"
64 "3: l 1,0(1)\n"
65 " br 1\n"
66 ".previous\n"
67 ".section __ex_table,\"a\"\n"
68 " .align 4\n"
69 " .long 0b,2b\n" ".previous\n":"=d" (rc)
70 :"d" ((void *) __pa (devchar))
71 :"1");
72 return rc;
73 }
74
75 static __inline__ int
76 dia250 (void *iob, int cmd)
77 {
78 __asm__ __volatile__ (" lr 0,%1\n"
79 " diag 0,%0,0x250\n"
80 "0: ipm %0\n"
81 " srl %0,28\n"
82 " or %0,1\n"
83 "1:\n"
84 ".section .fixup,\"ax\"\n"
85 "2: lhi %0,3\n"
86 " bras 1,3f\n"
87 " .long 1b\n"
88 "3: l 1,0(1)\n"
89 " br 1\n"
90 ".previous\n"
91 ".section __ex_table,\"a\"\n"
92 " .align 4\n"
93 " .long 0b,2b\n" ".previous\n":"+d" (cmd)
94 :"d" ((void *) __pa (iob))
95 :"0", "1", "cc");
96 return cmd;
97 }
98
99 static __inline__ int
100 mdsk_init_io (dasd_device_t * device, int blocksize, int offset, int size)
101 {
102 dasd_diag_private_t *private = (dasd_diag_private_t *) device->private;
103 diag_init_io_t *iib = &private->iib;
104 int rc;
105
106 memset (iib, 0, sizeof (diag_init_io_t));
107
108 iib->dev_nr = device->devinfo.devno;
109 iib->block_size = blocksize;
110 iib->offset = offset;
111 iib->start_block = 0;
112 iib->end_block = size;
113
114 rc = dia250 (iib, INIT_BIO);
115
116 return rc & 3;
117 }
118
119 static __inline__ int
120 mdsk_term_io (dasd_device_t * device)
121 {
122 dasd_diag_private_t *private = (dasd_diag_private_t *) device->private;
123 diag_init_io_t *iib = &private->iib;
124 int rc;
125
126 memset (iib, 0, sizeof (diag_init_io_t));
127 iib->dev_nr = device->devinfo.devno;
128 rc = dia250 (iib, TERM_BIO);
129 return rc & 3;
130 }
131
132 int
133 dasd_start_diag (ccw_req_t * cqr)
134 {
135 int rc;
136 dasd_device_t *device = cqr->device;
137 dasd_diag_private_t *private;
138 diag_rw_io_t *iob;
139
140 private = (dasd_diag_private_t *) device->private;
141 iob = &private->iob;
142
143 iob->dev_nr = device->devinfo.devno;
144 iob->key = 0;
145 iob->flags = 2;
146 iob->block_count = cqr->cplength >> 1;
147 iob->interrupt_params = (u32) cqr;
148 iob->bio_list = __pa (cqr->cpaddr);
149
150 asm volatile ("STCK %0":"=m" (cqr->startclk));
151 rc = dia250 (iob, RW_BIO);
152 if (rc > 8) {
153 PRINT_WARN ("dia250 returned CC %d\n", rc);
154 check_then_set (&cqr->status,
155 CQR_STATUS_QUEUED, CQR_STATUS_ERROR);
156 } else if (rc == 0) {
157 check_then_set (&cqr->status,
158 CQR_STATUS_QUEUED, CQR_STATUS_DONE);
159 dasd_schedule_bh (device);
160 } else {
161 if (cqr->expires) {
162 cqr->expires += cqr->startclk;
163 }
164 check_then_set (&cqr->status,
165 CQR_STATUS_QUEUED, CQR_STATUS_IN_IO);
166 rc = 0;
167 }
168 return rc;
169 }
170
171 void
172 dasd_ext_handler (struct pt_regs *regs, __u16 code)
173 {
174 int cpu = smp_processor_id();
175 ccw_req_t *cqr;
176 int ip = S390_lowcore.ext_params;
177 char status = *((char *) &S390_lowcore.ext_params + 5);
178 dasd_device_t *device;
179 int done_fast_io = 0;
180 int devno;
181
182 irq_enter(cpu, -1);
183
184 if (!ip) { /* no intparm: unsolicited interrupt */
185 printk (KERN_WARNING PRINTK_HEADER
186 "caught unsolicited interrupt\n");
187 irq_exit(cpu, -1);
188 return;
189 }
190 if (ip & 0x80000001) {
191 printk (KERN_WARNING PRINTK_HEADER
192 "caught spurious interrupt with parm %08x\n", ip);
193 irq_exit(cpu, -1);
194 return;
195 }
196 cqr = (ccw_req_t *) ip;
197 device = (dasd_device_t *) cqr->device;
198 devno = device->devinfo.devno;
199 if (device == NULL) {
200 printk (KERN_WARNING PRINTK_HEADER
201 " INT on devno 0x%04X = /dev/%s (%d:%d)"
202 " belongs to NULL device\n",
203 devno, device->name, MAJOR (device->kdev),
204 MINOR (device->kdev));
205 }
206 if (strncmp (device->discipline->ebcname, (char *) &cqr->magic, 4)) {
207 printk (KERN_WARNING PRINTK_HEADER
208 "0x%04X : /dev/%s (%d:%d)"
209 " magic number of ccw_req_t 0x%08X doesn't match"
210 " discipline 0x%08X\n",
211 devno, device->name,
212 MAJOR (device->kdev), MINOR (device->kdev),
213 cqr->magic, *(int *) (&device->discipline->name));
214 irq_exit(cpu, -1);
215 return;
216 }
217 asm volatile ("STCK %0":"=m" (cqr->stopclk));
218 switch (status) {
219 case 0x00:
220 check_then_set (&cqr->status,
221 CQR_STATUS_IN_IO, CQR_STATUS_DONE);
222 if (cqr->next && (cqr->next->status == CQR_STATUS_QUEUED)) {
223 if (dasd_start_diag (cqr->next) == 0) {
224 done_fast_io = 1;
225 }
226 }
227 break;
228 case 0x01:
229 case 0x02:
230 case 0x03:
231 default:
232 check_then_set (&cqr->status,
233 CQR_STATUS_IN_IO, CQR_STATUS_FAILED);
234 break;
235 }
236 wake_up (&device->wait_q);
237 dasd_schedule_bh (device);
238 irq_exit(cpu, -1);
239 }
240
241 static int
242 dasd_diag_check_characteristics (struct dasd_device_t *device)
243 {
244 int rc = 0;
245 int bsize;
246 int label_block;
247 dasd_diag_private_t *private;
248 dasd_diag_characteristics_t *rdc_data;
249 ccw_req_t *cqr;
250 long *label;
251 int sb;
252
253 if (device == NULL) {
254 printk (KERN_WARNING PRINTK_HEADER
255 "Null device pointer passed to characteristics checker\n");
256 return -ENODEV;
257 }
258 device->private = kmalloc (sizeof (dasd_diag_private_t), GFP_KERNEL);
259 if (device->private == NULL) {
260 printk (KERN_WARNING PRINTK_HEADER
261 "memory allocation failed for private data\n");
262 rc = -ENOMEM;
263 goto fail;
264 }
265 private = (dasd_diag_private_t *) device->private;
266 rdc_data = (void *) &(private->rdc_data);
267
268 rdc_data->dev_nr = device->devinfo.devno;
269 rdc_data->rdc_len = sizeof (dasd_diag_characteristics_t);
270
271 rc = dia210 (rdc_data);
272 if ( rc != 0) {
273 goto fail;
274 }
275 if (rdc_data->vdev_class != DEV_CLASS_FBA &&
276 rdc_data->vdev_class != DEV_CLASS_ECKD &&
277 rdc_data->vdev_class != DEV_CLASS_CKD) {
278 rc = -ENOTSUPP;
279 goto fail;
280 }
281 #if 0
282 printk (KERN_INFO PRINTK_HEADER
283 "%04X: %04X on real %04X/%02X\n",
284 rdc_data->dev_nr,
285 rdc_data->vdev_type, rdc_data->rdev_type, rdc_data->rdev_model);
286 #endif
287 /* Figure out position of label block */
288 if (private->rdc_data.vdev_class == DEV_CLASS_FBA) {
289 device->sizes.pt_block = 1;
290 } else if (private->rdc_data.vdev_class == DEV_CLASS_ECKD ||
291 private->rdc_data.vdev_class == DEV_CLASS_CKD) {
292 device->sizes.pt_block = 2;
293 } else {
294 BUG();
295 }
296 private->label = (long *) get_free_page (GFP_KERNEL);
297 label = private->label;
298 mdsk_term_io (device); /* first terminate all outstanding operations */
299 /* figure out blocksize of device */
300 for (bsize = 512; bsize <= PAGE_SIZE; bsize <<= 1) {
301 diag_bio_t *bio;
302 diag_rw_io_t *iob = &private->iob;
303
304 rc = mdsk_init_io (device, bsize, 0, 64);
305 if (rc > 4) {
306 continue;
307 }
308 cqr = dasd_alloc_request (dasd_diag_discipline.name,
309 sizeof (diag_bio_t) / sizeof (ccw1_t),
310 0, device);
311 if (cqr == NULL) {
312 printk (KERN_WARNING PRINTK_HEADER
313 "No memory to allocate initialization request\n");
314 free_page ((long) private->label);
315 rc = -ENOMEM;
316 goto fail;
317 }
318 bio = (diag_bio_t *) (cqr->cpaddr);
319 memset (bio, 0, sizeof (diag_bio_t));
320 bio->type = MDSK_READ_REQ;
321 bio->block_number = device->sizes.pt_block + 1;
322 bio->buffer = __pa (private->label);
323 cqr->device = device;
324 cqr->status = CQR_STATUS_FILLED;
325 memset (iob, 0, sizeof (diag_rw_io_t));
326 iob->dev_nr = rdc_data->dev_nr;
327 iob->block_count = 1;
328 iob->interrupt_params = (u32) cqr;
329 iob->bio_list = __pa (bio);
330 rc = dia250 (iob, RW_BIO);
331 if (rc == 0) {
332 if (label[3] != bsize ||
333 label[0] != 0xc3d4e2f1 || /* != CMS1 */
334 label[13] == 0 ){
335 rc = -EMEDIUMTYPE;
336 goto fail;
337 }
338 break;
339 }
340 mdsk_term_io (device);
341 }
342 if (bsize > PAGE_SIZE) {
343 rc = -EMEDIUMTYPE;
344 goto fail;
345 }
346 device->sizes.blocks = label[7];
347 device->sizes.bp_block = bsize;
348 device->sizes.s2b_shift = 0; /* bits to shift 512 to get a block */
349 for (sb = 512; sb < bsize; sb = sb << 1)
350 device->sizes.s2b_shift++;
351 printk (KERN_INFO PRINTK_HEADER
352 "/dev/%s (%04X): capacity (%dkB blks): %ldkB\n",
353 device->name, device->devinfo.devno,
354 (device->sizes.bp_block >> 10),
355 (device->sizes.blocks << device->sizes.s2b_shift) >> 1);
356 free_page ((long) private->label);
357 rc = 0;
358 goto out;
359 fail:
360 if ( rc ) {
361 kfree (device->private);
362 device->private = NULL;
363 }
364 out:
365 return rc;
366 }
367
368 static int
369 dasd_diag_fill_geometry (struct dasd_device_t *device, struct hd_geometry *geo)
370 {
371 int rc = 0;
372 dasd_diag_private_t *private = (dasd_diag_private_t *) device->private;
373 unsigned long sectors = device->sizes.blocks << device->sizes.s2b_shift;
374 unsigned long tracks = sectors >> 6;
375 unsigned long trk_rem = sectors & ((1 << 6) - 1); /* 64k per track */
376 unsigned long cyls = tracks >> 4;
377 unsigned long cyls_rem = tracks & ((1 << 4) - 1); /* 16 head per cyl */
378
379 switch (device->sizes.bp_block) {
380 case 512:
381 case 1024:
382 case 2048:
383 case 4096:
384 break;
385 default:
386 return -EINVAL;
387 }
388 geo->cylinders = cyls;
389 geo->heads = 16;
390 geo->sectors = 128 >> device->sizes.s2b_shift;
391 return rc;
392 }
393
394 static dasd_era_t
395 dasd_diag_examine_error (ccw_req_t * cqr, devstat_t * stat)
396 {
397 return dasd_era_fatal;
398 }
399
400 static ccw_req_t *
401 dasd_diag_build_cp_from_req (dasd_device_t * device, struct request *req)
402 {
403 ccw_req_t *rw_cp = NULL;
404 struct buffer_head *bh;
405 int rw_cmd;
406 int noblk = req->nr_sectors >> device->sizes.s2b_shift;
407 int byt_per_blk = device->sizes.bp_block;
408 int block;
409 diag_bio_t *bio;
410 int bhct;
411 long size;
412
413 if (!noblk) {
414 PRINT_ERR ("No blocks to read/write...returning\n");
415 return NULL;
416 }
417 if (req->cmd == READ) {
418 rw_cmd = MDSK_READ_REQ;
419 } else if (req->cmd == WRITE) {
420 rw_cmd = MDSK_WRITE_REQ;
421 }
422 bhct = 0;
423 for (bh = req->bh; bh; bh = bh->b_reqnext) {
424 if (bh->b_size < byt_per_blk)
425 BUG();
426 bhct += bh->b_size >> (device->sizes.s2b_shift+9);
427 }
428 /* Build the request */
429 rw_cp = dasd_alloc_request (dasd_diag_discipline.name, bhct << 1, 0, device);
430 if (!rw_cp) {
431 return NULL;
432 }
433 bio = (diag_bio_t *) (rw_cp->cpaddr);
434
435 block = req->sector >> device->sizes.s2b_shift;
436 for (bh = req->bh; bh; bh = bh->b_reqnext) {
437 memset (bio, 0, sizeof (diag_bio_t));
438 for (size = 0; size < bh->b_size; size += byt_per_blk) {
439 bio->type = rw_cmd;
440 bio->block_number = block + 1;
441 bio->buffer = __pa (bh->b_data + size);
442 bio++;
443 block++;
444 }
445 }
446 asm volatile ("STCK %0":"=m" (rw_cp->buildclk));
447 rw_cp->device = device;
448 rw_cp->expires = 50 * TOD_SEC; /* 50 seconds */
449 rw_cp->req = req;
450 check_then_set (&rw_cp->status, CQR_STATUS_EMPTY, CQR_STATUS_FILLED);
451 return rw_cp;
452 }
453
454 static int
455 dasd_diag_fill_info (dasd_device_t * device, dasd_information_t * info)
456 {
457 int rc = 0;
458 info->FBA_layout = 1;
459 info->characteristics_size = sizeof (dasd_diag_characteristics_t);
460 memcpy (info->characteristics,
461 &((dasd_diag_private_t *) device->private)->rdc_data,
462 sizeof (dasd_diag_characteristics_t));
463 info->confdata_size = 0;
464 return rc;
465 }
466
467 static char *
468 dasd_diag_dump_sense (struct dasd_device_t *device, ccw_req_t * req)
469 {
470 char *page = (char *) get_free_page (GFP_KERNEL);
471 int len;
472 if (page == NULL) {
473 return NULL;
474 }
475 len = sprintf (page, KERN_WARNING PRINTK_HEADER
476 "device %04X on irq %d: I/O status report:\n",
477 device->devinfo.devno, device->devinfo.irq);
478
479 return page;
480 }
481
482 dasd_discipline_t dasd_diag_discipline = {
483 owner: THIS_MODULE,
484 name:"DIAG",
485 ebcname:"DIAG",
486 max_blocks:PAGE_SIZE / sizeof (diag_bio_t),
487 check_characteristics:dasd_diag_check_characteristics,
488 fill_geometry:dasd_diag_fill_geometry,
489 start_IO:dasd_start_diag,
490 examine_error:dasd_diag_examine_error,
491 build_cp_from_req:dasd_diag_build_cp_from_req,
492 dump_sense:dasd_diag_dump_sense,
493 int_handler:dasd_ext_handler,
494 fill_info:dasd_diag_fill_info,
495 };
496
497 int
498 dasd_diag_init (void)
499 {
500 int rc = 0;
501 if (MACHINE_IS_VM) {
502 printk (KERN_INFO PRINTK_HEADER
503 "%s discipline initializing\n",
504 dasd_diag_discipline.name);
505 ASCEBC (dasd_diag_discipline.ebcname, 4);
506 ctl_set_bit (0, 9);
507 register_external_interrupt (0x2603, dasd_ext_handler);
508 dasd_discipline_add (&dasd_diag_discipline);
509 } else {
510 printk (KERN_INFO PRINTK_HEADER
511 "Machine is not VM: %s discipline not initializing\n",
512 dasd_diag_discipline.name);
513 rc = -EINVAL;
514 }
515 return rc;
516 }
517
518 void
519 dasd_diag_cleanup (void)
520 {
521 if (MACHINE_IS_VM) {
522 printk (KERN_INFO PRINTK_HEADER
523 "%s discipline cleaning up\n",
524 dasd_diag_discipline.name);
525 dasd_discipline_del (&dasd_diag_discipline);
526 unregister_external_interrupt (0x2603, dasd_ext_handler);
527 ctl_clear_bit (0, 9);
528 } else {
529 printk (KERN_INFO PRINTK_HEADER
530 "Machine is not VM: %s discipline not initializing\n",
531 dasd_diag_discipline.name);
532 }
533 }
534
535 #ifdef MODULE
536 int
537 init_module (void)
538 {
539 int rc = 0;
540 rc = dasd_diag_init ();
541 return rc;
542 }
543
544 void
545 cleanup_module (void)
546 {
547 dasd_diag_cleanup ();
548 return;
549 }
550 #endif
551
552 /*
553 * Overrides for Emacs so that we follow Linus's tabbing style.
554 * Emacs will notice this stuff at the end of the file and automatically
555 * adjust the settings for this buffer only. This must remain at the end
556 * of the file.
557 * ---------------------------------------------------------------------------
558 * Local variables:
559 * c-indent-level: 4
560 * c-brace-imaginary-offset: 0
561 * c-brace-offset: -4
562 * c-argdecl-indent: 4
563 * c-label-offset: -4
564 * c-continued-statement-offset: 4
565 * c-continued-brace-offset: 0
566 * indent-tabs-mode: nil
567 * tab-width: 8
568 * End:
569 */
570