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