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