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