1 /*
2 * File...........: linux/drivers/s390/block/dasd_fba.c
3 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
4 * Bugreports.to..: <Linux390@de.ibm.com>
5 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
6 *
7 * $Revision: 1.50 $
8 *
9 * History of changes
10 * fixed partition handling and HDIO_GETGEO
11 */
12
13 #include <linux/config.h>
14 #include <linux/stddef.h>
15 #include <linux/kernel.h>
16 #include <asm/debug.h>
17
18 #include <linux/slab.h>
19 #include <linux/hdreg.h> /* HDIO_GETGEO */
20 #include <linux/blk.h>
21
22 #include <asm/ccwcache.h>
23 #include <asm/idals.h>
24 #include <asm/ebcdic.h>
25 #include <asm/io.h>
26 #include <asm/irq.h>
27 #include <asm/s390dyn.h>
28
29 #include "dasd_int.h"
30 #include "dasd_fba.h"
31 #include "dasd_3370_erp.h"
32 #include "dasd_9336_erp.h"
33
34 #ifdef PRINTK_HEADER
35 #undef PRINTK_HEADER
36 #endif /* PRINTK_HEADER */
37 #define PRINTK_HEADER DASD_NAME"(fba):"
38
39 #define DASD_FBA_CCW_WRITE 0x41
40 #define DASD_FBA_CCW_READ 0x42
41 #define DASD_FBA_CCW_LOCATE 0x43
42 #define DASD_FBA_CCW_DEFINE_EXTENT 0x63
43
44 #ifdef MODULE
45 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
46 MODULE_LICENSE("GPL");
47 #endif
48 #endif
49
50 dasd_discipline_t dasd_fba_discipline;
51
52 typedef struct
53 dasd_fba_private_t {
54 dasd_fba_characteristics_t rdc_data;
55 } dasd_fba_private_t;
56
57 #ifdef CONFIG_DASD_DYNAMIC
58 static
59 devreg_t dasd_fba_known_devices[] = {
60 {
61 ci: { hc: {ctype:0x6310, dtype:0x9336}},
62 flag:(DEVREG_MATCH_CU_TYPE |
63 DEVREG_MATCH_DEV_TYPE | DEVREG_TYPE_DEVCHARS),
64 oper_func:dasd_oper_handler
65 },
66 {
67 ci: { hc: {ctype:0x3880, dtype:0x3370}},
68 flag:(DEVREG_MATCH_CU_TYPE |
69 DEVREG_MATCH_DEV_TYPE | DEVREG_TYPE_DEVCHARS),
70 oper_func:dasd_oper_handler
71 }
72 };
73 #endif
74 static inline int
define_extent(ccw1_t * ccw,DE_fba_data_t * DE_data,int rw,int blksize,int beg,int nr,ccw_req_t * cqr,dasd_device_t * device)75 define_extent (ccw1_t * ccw, DE_fba_data_t * DE_data, int rw,
76 int blksize, int beg, int nr, ccw_req_t* cqr,
77 dasd_device_t* device)
78 {
79 int rc=0;
80 memset (DE_data, 0, sizeof (DE_fba_data_t));
81 ccw->cmd_code = DASD_FBA_CCW_DEFINE_EXTENT;
82 ccw->count = 16;
83 if ((rc=dasd_set_normalized_cda (ccw, __pa (DE_data), cqr, device)))
84 return rc;
85 if (rw == WRITE)
86 (DE_data->mask).perm = 0x0;
87 else if (rw == READ)
88 (DE_data->mask).perm = 0x1;
89 else
90 DE_data->mask.perm = 0x2;
91 DE_data->blk_size = blksize;
92 DE_data->ext_loc = beg;
93 DE_data->ext_end = nr - 1;
94 return rc;
95 }
96
97 static inline void
locate_record(ccw1_t * ccw,LO_fba_data_t * LO_data,int rw,int block_nr,int block_ct,ccw_req_t * cqr,dasd_device_t * device)98 locate_record (ccw1_t * ccw, LO_fba_data_t * LO_data, int rw, int block_nr,
99 int block_ct, ccw_req_t* cqr, dasd_device_t* device)
100 {
101 memset (LO_data, 0, sizeof (LO_fba_data_t));
102 ccw->cmd_code = DASD_FBA_CCW_LOCATE;
103 ccw->count = 8;
104 dasd_set_normalized_cda (ccw, __pa (LO_data), cqr, device);
105 if (rw == WRITE)
106 LO_data->operation.cmd = 0x5;
107 else if (rw == READ)
108 LO_data->operation.cmd = 0x6;
109 else
110 LO_data->operation.cmd = 0x8;
111 LO_data->blk_nr = block_nr;
112 LO_data->blk_ct = block_ct;
113 }
114
115 static int
dasd_fba_id_check(s390_dev_info_t * info)116 dasd_fba_id_check (s390_dev_info_t * info)
117 {
118 if (info->sid_data.cu_type == 0x3880)
119 if (info->sid_data.dev_type == 0x3370)
120 return 0;
121 if (info->sid_data.cu_type == 0x6310)
122 if (info->sid_data.dev_type == 0x9336)
123 return 0;
124 return -ENODEV;
125 }
126
127 static int
dasd_fba_check_characteristics(struct dasd_device_t * device)128 dasd_fba_check_characteristics (struct dasd_device_t *device)
129 {
130 int rc = -ENODEV;
131 void *rdc_data;
132 dasd_fba_private_t *private;
133
134 if (device == NULL) {
135
136 MESSAGE (KERN_WARNING, "%s",
137 "Null device pointer passed to characteristics "
138 "checker");
139
140 return -ENODEV;
141 }
142 device->private = kmalloc (sizeof (dasd_fba_private_t), GFP_KERNEL);
143
144 if (device->private == NULL) {
145
146 MESSAGE (KERN_WARNING, "%s",
147 "memory allocation failed for private data");
148
149 rc = -ENOMEM;
150 goto fail;
151 }
152 private = (dasd_fba_private_t *) device->private;
153 rdc_data = (void *) &(private->rdc_data);
154 rc = read_dev_chars (device->devinfo.irq, &rdc_data, 32);
155
156 if (rc) {
157
158 MESSAGE (KERN_WARNING,
159 "Read device characteristics returned error %d",
160 rc);
161
162 goto fail;
163 }
164
165 DEV_MESSAGE (KERN_INFO, device,
166 "%04X/%02X(CU:%04X/%02X) %dMB at(%d B/blk)",
167 device->devinfo.sid_data.dev_type,
168 device->devinfo.sid_data.dev_model,
169 device->devinfo.sid_data.cu_type,
170 device->devinfo.sid_data.cu_model,
171 ((private->rdc_data.blk_bdsa *
172 (private->rdc_data.blk_size >> 9)) >> 11),
173 private->rdc_data.blk_size);
174
175 goto out;
176 fail:
177 if ( rc ) {
178 kfree(device->private);
179 device->private = NULL;
180 }
181
182 out:
183 return rc;
184 }
185
186 static int
dasd_fba_do_analysis(struct dasd_device_t * device)187 dasd_fba_do_analysis (struct dasd_device_t *device)
188 {
189 int rc = 0;
190 int sb;
191 dasd_fba_private_t *private = (dasd_fba_private_t *) device->private;
192 int bs = private->rdc_data.blk_size;
193
194 memset (&(device->sizes), 0, sizeof (dasd_sizes_t));
195 switch (bs) {
196 case 512:
197 case 1024:
198 case 2048:
199 case 4096:
200 device->sizes.bp_block = bs;
201 break;
202 default:
203
204 DEV_MESSAGE (KERN_INFO, device,
205 "unknown blocksize %d",
206 bs);
207
208 return -EMEDIUMTYPE;
209 }
210 device->sizes.s2b_shift = 0; /* bits to shift 512 to get a block */
211 for (sb = 512; sb < bs; sb = sb << 1)
212 device->sizes.s2b_shift++;
213
214 device->sizes.blocks = (private->rdc_data.blk_bdsa);
215 device->sizes.pt_block = 1;
216
217 return rc;
218 }
219
220 static int
dasd_fba_fill_geometry(struct dasd_device_t * device,struct hd_geometry * geo)221 dasd_fba_fill_geometry (struct dasd_device_t *device, struct hd_geometry *geo)
222 {
223 int rc = 0;
224 unsigned long sectors = device->sizes.blocks << device->sizes.s2b_shift;
225 unsigned long tracks = sectors >> 6;
226 unsigned long cyls = tracks >> 4;
227
228 switch (device->sizes.bp_block) {
229 case 512:
230 case 1024:
231 case 2048:
232 case 4096:
233 break;
234 default:
235 return -EINVAL;
236 }
237 geo->cylinders = cyls;
238 geo->heads = 16;
239 geo->sectors = 128 >> device->sizes.s2b_shift;
240 return rc;
241 }
242
243 static dasd_era_t
dasd_fba_examine_error(ccw_req_t * cqr,devstat_t * stat)244 dasd_fba_examine_error (ccw_req_t * cqr, devstat_t * stat)
245 {
246 dasd_device_t *device = (dasd_device_t *) cqr->device;
247 if (stat->cstat == 0x00 &&
248 stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
249 return dasd_era_none;
250
251 switch (device->devinfo.sid_data.dev_model) {
252 case 0x3370:
253 return dasd_3370_erp_examine (cqr, stat);
254 case 0x9336:
255 return dasd_9336_erp_examine (cqr, stat);
256 default:
257 return dasd_era_recover;
258 }
259 }
260
261 static dasd_erp_action_fn_t
dasd_fba_erp_action(ccw_req_t * cqr)262 dasd_fba_erp_action (ccw_req_t * cqr)
263 {
264 return dasd_default_erp_action;
265 }
266
267 static dasd_erp_postaction_fn_t
dasd_fba_erp_postaction(ccw_req_t * cqr)268 dasd_fba_erp_postaction (ccw_req_t * cqr)
269 {
270 if (cqr->function == dasd_default_erp_action)
271 return dasd_default_erp_postaction;
272
273 MESSAGE (KERN_WARNING,
274 "unknown ERP action %p",
275 cqr->function);
276
277 return NULL;
278 }
279
280 static ccw_req_t *
dasd_fba_build_cp_from_req(dasd_device_t * device,struct request * req)281 dasd_fba_build_cp_from_req (dasd_device_t * device, struct request *req)
282 {
283 ccw_req_t *rw_cp = NULL;
284 int rw_cmd;
285 int bhct, i = 0;
286 long size;
287 ccw1_t *ccw;
288 DE_fba_data_t *DE_data;
289 LO_fba_data_t *LO_data;
290 struct buffer_head *bh;
291 dasd_fba_private_t *private = (dasd_fba_private_t *) device->private;
292 int byt_per_blk = device->sizes.bp_block;
293 unsigned long reloc_sector = req->sector +
294 device->major_info->gendisk.part[MINOR (req->rq_dev)].start_sect;
295
296 if (req->cmd == READ) {
297 rw_cmd = DASD_FBA_CCW_READ;
298 } else if (req->cmd == WRITE) {
299 rw_cmd = DASD_FBA_CCW_WRITE;
300 } else {
301
302 MESSAGE (KERN_ERR,
303 "Unknown command %d\n",
304 req->cmd);
305
306 return ERR_PTR(-EINVAL);
307 }
308 /* Build the request */
309 /* count hs to prevent errors, when bh smaller than block */
310 bh = req -> bh;
311 bhct = 0;
312 while ( bh != NULL ) {
313 if (bh->b_size < byt_per_blk) {
314 BUG();
315 }
316 bhct += bh->b_size >> (device->sizes.s2b_shift+9);
317 bh = bh->b_reqnext;
318 }
319
320 if (private->rdc_data.mode.bits.data_chain) {
321 rw_cp = dasd_alloc_request (dasd_fba_discipline.name,
322 2 + bhct,
323 sizeof (DE_fba_data_t) +
324 sizeof (LO_fba_data_t),
325 device);
326 } else {
327 rw_cp = dasd_alloc_request (dasd_fba_discipline.name,
328 1 + 2 * bhct,
329 sizeof (DE_fba_data_t) +
330 bhct * sizeof (LO_fba_data_t),
331 device);
332 }
333 if (!rw_cp) {
334 return ERR_PTR(-ENOMEM);
335 }
336 DE_data = rw_cp->data;
337 LO_data = rw_cp->data + sizeof (DE_fba_data_t);
338 ccw = rw_cp->cpaddr;
339
340 if (define_extent (ccw, DE_data, req->cmd, byt_per_blk,
341 reloc_sector, req->nr_sectors, rw_cp, device)) {
342 goto clear_rw_cp;
343 }
344 ccw->flags |= CCW_FLAG_CC;
345 ccw ++;
346 locate_record (ccw, LO_data, req->cmd, 0,
347 private->rdc_data.mode.bits.data_chain ? bhct : 1, rw_cp, device);
348 if (ccw->cda == 0) {
349 goto clear_rw_cp;
350 }
351 ccw->flags |= CCW_FLAG_CC;
352
353 bh = req -> bh;
354 i = 0;
355 while ( bh != NULL ) {
356 for (size = 0; size < bh->b_size; size += byt_per_blk) {
357 ccw ++;
358 ccw->cmd_code = rw_cmd;
359 ccw->count = byt_per_blk;
360 if (dasd_set_normalized_cda (ccw,__pa (bh->b_data + size), rw_cp, device)) {
361 goto clear_rw_cp;
362 }
363 if (private->rdc_data.mode.bits.data_chain) {
364 ccw->flags |= CCW_FLAG_DC;
365 } else {
366 ccw->flags |= CCW_FLAG_CC;
367 }
368 }
369 bh = bh->b_reqnext;
370 if ( bh != NULL &&
371 !(private->rdc_data.mode.bits.data_chain)) {
372 ccw++;
373 i++;
374 LO_data++;
375 locate_record (ccw, LO_data, req->cmd, i, 1, rw_cp, device);
376 if (ccw->cda == 0) {
377 goto clear_rw_cp;
378 }
379 ccw->flags |= CCW_FLAG_CC;
380 }
381 }
382 ccw->flags &= ~(CCW_FLAG_DC | CCW_FLAG_CC);
383
384 rw_cp->device = device;
385 rw_cp->expires = 5 * TOD_MIN; /* 5 minutes */
386 rw_cp->req = req;
387 check_then_set (&rw_cp->status, CQR_STATUS_EMPTY, CQR_STATUS_FILLED);
388 goto out;
389 clear_rw_cp:
390 dasd_free_request (rw_cp, device);
391 rw_cp = NULL;
392 out:
393 return rw_cp;
394 }
395
396 static int
dasd_fba_fill_info(dasd_device_t * device,dasd_information2_t * info)397 dasd_fba_fill_info (dasd_device_t * device, dasd_information2_t * info)
398 {
399 int rc = 0;
400 info->label_block = 1;
401 info->FBA_layout = 1;
402 info->format = DASD_FORMAT_LDL;
403 info->characteristics_size = sizeof (dasd_fba_characteristics_t);
404 memcpy (info->characteristics,
405 &((dasd_fba_private_t *) device->private)->rdc_data,
406 sizeof (dasd_fba_characteristics_t));
407 info->confdata_size = 0;
408 return rc;
409 }
410
411
412 dasd_discipline_t dasd_fba_discipline = {
413 owner: THIS_MODULE,
414 name:"FBA ",
415 ebcname:"FBA ",
416 max_blocks:((PAGE_SIZE >> 1) / sizeof (ccw1_t) - 1),
417 id_check:dasd_fba_id_check,
418 check_characteristics:dasd_fba_check_characteristics,
419 do_analysis:dasd_fba_do_analysis,
420 fill_geometry:dasd_fba_fill_geometry,
421 start_IO:dasd_start_IO,
422 term_IO:dasd_term_IO,
423 examine_error:dasd_fba_examine_error,
424 erp_action:dasd_fba_erp_action,
425 erp_postaction:dasd_fba_erp_postaction,
426 build_cp_from_req:dasd_fba_build_cp_from_req,
427 int_handler:dasd_int_handler,
428 fill_info:dasd_fba_fill_info,
429 list:LIST_HEAD_INIT(dasd_fba_discipline.list),
430 };
431
432 int
dasd_fba_init(void)433 dasd_fba_init (void)
434 {
435 int rc = 0;
436
437 MESSAGE (KERN_INFO,
438 "%s discipline initializing",
439 dasd_fba_discipline.name);
440
441 ASCEBC (dasd_fba_discipline.ebcname, 4);
442 dasd_discipline_add (&dasd_fba_discipline);
443 #ifdef CONFIG_DASD_DYNAMIC
444 {
445 int i;
446 for (i = 0;
447 i < sizeof (dasd_fba_known_devices) / sizeof (devreg_t);
448 i++) {
449
450 MESSAGE (KERN_INFO,
451 "We are interested in: "
452 "Dev %04X/%02X @ CU %04X/%02x",
453 dasd_fba_known_devices[i].ci.hc.dtype,
454 dasd_fba_known_devices[i].ci.hc.dmode,
455 dasd_fba_known_devices[i].ci.hc.ctype,
456 dasd_fba_known_devices[i].ci.hc.cmode);
457
458 s390_device_register (&dasd_fba_known_devices[i]);
459 }
460 }
461 #endif /* CONFIG_DASD_DYNAMIC */
462 return rc;
463 }
464
465 void
dasd_fba_cleanup(void)466 dasd_fba_cleanup( void ) {
467
468 MESSAGE (KERN_INFO,
469 "%s discipline cleaning up",
470 dasd_fba_discipline.name);
471
472 #ifdef CONFIG_DASD_DYNAMIC
473 {
474 int i;
475 for ( i=0; i<sizeof(dasd_fba_known_devices)/sizeof(devreg_t); i++) {
476 s390_device_unregister(&dasd_fba_known_devices[i]);
477 }
478 }
479 #endif /* CONFIG_DASD_DYNAMIC */
480 dasd_discipline_del(&dasd_fba_discipline);
481 }
482
483 #ifdef MODULE
484 int
init_module(void)485 init_module (void)
486 {
487 int rc = 0;
488 rc = dasd_fba_init ();
489 return rc;
490 }
491
492 void
cleanup_module(void)493 cleanup_module (void)
494 {
495 dasd_fba_cleanup ();
496 return;
497 }
498 #endif
499
500
501 /*
502 * Overrides for Emacs so that we follow Linus's tabbing style.
503 * Emacs will notice this stuff at the end of the file and automatically
504 * adjust the settings for this buffer only. This must remain at the end
505 * of the file.
506 * ---------------------------------------------------------------------------
507 * Local variables:
508 * c-indent-level: 4
509 * c-brace-imaginary-offset: 0
510 * c-brace-offset: -4
511 * c-argdecl-indent: 4
512 * c-label-offset: -4
513 * c-continued-statement-offset: 4
514 * c-continued-brace-offset: 0
515 * indent-tabs-mode: nil
516 * tab-width: 8
517 * End:
518 */
519