xref: /openbmc/linux/drivers/s390/block/dasd_eckd.c (revision 17283b56)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * File...........: linux/drivers/s390/block/dasd_eckd.c
31da177e4SLinus Torvalds  * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
41da177e4SLinus Torvalds  *		    Horst Hummel <Horst.Hummel@de.ibm.com>
51da177e4SLinus Torvalds  *		    Carsten Otte <Cotte@de.ibm.com>
61da177e4SLinus Torvalds  *		    Martin Schwidefsky <schwidefsky@de.ibm.com>
71da177e4SLinus Torvalds  * Bugreports.to..: <Linux390@de.ibm.com>
81da177e4SLinus Torvalds  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/stddef.h>
131da177e4SLinus Torvalds #include <linux/kernel.h>
141da177e4SLinus Torvalds #include <linux/slab.h>
151da177e4SLinus Torvalds #include <linux/hdreg.h>	/* HDIO_GETGEO			    */
161da177e4SLinus Torvalds #include <linux/bio.h>
171da177e4SLinus Torvalds #include <linux/module.h>
181da177e4SLinus Torvalds #include <linux/init.h>
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds #include <asm/debug.h>
211da177e4SLinus Torvalds #include <asm/idals.h>
221da177e4SLinus Torvalds #include <asm/ebcdic.h>
231da177e4SLinus Torvalds #include <asm/io.h>
241da177e4SLinus Torvalds #include <asm/todclk.h>
251da177e4SLinus Torvalds #include <asm/uaccess.h>
2640545573SHorst Hummel #include <asm/cio.h>
271da177e4SLinus Torvalds #include <asm/ccwdev.h>
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #include "dasd_int.h"
301da177e4SLinus Torvalds #include "dasd_eckd.h"
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds #ifdef PRINTK_HEADER
331da177e4SLinus Torvalds #undef PRINTK_HEADER
341da177e4SLinus Torvalds #endif				/* PRINTK_HEADER */
351da177e4SLinus Torvalds #define PRINTK_HEADER "dasd(eckd):"
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds #define ECKD_C0(i) (i->home_bytes)
381da177e4SLinus Torvalds #define ECKD_F(i) (i->formula)
391da177e4SLinus Torvalds #define ECKD_F1(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f1):\
401da177e4SLinus Torvalds 		    (i->factors.f_0x02.f1))
411da177e4SLinus Torvalds #define ECKD_F2(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f2):\
421da177e4SLinus Torvalds 		    (i->factors.f_0x02.f2))
431da177e4SLinus Torvalds #define ECKD_F3(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f3):\
441da177e4SLinus Torvalds 		    (i->factors.f_0x02.f3))
451da177e4SLinus Torvalds #define ECKD_F4(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f4):0)
461da177e4SLinus Torvalds #define ECKD_F5(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f5):0)
471da177e4SLinus Torvalds #define ECKD_F6(i) (i->factor6)
481da177e4SLinus Torvalds #define ECKD_F7(i) (i->factor7)
491da177e4SLinus Torvalds #define ECKD_F8(i) (i->factor8)
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds MODULE_LICENSE("GPL");
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds static struct dasd_discipline dasd_eckd_discipline;
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds struct dasd_eckd_private {
561da177e4SLinus Torvalds 	struct dasd_eckd_characteristics rdc_data;
571da177e4SLinus Torvalds 	struct dasd_eckd_confdata conf_data;
581da177e4SLinus Torvalds 	struct dasd_eckd_path path_data;
591da177e4SLinus Torvalds 	struct eckd_count count_area[5];
601da177e4SLinus Torvalds 	int init_cqr_status;
611da177e4SLinus Torvalds 	int uses_cdl;
621da177e4SLinus Torvalds 	struct attrib_data_t attrib;	/* e.g. cache operations */
631da177e4SLinus Torvalds };
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds /* The ccw bus type uses this table to find devices that it sends to
661da177e4SLinus Torvalds  * dasd_eckd_probe */
671da177e4SLinus Torvalds static struct ccw_device_id dasd_eckd_ids[] = {
68d2c993d8SHeiko Carstens 	{ CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), .driver_info = 0x1},
69d2c993d8SHeiko Carstens 	{ CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), .driver_info = 0x2},
70d2c993d8SHeiko Carstens 	{ CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3390, 0), .driver_info = 0x3},
71d2c993d8SHeiko Carstens 	{ CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), .driver_info = 0x4},
72d2c993d8SHeiko Carstens 	{ CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), .driver_info = 0x5},
73d2c993d8SHeiko Carstens 	{ CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), .driver_info = 0x6},
74d2c993d8SHeiko Carstens 	{ CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3390, 0), .driver_info = 0x7},
75d2c993d8SHeiko Carstens 	{ CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3380, 0), .driver_info = 0x8},
76d2c993d8SHeiko Carstens 	{ CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3390, 0), .driver_info = 0x9},
77d2c993d8SHeiko Carstens 	{ CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3380, 0), .driver_info = 0xa},
781da177e4SLinus Torvalds 	{ /* end of list */ },
791da177e4SLinus Torvalds };
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids);
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds static struct ccw_driver dasd_eckd_driver; /* see below */
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds /* initial attempt at a probe function. this can be simplified once
861da177e4SLinus Torvalds  * the other detection code is gone */
871da177e4SLinus Torvalds static int
881da177e4SLinus Torvalds dasd_eckd_probe (struct ccw_device *cdev)
891da177e4SLinus Torvalds {
901da177e4SLinus Torvalds 	int ret;
911da177e4SLinus Torvalds 
9240545573SHorst Hummel 	/* set ECKD specific ccw-device options */
9340545573SHorst Hummel 	ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE);
9440545573SHorst Hummel 	if (ret) {
9540545573SHorst Hummel 		printk(KERN_WARNING
9640545573SHorst Hummel 		       "dasd_eckd_probe: could not set ccw-device options "
9740545573SHorst Hummel 		       "for %s\n", cdev->dev.bus_id);
981da177e4SLinus Torvalds 		return ret;
9940545573SHorst Hummel 	}
10040545573SHorst Hummel 	ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);
10140545573SHorst Hummel 	return ret;
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds static int
1051da177e4SLinus Torvalds dasd_eckd_set_online(struct ccw_device *cdev)
1061da177e4SLinus Torvalds {
1071da177e4SLinus Torvalds 	return dasd_generic_set_online(cdev, &dasd_eckd_discipline);
1081da177e4SLinus Torvalds }
1091da177e4SLinus Torvalds 
1101da177e4SLinus Torvalds static struct ccw_driver dasd_eckd_driver = {
1111da177e4SLinus Torvalds 	.name        = "dasd-eckd",
1121da177e4SLinus Torvalds 	.owner       = THIS_MODULE,
1131da177e4SLinus Torvalds 	.ids         = dasd_eckd_ids,
1141da177e4SLinus Torvalds 	.probe       = dasd_eckd_probe,
1151da177e4SLinus Torvalds 	.remove      = dasd_generic_remove,
1161da177e4SLinus Torvalds 	.set_offline = dasd_generic_set_offline,
1171da177e4SLinus Torvalds 	.set_online  = dasd_eckd_set_online,
1181da177e4SLinus Torvalds 	.notify      = dasd_generic_notify,
1191da177e4SLinus Torvalds };
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds static const int sizes_trk0[] = { 28, 148, 84 };
1221da177e4SLinus Torvalds #define LABEL_SIZE 140
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds static inline unsigned int
1251da177e4SLinus Torvalds round_up_multiple(unsigned int no, unsigned int mult)
1261da177e4SLinus Torvalds {
1271da177e4SLinus Torvalds 	int rem = no % mult;
1281da177e4SLinus Torvalds 	return (rem ? no - rem + mult : no);
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds static inline unsigned int
1321da177e4SLinus Torvalds ceil_quot(unsigned int d1, unsigned int d2)
1331da177e4SLinus Torvalds {
1341da177e4SLinus Torvalds 	return (d1 + (d2 - 1)) / d2;
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds 
1374d284cacSHeiko Carstens static unsigned int
1381da177e4SLinus Torvalds recs_per_track(struct dasd_eckd_characteristics * rdc,
1391da177e4SLinus Torvalds 	       unsigned int kl, unsigned int dl)
1401da177e4SLinus Torvalds {
1411da177e4SLinus Torvalds 	int dn, kn;
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	switch (rdc->dev_type) {
1441da177e4SLinus Torvalds 	case 0x3380:
1451da177e4SLinus Torvalds 		if (kl)
1461da177e4SLinus Torvalds 			return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) +
1471da177e4SLinus Torvalds 				       ceil_quot(dl + 12, 32));
1481da177e4SLinus Torvalds 		else
1491da177e4SLinus Torvalds 			return 1499 / (15 + ceil_quot(dl + 12, 32));
1501da177e4SLinus Torvalds 	case 0x3390:
1511da177e4SLinus Torvalds 		dn = ceil_quot(dl + 6, 232) + 1;
1521da177e4SLinus Torvalds 		if (kl) {
1531da177e4SLinus Torvalds 			kn = ceil_quot(kl + 6, 232) + 1;
1541da177e4SLinus Torvalds 			return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) +
1551da177e4SLinus Torvalds 				       9 + ceil_quot(dl + 6 * dn, 34));
1561da177e4SLinus Torvalds 		} else
1571da177e4SLinus Torvalds 			return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34));
1581da177e4SLinus Torvalds 	case 0x9345:
1591da177e4SLinus Torvalds 		dn = ceil_quot(dl + 6, 232) + 1;
1601da177e4SLinus Torvalds 		if (kl) {
1611da177e4SLinus Torvalds 			kn = ceil_quot(kl + 6, 232) + 1;
1621da177e4SLinus Torvalds 			return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) +
1631da177e4SLinus Torvalds 				       ceil_quot(dl + 6 * dn, 34));
1641da177e4SLinus Torvalds 		} else
1651da177e4SLinus Torvalds 			return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34));
1661da177e4SLinus Torvalds 	}
1671da177e4SLinus Torvalds 	return 0;
1681da177e4SLinus Torvalds }
1691da177e4SLinus Torvalds 
1704d284cacSHeiko Carstens static int
1711da177e4SLinus Torvalds check_XRC (struct ccw1         *de_ccw,
1721da177e4SLinus Torvalds            struct DE_eckd_data *data,
1731da177e4SLinus Torvalds            struct dasd_device  *device)
1741da177e4SLinus Torvalds {
1751da177e4SLinus Torvalds         struct dasd_eckd_private *private;
176d54853efSMartin Schwidefsky 	int rc;
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds         private = (struct dasd_eckd_private *) device->private;
179d54853efSMartin Schwidefsky 	if (!private->rdc_data.facilities.XRC_supported)
180d54853efSMartin Schwidefsky 		return 0;
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds         /* switch on System Time Stamp - needed for XRC Support */
1831da177e4SLinus Torvalds 	data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid'   */
1841da177e4SLinus Torvalds 	data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
1851da177e4SLinus Torvalds 
186d54853efSMartin Schwidefsky 	rc = get_sync_clock(&data->ep_sys_time);
187d54853efSMartin Schwidefsky 	/* Ignore return code if sync clock is switched off. */
188d54853efSMartin Schwidefsky 	if (rc == -ENOSYS || rc == -EACCES)
189d54853efSMartin Schwidefsky 		rc = 0;
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds 	de_ccw->count = sizeof (struct DE_eckd_data);
1921da177e4SLinus Torvalds 	de_ccw->flags |= CCW_FLAG_SLI;
193d54853efSMartin Schwidefsky 	return rc;
1941da177e4SLinus Torvalds }
1951da177e4SLinus Torvalds 
1964d284cacSHeiko Carstens static int
1971da177e4SLinus Torvalds define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
1981da177e4SLinus Torvalds 	      int totrk, int cmd, struct dasd_device * device)
1991da177e4SLinus Torvalds {
2001da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
2011da177e4SLinus Torvalds 	struct ch_t geo, beg, end;
202d54853efSMartin Schwidefsky 	int rc = 0;
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) device->private;
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds 	ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT;
2071da177e4SLinus Torvalds 	ccw->flags = 0;
2081da177e4SLinus Torvalds 	ccw->count = 16;
2091da177e4SLinus Torvalds 	ccw->cda = (__u32) __pa(data);
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	memset(data, 0, sizeof (struct DE_eckd_data));
2121da177e4SLinus Torvalds 	switch (cmd) {
2131da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_HOME_ADDRESS:
2141da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_RECORD_ZERO:
2151da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ:
2161da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_MT:
2171da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_CKD:
2181da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_CKD_MT:
2191da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_KD:
2201da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_KD_MT:
2211da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_COUNT:
2221da177e4SLinus Torvalds 		data->mask.perm = 0x1;
2231da177e4SLinus Torvalds 		data->attributes.operation = private->attrib.operation;
2241da177e4SLinus Torvalds 		break;
2251da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE:
2261da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_MT:
2271da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_KD:
2281da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_KD_MT:
2291da177e4SLinus Torvalds 		data->mask.perm = 0x02;
2301da177e4SLinus Torvalds 		data->attributes.operation = private->attrib.operation;
231d54853efSMartin Schwidefsky 		rc = check_XRC (ccw, data, device);
2321da177e4SLinus Torvalds 		break;
2331da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_CKD:
2341da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_CKD_MT:
2351da177e4SLinus Torvalds 		data->attributes.operation = DASD_BYPASS_CACHE;
236d54853efSMartin Schwidefsky 		rc = check_XRC (ccw, data, device);
2371da177e4SLinus Torvalds 		break;
2381da177e4SLinus Torvalds 	case DASD_ECKD_CCW_ERASE:
2391da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
2401da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_RECORD_ZERO:
2411da177e4SLinus Torvalds 		data->mask.perm = 0x3;
2421da177e4SLinus Torvalds 		data->mask.auth = 0x1;
2431da177e4SLinus Torvalds 		data->attributes.operation = DASD_BYPASS_CACHE;
244d54853efSMartin Schwidefsky 		rc = check_XRC (ccw, data, device);
2451da177e4SLinus Torvalds 		break;
2461da177e4SLinus Torvalds 	default:
2471da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd);
2481da177e4SLinus Torvalds 		break;
2491da177e4SLinus Torvalds 	}
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds 	data->attributes.mode = 0x3;	/* ECKD */
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds 	if ((private->rdc_data.cu_type == 0x2105 ||
2541da177e4SLinus Torvalds 	     private->rdc_data.cu_type == 0x2107 ||
2551da177e4SLinus Torvalds 	     private->rdc_data.cu_type == 0x1750)
2561da177e4SLinus Torvalds 	    && !(private->uses_cdl && trk < 2))
2571da177e4SLinus Torvalds 		data->ga_extended |= 0x40; /* Regular Data Format Mode */
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	geo.cyl = private->rdc_data.no_cyl;
2601da177e4SLinus Torvalds 	geo.head = private->rdc_data.trk_per_cyl;
2611da177e4SLinus Torvalds 	beg.cyl = trk / geo.head;
2621da177e4SLinus Torvalds 	beg.head = trk % geo.head;
2631da177e4SLinus Torvalds 	end.cyl = totrk / geo.head;
2641da177e4SLinus Torvalds 	end.head = totrk % geo.head;
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds 	/* check for sequential prestage - enhance cylinder range */
2671da177e4SLinus Torvalds 	if (data->attributes.operation == DASD_SEQ_PRESTAGE ||
2681da177e4SLinus Torvalds 	    data->attributes.operation == DASD_SEQ_ACCESS) {
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds 		if (end.cyl + private->attrib.nr_cyl < geo.cyl)
2711da177e4SLinus Torvalds 			end.cyl += private->attrib.nr_cyl;
2721da177e4SLinus Torvalds 		else
2731da177e4SLinus Torvalds 			end.cyl = (geo.cyl - 1);
2741da177e4SLinus Torvalds 	}
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 	data->beg_ext.cyl = beg.cyl;
2771da177e4SLinus Torvalds 	data->beg_ext.head = beg.head;
2781da177e4SLinus Torvalds 	data->end_ext.cyl = end.cyl;
2791da177e4SLinus Torvalds 	data->end_ext.head = end.head;
280d54853efSMartin Schwidefsky 	return rc;
2811da177e4SLinus Torvalds }
2821da177e4SLinus Torvalds 
2834d284cacSHeiko Carstens static void
2841da177e4SLinus Torvalds locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
2851da177e4SLinus Torvalds 	      int rec_on_trk, int no_rec, int cmd,
2861da177e4SLinus Torvalds 	      struct dasd_device * device, int reclen)
2871da177e4SLinus Torvalds {
2881da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
2891da177e4SLinus Torvalds 	int sector;
2901da177e4SLinus Torvalds 	int dn, d;
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) device->private;
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds 	DBF_DEV_EVENT(DBF_INFO, device,
2951da177e4SLinus Torvalds 		  "Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d",
2961da177e4SLinus Torvalds 		  trk, rec_on_trk, no_rec, cmd, reclen);
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds 	ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD;
2991da177e4SLinus Torvalds 	ccw->flags = 0;
3001da177e4SLinus Torvalds 	ccw->count = 16;
3011da177e4SLinus Torvalds 	ccw->cda = (__u32) __pa(data);
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	memset(data, 0, sizeof (struct LO_eckd_data));
3041da177e4SLinus Torvalds 	sector = 0;
3051da177e4SLinus Torvalds 	if (rec_on_trk) {
3061da177e4SLinus Torvalds 		switch (private->rdc_data.dev_type) {
3071da177e4SLinus Torvalds 		case 0x3390:
3081da177e4SLinus Torvalds 			dn = ceil_quot(reclen + 6, 232);
3091da177e4SLinus Torvalds 			d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34);
3101da177e4SLinus Torvalds 			sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8;
3111da177e4SLinus Torvalds 			break;
3121da177e4SLinus Torvalds 		case 0x3380:
3131da177e4SLinus Torvalds 			d = 7 + ceil_quot(reclen + 12, 32);
3141da177e4SLinus Torvalds 			sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7;
3151da177e4SLinus Torvalds 			break;
3161da177e4SLinus Torvalds 		}
3171da177e4SLinus Torvalds 	}
3181da177e4SLinus Torvalds 	data->sector = sector;
3191da177e4SLinus Torvalds 	data->count = no_rec;
3201da177e4SLinus Torvalds 	switch (cmd) {
3211da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
3221da177e4SLinus Torvalds 		data->operation.orientation = 0x3;
3231da177e4SLinus Torvalds 		data->operation.operation = 0x03;
3241da177e4SLinus Torvalds 		break;
3251da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_HOME_ADDRESS:
3261da177e4SLinus Torvalds 		data->operation.orientation = 0x3;
3271da177e4SLinus Torvalds 		data->operation.operation = 0x16;
3281da177e4SLinus Torvalds 		break;
3291da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_RECORD_ZERO:
3301da177e4SLinus Torvalds 		data->operation.orientation = 0x1;
3311da177e4SLinus Torvalds 		data->operation.operation = 0x03;
3321da177e4SLinus Torvalds 		data->count++;
3331da177e4SLinus Torvalds 		break;
3341da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_RECORD_ZERO:
3351da177e4SLinus Torvalds 		data->operation.orientation = 0x3;
3361da177e4SLinus Torvalds 		data->operation.operation = 0x16;
3371da177e4SLinus Torvalds 		data->count++;
3381da177e4SLinus Torvalds 		break;
3391da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE:
3401da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_MT:
3411da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_KD:
3421da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_KD_MT:
3431da177e4SLinus Torvalds 		data->auxiliary.last_bytes_used = 0x1;
3441da177e4SLinus Torvalds 		data->length = reclen;
3451da177e4SLinus Torvalds 		data->operation.operation = 0x01;
3461da177e4SLinus Torvalds 		break;
3471da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_CKD:
3481da177e4SLinus Torvalds 	case DASD_ECKD_CCW_WRITE_CKD_MT:
3491da177e4SLinus Torvalds 		data->auxiliary.last_bytes_used = 0x1;
3501da177e4SLinus Torvalds 		data->length = reclen;
3511da177e4SLinus Torvalds 		data->operation.operation = 0x03;
3521da177e4SLinus Torvalds 		break;
3531da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ:
3541da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_MT:
3551da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_KD:
3561da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_KD_MT:
3571da177e4SLinus Torvalds 		data->auxiliary.last_bytes_used = 0x1;
3581da177e4SLinus Torvalds 		data->length = reclen;
3591da177e4SLinus Torvalds 		data->operation.operation = 0x06;
3601da177e4SLinus Torvalds 		break;
3611da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_CKD:
3621da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_CKD_MT:
3631da177e4SLinus Torvalds 		data->auxiliary.last_bytes_used = 0x1;
3641da177e4SLinus Torvalds 		data->length = reclen;
3651da177e4SLinus Torvalds 		data->operation.operation = 0x16;
3661da177e4SLinus Torvalds 		break;
3671da177e4SLinus Torvalds 	case DASD_ECKD_CCW_READ_COUNT:
3681da177e4SLinus Torvalds 		data->operation.operation = 0x06;
3691da177e4SLinus Torvalds 		break;
3701da177e4SLinus Torvalds 	case DASD_ECKD_CCW_ERASE:
3711da177e4SLinus Torvalds 		data->length = reclen;
3721da177e4SLinus Torvalds 		data->auxiliary.last_bytes_used = 0x1;
3731da177e4SLinus Torvalds 		data->operation.operation = 0x0b;
3741da177e4SLinus Torvalds 		break;
3751da177e4SLinus Torvalds 	default:
3761da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd);
3771da177e4SLinus Torvalds 	}
3781da177e4SLinus Torvalds 	data->seek_addr.cyl = data->search_arg.cyl =
3791da177e4SLinus Torvalds 		trk / private->rdc_data.trk_per_cyl;
3801da177e4SLinus Torvalds 	data->seek_addr.head = data->search_arg.head =
3811da177e4SLinus Torvalds 		trk % private->rdc_data.trk_per_cyl;
3821da177e4SLinus Torvalds 	data->search_arg.record = rec_on_trk;
3831da177e4SLinus Torvalds }
3841da177e4SLinus Torvalds 
3851da177e4SLinus Torvalds /*
3861da177e4SLinus Torvalds  * Returns 1 if the block is one of the special blocks that needs
3871da177e4SLinus Torvalds  * to get read/written with the KD variant of the command.
3881da177e4SLinus Torvalds  * That is DASD_ECKD_READ_KD_MT instead of DASD_ECKD_READ_MT and
3891da177e4SLinus Torvalds  * DASD_ECKD_WRITE_KD_MT instead of DASD_ECKD_WRITE_MT.
3901da177e4SLinus Torvalds  * Luckily the KD variants differ only by one bit (0x08) from the
3911da177e4SLinus Torvalds  * normal variant. So don't wonder about code like:
3921da177e4SLinus Torvalds  * if (dasd_eckd_cdl_special(blk_per_trk, recid))
3931da177e4SLinus Torvalds  *         ccw->cmd_code |= 0x8;
3941da177e4SLinus Torvalds  */
3951da177e4SLinus Torvalds static inline int
3961da177e4SLinus Torvalds dasd_eckd_cdl_special(int blk_per_trk, int recid)
3971da177e4SLinus Torvalds {
3981da177e4SLinus Torvalds 	if (recid < 3)
3991da177e4SLinus Torvalds 		return 1;
4001da177e4SLinus Torvalds 	if (recid < blk_per_trk)
4011da177e4SLinus Torvalds 		return 0;
4021da177e4SLinus Torvalds 	if (recid < 2 * blk_per_trk)
4031da177e4SLinus Torvalds 		return 1;
4041da177e4SLinus Torvalds 	return 0;
4051da177e4SLinus Torvalds }
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds /*
4081da177e4SLinus Torvalds  * Returns the record size for the special blocks of the cdl format.
4091da177e4SLinus Torvalds  * Only returns something useful if dasd_eckd_cdl_special is true
4101da177e4SLinus Torvalds  * for the recid.
4111da177e4SLinus Torvalds  */
4121da177e4SLinus Torvalds static inline int
4131da177e4SLinus Torvalds dasd_eckd_cdl_reclen(int recid)
4141da177e4SLinus Torvalds {
4151da177e4SLinus Torvalds 	if (recid < 3)
4161da177e4SLinus Torvalds 		return sizes_trk0[recid];
4171da177e4SLinus Torvalds 	return LABEL_SIZE;
4181da177e4SLinus Torvalds }
4191da177e4SLinus Torvalds 
4203d052595SHorst Hummel /*
4213d052595SHorst Hummel  * Generate device unique id that specifies the physical device.
4223d052595SHorst Hummel  */
4233d052595SHorst Hummel static int
4243d052595SHorst Hummel dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid)
4253d052595SHorst Hummel {
4263d052595SHorst Hummel 	struct dasd_eckd_private *private;
4273d052595SHorst Hummel 	struct dasd_eckd_confdata *confdata;
4283d052595SHorst Hummel 
4293d052595SHorst Hummel 	private = (struct dasd_eckd_private *) device->private;
4303d052595SHorst Hummel 	if (!private)
4313d052595SHorst Hummel 		return -ENODEV;
4323d052595SHorst Hummel 	confdata = &private->conf_data;
4333d052595SHorst Hummel 	if (!confdata)
4343d052595SHorst Hummel 		return -ENODEV;
4353d052595SHorst Hummel 
4363d052595SHorst Hummel 	memset(uid, 0, sizeof(struct dasd_uid));
437d0710c7cSHorst Hummel 	memcpy(uid->vendor, confdata->ned1.HDA_manufacturer,
4383d052595SHorst Hummel 	       sizeof(uid->vendor) - 1);
4393d052595SHorst Hummel 	EBCASC(uid->vendor, sizeof(uid->vendor) - 1);
440d0710c7cSHorst Hummel 	memcpy(uid->serial, confdata->ned1.HDA_location,
4413d052595SHorst Hummel 	       sizeof(uid->serial) - 1);
4423d052595SHorst Hummel 	EBCASC(uid->serial, sizeof(uid->serial) - 1);
4433d052595SHorst Hummel 	uid->ssid = confdata->neq.subsystemID;
4443d052595SHorst Hummel 	if (confdata->ned2.sneq.flags == 0x40) {
4453d052595SHorst Hummel 		uid->alias = 1;
4463d052595SHorst Hummel 		uid->unit_addr = confdata->ned2.sneq.base_unit_addr;
4473d052595SHorst Hummel 	} else
4483d052595SHorst Hummel 		uid->unit_addr = confdata->ned1.unit_addr;
4493d052595SHorst Hummel 
4503d052595SHorst Hummel 	return 0;
4513d052595SHorst Hummel }
4523d052595SHorst Hummel 
45317283b56SCornelia Huck struct dasd_ccw_req * dasd_eckd_build_rcd_lpm(struct dasd_device *device,
45417283b56SCornelia Huck 					      void *rcd_buffer,
45517283b56SCornelia Huck 					      struct ciw *ciw, __u8 lpm)
45617283b56SCornelia Huck {
45717283b56SCornelia Huck 	struct dasd_ccw_req *cqr;
45817283b56SCornelia Huck 	struct ccw1 *ccw;
45917283b56SCornelia Huck 
46017283b56SCornelia Huck 	cqr = dasd_smalloc_request("ECKD", 1 /* RCD */, ciw->count, device);
46117283b56SCornelia Huck 
46217283b56SCornelia Huck 	if (IS_ERR(cqr)) {
46317283b56SCornelia Huck 		DEV_MESSAGE(KERN_WARNING, device, "%s",
46417283b56SCornelia Huck 			    "Could not allocate RCD request");
46517283b56SCornelia Huck 		return cqr;
46617283b56SCornelia Huck 	}
46717283b56SCornelia Huck 
46817283b56SCornelia Huck 	ccw = cqr->cpaddr;
46917283b56SCornelia Huck 	ccw->cmd_code = ciw->cmd;
47017283b56SCornelia Huck 	ccw->cda = (__u32)(addr_t)rcd_buffer;
47117283b56SCornelia Huck 	ccw->count = ciw->count;
47217283b56SCornelia Huck 
47317283b56SCornelia Huck 	cqr->device = device;
47417283b56SCornelia Huck 	cqr->expires = 10*HZ;
47517283b56SCornelia Huck 	cqr->lpm = lpm;
47617283b56SCornelia Huck 	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
47717283b56SCornelia Huck 	cqr->retries = 2;
47817283b56SCornelia Huck 	cqr->buildclk = get_clock();
47917283b56SCornelia Huck 	cqr->status = DASD_CQR_FILLED;
48017283b56SCornelia Huck 	return cqr;
48117283b56SCornelia Huck }
48217283b56SCornelia Huck 
48317283b56SCornelia Huck static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
48417283b56SCornelia Huck 				   void **rcd_buffer,
48517283b56SCornelia Huck 				   int *rcd_buffer_size, __u8 lpm)
48617283b56SCornelia Huck {
48717283b56SCornelia Huck 	struct ciw *ciw;
48817283b56SCornelia Huck 	char *rcd_buf = NULL;
48917283b56SCornelia Huck 	int ret;
49017283b56SCornelia Huck 	struct dasd_ccw_req *cqr;
49117283b56SCornelia Huck 
49217283b56SCornelia Huck 	/*
49317283b56SCornelia Huck 	 * scan for RCD command in extended SenseID data
49417283b56SCornelia Huck 	 */
49517283b56SCornelia Huck 	ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD);
49617283b56SCornelia Huck 	if (!ciw || ciw->cmd == 0) {
49717283b56SCornelia Huck 		ret = -EOPNOTSUPP;
49817283b56SCornelia Huck 		goto out_error;
49917283b56SCornelia Huck 	}
50017283b56SCornelia Huck 	rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA);
50117283b56SCornelia Huck 	if (!rcd_buf) {
50217283b56SCornelia Huck 		ret = -ENOMEM;
50317283b56SCornelia Huck 		goto out_error;
50417283b56SCornelia Huck 	}
50517283b56SCornelia Huck 	cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm);
50617283b56SCornelia Huck 	if (IS_ERR(cqr)) {
50717283b56SCornelia Huck 		ret =  PTR_ERR(cqr);
50817283b56SCornelia Huck 		goto out_error;
50917283b56SCornelia Huck 	}
51017283b56SCornelia Huck 	ret = dasd_sleep_on(cqr);
51117283b56SCornelia Huck 	/*
51217283b56SCornelia Huck 	 * on success we update the user input parms
51317283b56SCornelia Huck 	 */
51417283b56SCornelia Huck 	dasd_sfree_request(cqr, cqr->device);
51517283b56SCornelia Huck 	if (ret)
51617283b56SCornelia Huck 		goto out_error;
51717283b56SCornelia Huck 
51817283b56SCornelia Huck 	*rcd_buffer_size = ciw->count;
51917283b56SCornelia Huck 	*rcd_buffer = rcd_buf;
52017283b56SCornelia Huck 	return 0;
52117283b56SCornelia Huck out_error:
52217283b56SCornelia Huck 	kfree(rcd_buf);
52317283b56SCornelia Huck 	*rcd_buffer = NULL;
52417283b56SCornelia Huck 	*rcd_buffer_size = 0;
52517283b56SCornelia Huck 	return ret;
52617283b56SCornelia Huck }
52717283b56SCornelia Huck 
5281da177e4SLinus Torvalds static int
5291da177e4SLinus Torvalds dasd_eckd_read_conf(struct dasd_device *device)
5301da177e4SLinus Torvalds {
5311da177e4SLinus Torvalds 	void *conf_data;
5321da177e4SLinus Torvalds 	int conf_len, conf_data_saved;
5331da177e4SLinus Torvalds 	int rc;
5341da177e4SLinus Torvalds 	__u8 lpm;
5351da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
5361da177e4SLinus Torvalds 	struct dasd_eckd_path *path_data;
5371da177e4SLinus Torvalds 
5381da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) device->private;
5391da177e4SLinus Torvalds 	path_data = (struct dasd_eckd_path *) &private->path_data;
5401da177e4SLinus Torvalds 	path_data->opm = ccw_device_get_path_mask(device->cdev);
5411da177e4SLinus Torvalds 	lpm = 0x80;
5421da177e4SLinus Torvalds 	conf_data_saved = 0;
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	/* get configuration data per operational path */
5451da177e4SLinus Torvalds 	for (lpm = 0x80; lpm; lpm>>= 1) {
5461da177e4SLinus Torvalds 		if (lpm & path_data->opm){
54717283b56SCornelia Huck 			rc = dasd_eckd_read_conf_lpm(device, &conf_data,
5481da177e4SLinus Torvalds 						     &conf_len, lpm);
5491da177e4SLinus Torvalds 			if (rc && rc != -EOPNOTSUPP) {	/* -EOPNOTSUPP is ok */
5501da177e4SLinus Torvalds 				MESSAGE(KERN_WARNING,
5511da177e4SLinus Torvalds 					"Read configuration data returned "
5521da177e4SLinus Torvalds 					"error %d", rc);
5531da177e4SLinus Torvalds 				return rc;
5541da177e4SLinus Torvalds 			}
5551da177e4SLinus Torvalds 			if (conf_data == NULL) {
5561da177e4SLinus Torvalds 				MESSAGE(KERN_WARNING, "%s", "No configuration "
5571da177e4SLinus Torvalds 					"data retrieved");
5581da177e4SLinus Torvalds 				continue;	/* no errror */
5591da177e4SLinus Torvalds 			}
5601da177e4SLinus Torvalds 			if (conf_len != sizeof (struct dasd_eckd_confdata)) {
5611da177e4SLinus Torvalds 				MESSAGE(KERN_WARNING,
5621da177e4SLinus Torvalds 					"sizes of configuration data mismatch"
5631da177e4SLinus Torvalds 					"%d (read) vs %ld (expected)",
5641da177e4SLinus Torvalds 					conf_len,
5651da177e4SLinus Torvalds 					sizeof (struct dasd_eckd_confdata));
5661da177e4SLinus Torvalds 				kfree(conf_data);
5671da177e4SLinus Torvalds 				continue;	/* no errror */
5681da177e4SLinus Torvalds 			}
5691da177e4SLinus Torvalds 			/* save first valid configuration data */
5701da177e4SLinus Torvalds 			if (!conf_data_saved){
5711da177e4SLinus Torvalds 				memcpy(&private->conf_data, conf_data,
5721da177e4SLinus Torvalds 				       sizeof (struct dasd_eckd_confdata));
5731da177e4SLinus Torvalds 				conf_data_saved++;
5741da177e4SLinus Torvalds 			}
5751da177e4SLinus Torvalds 			switch (((char *)conf_data)[242] & 0x07){
5761da177e4SLinus Torvalds 			case 0x02:
5771da177e4SLinus Torvalds 				path_data->npm |= lpm;
5781da177e4SLinus Torvalds 				break;
5791da177e4SLinus Torvalds 			case 0x03:
5801da177e4SLinus Torvalds 				path_data->ppm |= lpm;
5811da177e4SLinus Torvalds 				break;
5821da177e4SLinus Torvalds 			}
5831da177e4SLinus Torvalds 			kfree(conf_data);
5841da177e4SLinus Torvalds 		}
5851da177e4SLinus Torvalds 	}
5861da177e4SLinus Torvalds 	return 0;
5871da177e4SLinus Torvalds }
5881da177e4SLinus Torvalds 
5893d052595SHorst Hummel /*
59040545573SHorst Hummel  * Build CP for Perform Subsystem Function - SSC.
59140545573SHorst Hummel  */
5922b67fc46SHeiko Carstens static struct dasd_ccw_req *
59340545573SHorst Hummel dasd_eckd_build_psf_ssc(struct dasd_device *device)
59440545573SHorst Hummel {
59540545573SHorst Hummel        struct dasd_ccw_req *cqr;
59640545573SHorst Hummel        struct dasd_psf_ssc_data *psf_ssc_data;
59740545573SHorst Hummel        struct ccw1 *ccw;
59840545573SHorst Hummel 
59940545573SHorst Hummel        cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ ,
60040545573SHorst Hummel 				  sizeof(struct dasd_psf_ssc_data),
60140545573SHorst Hummel 				  device);
60240545573SHorst Hummel 
60340545573SHorst Hummel        if (IS_ERR(cqr)) {
60440545573SHorst Hummel 	       DEV_MESSAGE(KERN_WARNING, device, "%s",
60540545573SHorst Hummel 			   "Could not allocate PSF-SSC request");
60640545573SHorst Hummel 	       return cqr;
60740545573SHorst Hummel        }
60840545573SHorst Hummel        psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
60940545573SHorst Hummel        psf_ssc_data->order = PSF_ORDER_SSC;
61040545573SHorst Hummel        psf_ssc_data->suborder = 0x08;
61140545573SHorst Hummel 
61240545573SHorst Hummel        ccw = cqr->cpaddr;
61340545573SHorst Hummel        ccw->cmd_code = DASD_ECKD_CCW_PSF;
61440545573SHorst Hummel        ccw->cda = (__u32)(addr_t)psf_ssc_data;
61540545573SHorst Hummel        ccw->count = 66;
61640545573SHorst Hummel 
61740545573SHorst Hummel        cqr->device = device;
61840545573SHorst Hummel        cqr->expires = 10*HZ;
61940545573SHorst Hummel        cqr->buildclk = get_clock();
62040545573SHorst Hummel        cqr->status = DASD_CQR_FILLED;
62140545573SHorst Hummel        return cqr;
62240545573SHorst Hummel }
62340545573SHorst Hummel 
62440545573SHorst Hummel /*
62540545573SHorst Hummel  * Perform Subsystem Function.
62640545573SHorst Hummel  * It is necessary to trigger CIO for channel revalidation since this
62740545573SHorst Hummel  * call might change behaviour of DASD devices.
62840545573SHorst Hummel  */
62940545573SHorst Hummel static int
63040545573SHorst Hummel dasd_eckd_psf_ssc(struct dasd_device *device)
63140545573SHorst Hummel {
63240545573SHorst Hummel        struct dasd_ccw_req *cqr;
63340545573SHorst Hummel        int rc;
63440545573SHorst Hummel 
63540545573SHorst Hummel        cqr = dasd_eckd_build_psf_ssc(device);
63640545573SHorst Hummel        if (IS_ERR(cqr))
63740545573SHorst Hummel 	       return PTR_ERR(cqr);
63840545573SHorst Hummel 
63940545573SHorst Hummel        rc = dasd_sleep_on(cqr);
64040545573SHorst Hummel        if (!rc)
64140545573SHorst Hummel 	       /* trigger CIO to reprobe devices */
64240545573SHorst Hummel 	       css_schedule_reprobe();
64340545573SHorst Hummel        dasd_sfree_request(cqr, cqr->device);
64440545573SHorst Hummel        return rc;
64540545573SHorst Hummel }
64640545573SHorst Hummel 
64740545573SHorst Hummel /*
64840545573SHorst Hummel  * Valide storage server of current device.
64940545573SHorst Hummel  */
65040545573SHorst Hummel static int
6518e79a441SHorst Hummel dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid)
65240545573SHorst Hummel {
65340545573SHorst Hummel 	int rc;
65440545573SHorst Hummel 
65540545573SHorst Hummel 	/* Currently PAV is the only reason to 'validate' server on LPAR */
65640545573SHorst Hummel 	if (dasd_nopav || MACHINE_IS_VM)
65740545573SHorst Hummel 		return 0;
65840545573SHorst Hummel 
65940545573SHorst Hummel 	rc = dasd_eckd_psf_ssc(device);
66040545573SHorst Hummel 	/* may be requested feature is not available on server,
66140545573SHorst Hummel 	 * therefore just report error and go ahead */
66240545573SHorst Hummel 	DEV_MESSAGE(KERN_INFO, device,
6638e79a441SHorst Hummel 		    "PSF-SSC on storage subsystem %s.%s.%04x returned rc=%d",
6648e79a441SHorst Hummel 		    uid->vendor, uid->serial, uid->ssid, rc);
66540545573SHorst Hummel 	/* RE-Read Configuration Data */
66640545573SHorst Hummel 	return dasd_eckd_read_conf(device);
66740545573SHorst Hummel }
66840545573SHorst Hummel 
66940545573SHorst Hummel /*
6703d052595SHorst Hummel  * Check device characteristics.
6713d052595SHorst Hummel  * If the device is accessible using ECKD discipline, the device is enabled.
6723d052595SHorst Hummel  */
6731da177e4SLinus Torvalds static int
6741da177e4SLinus Torvalds dasd_eckd_check_characteristics(struct dasd_device *device)
6751da177e4SLinus Torvalds {
6761da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
6773d052595SHorst Hummel 	struct dasd_uid uid;
6781da177e4SLinus Torvalds 	void *rdc_data;
6791da177e4SLinus Torvalds 	int rc;
6801da177e4SLinus Torvalds 
6811da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) device->private;
6821da177e4SLinus Torvalds 	if (private == NULL) {
683138c014dSHorst Hummel 		private = kzalloc(sizeof(struct dasd_eckd_private),
6841da177e4SLinus Torvalds 				  GFP_KERNEL | GFP_DMA);
6851da177e4SLinus Torvalds 		if (private == NULL) {
6861da177e4SLinus Torvalds 			DEV_MESSAGE(KERN_WARNING, device, "%s",
6871da177e4SLinus Torvalds 				    "memory allocation failed for private "
6881da177e4SLinus Torvalds 				    "data");
6891da177e4SLinus Torvalds 			return -ENOMEM;
6901da177e4SLinus Torvalds 		}
6911da177e4SLinus Torvalds 		device->private = (void *) private;
6921da177e4SLinus Torvalds 	}
6931da177e4SLinus Torvalds 	/* Invalidate status of initial analysis. */
6941da177e4SLinus Torvalds 	private->init_cqr_status = -1;
6951da177e4SLinus Torvalds 	/* Set default cache operations. */
6961da177e4SLinus Torvalds 	private->attrib.operation = DASD_NORMAL_CACHE;
6971da177e4SLinus Torvalds 	private->attrib.nr_cyl = 0;
6981da177e4SLinus Torvalds 
69940545573SHorst Hummel 	/* Read Configuration Data */
70040545573SHorst Hummel 	rc = dasd_eckd_read_conf(device);
70140545573SHorst Hummel 	if (rc)
70240545573SHorst Hummel 		return rc;
70340545573SHorst Hummel 
70440545573SHorst Hummel 	/* Generate device unique id and register in devmap */
70540545573SHorst Hummel 	rc = dasd_eckd_generate_uid(device, &uid);
70640545573SHorst Hummel 	if (rc)
70740545573SHorst Hummel 		return rc;
70840545573SHorst Hummel 	rc = dasd_set_uid(device->cdev, &uid);
70940545573SHorst Hummel 	if (rc == 1)	/* new server found */
7108e79a441SHorst Hummel 		rc = dasd_eckd_validate_server(device, &uid);
71140545573SHorst Hummel 	if (rc)
71240545573SHorst Hummel 		return rc;
71340545573SHorst Hummel 
7141da177e4SLinus Torvalds 	/* Read Device Characteristics */
7151da177e4SLinus Torvalds 	rdc_data = (void *) &(private->rdc_data);
7163d052595SHorst Hummel 	memset(rdc_data, 0, sizeof(rdc_data));
71717283b56SCornelia Huck 	rc = dasd_generic_read_dev_chars(device, "ECKD", &rdc_data, 64);
71840545573SHorst Hummel 	if (rc)
7191da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_WARNING, device,
72040545573SHorst Hummel 			    "Read device characteristics returned "
72140545573SHorst Hummel 			    "rc=%d", rc);
7221da177e4SLinus Torvalds 
7231da177e4SLinus Torvalds 	DEV_MESSAGE(KERN_INFO, device,
7241da177e4SLinus Torvalds 		    "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d",
7251da177e4SLinus Torvalds 		    private->rdc_data.dev_type,
7261da177e4SLinus Torvalds 		    private->rdc_data.dev_model,
7271da177e4SLinus Torvalds 		    private->rdc_data.cu_type,
7281da177e4SLinus Torvalds 		    private->rdc_data.cu_model.model,
7291da177e4SLinus Torvalds 		    private->rdc_data.no_cyl,
7301da177e4SLinus Torvalds 		    private->rdc_data.trk_per_cyl,
7311da177e4SLinus Torvalds 		    private->rdc_data.sec_per_trk);
7323d052595SHorst Hummel 	return rc;
7331da177e4SLinus Torvalds }
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds static struct dasd_ccw_req *
7361da177e4SLinus Torvalds dasd_eckd_analysis_ccw(struct dasd_device *device)
7371da177e4SLinus Torvalds {
7381da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
7391da177e4SLinus Torvalds 	struct eckd_count *count_data;
7401da177e4SLinus Torvalds 	struct LO_eckd_data *LO_data;
7411da177e4SLinus Torvalds 	struct dasd_ccw_req *cqr;
7421da177e4SLinus Torvalds 	struct ccw1 *ccw;
7431da177e4SLinus Torvalds 	int cplength, datasize;
7441da177e4SLinus Torvalds 	int i;
7451da177e4SLinus Torvalds 
7461da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) device->private;
7471da177e4SLinus Torvalds 
7481da177e4SLinus Torvalds 	cplength = 8;
7491da177e4SLinus Torvalds 	datasize = sizeof(struct DE_eckd_data) + 2*sizeof(struct LO_eckd_data);
7501da177e4SLinus Torvalds 	cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
7511da177e4SLinus Torvalds 				   cplength, datasize, device);
7521da177e4SLinus Torvalds 	if (IS_ERR(cqr))
7531da177e4SLinus Torvalds 		return cqr;
7541da177e4SLinus Torvalds 	ccw = cqr->cpaddr;
7551da177e4SLinus Torvalds 	/* Define extent for the first 3 tracks. */
7561da177e4SLinus Torvalds 	define_extent(ccw++, cqr->data, 0, 2,
7571da177e4SLinus Torvalds 		      DASD_ECKD_CCW_READ_COUNT, device);
7581da177e4SLinus Torvalds 	LO_data = cqr->data + sizeof (struct DE_eckd_data);
7591da177e4SLinus Torvalds 	/* Locate record for the first 4 records on track 0. */
7601da177e4SLinus Torvalds 	ccw[-1].flags |= CCW_FLAG_CC;
7611da177e4SLinus Torvalds 	locate_record(ccw++, LO_data++, 0, 0, 4,
7621da177e4SLinus Torvalds 		      DASD_ECKD_CCW_READ_COUNT, device, 0);
7631da177e4SLinus Torvalds 
7641da177e4SLinus Torvalds 	count_data = private->count_area;
7651da177e4SLinus Torvalds 	for (i = 0; i < 4; i++) {
7661da177e4SLinus Torvalds 		ccw[-1].flags |= CCW_FLAG_CC;
7671da177e4SLinus Torvalds 		ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;
7681da177e4SLinus Torvalds 		ccw->flags = 0;
7691da177e4SLinus Torvalds 		ccw->count = 8;
7701da177e4SLinus Torvalds 		ccw->cda = (__u32)(addr_t) count_data;
7711da177e4SLinus Torvalds 		ccw++;
7721da177e4SLinus Torvalds 		count_data++;
7731da177e4SLinus Torvalds 	}
7741da177e4SLinus Torvalds 
7751da177e4SLinus Torvalds 	/* Locate record for the first record on track 2. */
7761da177e4SLinus Torvalds 	ccw[-1].flags |= CCW_FLAG_CC;
7771da177e4SLinus Torvalds 	locate_record(ccw++, LO_data++, 2, 0, 1,
7781da177e4SLinus Torvalds 		      DASD_ECKD_CCW_READ_COUNT, device, 0);
7791da177e4SLinus Torvalds 	/* Read count ccw. */
7801da177e4SLinus Torvalds 	ccw[-1].flags |= CCW_FLAG_CC;
7811da177e4SLinus Torvalds 	ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;
7821da177e4SLinus Torvalds 	ccw->flags = 0;
7831da177e4SLinus Torvalds 	ccw->count = 8;
7841da177e4SLinus Torvalds 	ccw->cda = (__u32)(addr_t) count_data;
7851da177e4SLinus Torvalds 
7861da177e4SLinus Torvalds 	cqr->device = device;
7871da177e4SLinus Torvalds 	cqr->retries = 0;
7881da177e4SLinus Torvalds 	cqr->buildclk = get_clock();
7891da177e4SLinus Torvalds 	cqr->status = DASD_CQR_FILLED;
7901da177e4SLinus Torvalds 	return cqr;
7911da177e4SLinus Torvalds }
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds /*
7941da177e4SLinus Torvalds  * This is the callback function for the init_analysis cqr. It saves
7951da177e4SLinus Torvalds  * the status of the initial analysis ccw before it frees it and kicks
7961da177e4SLinus Torvalds  * the device to continue the startup sequence. This will call
7971da177e4SLinus Torvalds  * dasd_eckd_do_analysis again (if the devices has not been marked
7981da177e4SLinus Torvalds  * for deletion in the meantime).
7991da177e4SLinus Torvalds  */
8001da177e4SLinus Torvalds static void
8011da177e4SLinus Torvalds dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data)
8021da177e4SLinus Torvalds {
8031da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
8041da177e4SLinus Torvalds 	struct dasd_device *device;
8051da177e4SLinus Torvalds 
8061da177e4SLinus Torvalds 	device = init_cqr->device;
8071da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) device->private;
8081da177e4SLinus Torvalds 	private->init_cqr_status = init_cqr->status;
8091da177e4SLinus Torvalds 	dasd_sfree_request(init_cqr, device);
8101da177e4SLinus Torvalds 	dasd_kick_device(device);
8111da177e4SLinus Torvalds }
8121da177e4SLinus Torvalds 
8131da177e4SLinus Torvalds static int
8141da177e4SLinus Torvalds dasd_eckd_start_analysis(struct dasd_device *device)
8151da177e4SLinus Torvalds {
8161da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
8171da177e4SLinus Torvalds 	struct dasd_ccw_req *init_cqr;
8181da177e4SLinus Torvalds 
8191da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) device->private;
8201da177e4SLinus Torvalds 	init_cqr = dasd_eckd_analysis_ccw(device);
8211da177e4SLinus Torvalds 	if (IS_ERR(init_cqr))
8221da177e4SLinus Torvalds 		return PTR_ERR(init_cqr);
8231da177e4SLinus Torvalds 	init_cqr->callback = dasd_eckd_analysis_callback;
8241da177e4SLinus Torvalds 	init_cqr->callback_data = NULL;
8251da177e4SLinus Torvalds 	init_cqr->expires = 5*HZ;
8261da177e4SLinus Torvalds 	dasd_add_request_head(init_cqr);
8271da177e4SLinus Torvalds 	return -EAGAIN;
8281da177e4SLinus Torvalds }
8291da177e4SLinus Torvalds 
8301da177e4SLinus Torvalds static int
8311da177e4SLinus Torvalds dasd_eckd_end_analysis(struct dasd_device *device)
8321da177e4SLinus Torvalds {
8331da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
8341da177e4SLinus Torvalds 	struct eckd_count *count_area;
8351da177e4SLinus Torvalds 	unsigned int sb, blk_per_trk;
8361da177e4SLinus Torvalds 	int status, i;
8371da177e4SLinus Torvalds 
8381da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) device->private;
8391da177e4SLinus Torvalds 	status = private->init_cqr_status;
8401da177e4SLinus Torvalds 	private->init_cqr_status = -1;
8411da177e4SLinus Torvalds 	if (status != DASD_CQR_DONE) {
8421da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_WARNING, device, "%s",
8431da177e4SLinus Torvalds 			    "volume analysis returned unformatted disk");
8441da177e4SLinus Torvalds 		return -EMEDIUMTYPE;
8451da177e4SLinus Torvalds 	}
8461da177e4SLinus Torvalds 
8471da177e4SLinus Torvalds 	private->uses_cdl = 1;
8481da177e4SLinus Torvalds 	/* Calculate number of blocks/records per track. */
8491da177e4SLinus Torvalds 	blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block);
8501da177e4SLinus Torvalds 	/* Check Track 0 for Compatible Disk Layout */
8511da177e4SLinus Torvalds 	count_area = NULL;
8521da177e4SLinus Torvalds 	for (i = 0; i < 3; i++) {
8531da177e4SLinus Torvalds 		if (private->count_area[i].kl != 4 ||
8541da177e4SLinus Torvalds 		    private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4) {
8551da177e4SLinus Torvalds 			private->uses_cdl = 0;
8561da177e4SLinus Torvalds 			break;
8571da177e4SLinus Torvalds 		}
8581da177e4SLinus Torvalds 	}
8591da177e4SLinus Torvalds 	if (i == 3)
8601da177e4SLinus Torvalds 		count_area = &private->count_area[4];
8611da177e4SLinus Torvalds 
8621da177e4SLinus Torvalds 	if (private->uses_cdl == 0) {
8631da177e4SLinus Torvalds 		for (i = 0; i < 5; i++) {
8641da177e4SLinus Torvalds 			if ((private->count_area[i].kl != 0) ||
8651da177e4SLinus Torvalds 			    (private->count_area[i].dl !=
8661da177e4SLinus Torvalds 			     private->count_area[0].dl))
8671da177e4SLinus Torvalds 				break;
8681da177e4SLinus Torvalds 		}
8691da177e4SLinus Torvalds 		if (i == 5)
8701da177e4SLinus Torvalds 			count_area = &private->count_area[0];
8711da177e4SLinus Torvalds 	} else {
8721da177e4SLinus Torvalds 		if (private->count_area[3].record == 1)
8731da177e4SLinus Torvalds 			DEV_MESSAGE(KERN_WARNING, device, "%s",
8741da177e4SLinus Torvalds 				    "Trk 0: no records after VTOC!");
8751da177e4SLinus Torvalds 	}
8761da177e4SLinus Torvalds 	if (count_area != NULL && count_area->kl == 0) {
8771da177e4SLinus Torvalds 		/* we found notthing violating our disk layout */
8781da177e4SLinus Torvalds 		if (dasd_check_blocksize(count_area->dl) == 0)
8791da177e4SLinus Torvalds 			device->bp_block = count_area->dl;
8801da177e4SLinus Torvalds 	}
8811da177e4SLinus Torvalds 	if (device->bp_block == 0) {
8821da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_WARNING, device, "%s",
8831da177e4SLinus Torvalds 			    "Volume has incompatible disk layout");
8841da177e4SLinus Torvalds 		return -EMEDIUMTYPE;
8851da177e4SLinus Torvalds 	}
8861da177e4SLinus Torvalds 	device->s2b_shift = 0;	/* bits to shift 512 to get a block */
8871da177e4SLinus Torvalds 	for (sb = 512; sb < device->bp_block; sb = sb << 1)
8881da177e4SLinus Torvalds 		device->s2b_shift++;
8891da177e4SLinus Torvalds 
8901da177e4SLinus Torvalds 	blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block);
8911da177e4SLinus Torvalds 	device->blocks = (private->rdc_data.no_cyl *
8921da177e4SLinus Torvalds 			  private->rdc_data.trk_per_cyl *
8931da177e4SLinus Torvalds 			  blk_per_trk);
8941da177e4SLinus Torvalds 
8951da177e4SLinus Torvalds 	DEV_MESSAGE(KERN_INFO, device,
8961da177e4SLinus Torvalds 		    "(%dkB blks): %dkB at %dkB/trk %s",
8971da177e4SLinus Torvalds 		    (device->bp_block >> 10),
8981da177e4SLinus Torvalds 		    ((private->rdc_data.no_cyl *
8991da177e4SLinus Torvalds 		      private->rdc_data.trk_per_cyl *
9001da177e4SLinus Torvalds 		      blk_per_trk * (device->bp_block >> 9)) >> 1),
9011da177e4SLinus Torvalds 		    ((blk_per_trk * device->bp_block) >> 10),
9021da177e4SLinus Torvalds 		    private->uses_cdl ?
9031da177e4SLinus Torvalds 		    "compatible disk layout" : "linux disk layout");
9041da177e4SLinus Torvalds 
9051da177e4SLinus Torvalds 	return 0;
9061da177e4SLinus Torvalds }
9071da177e4SLinus Torvalds 
9081da177e4SLinus Torvalds static int
9091da177e4SLinus Torvalds dasd_eckd_do_analysis(struct dasd_device *device)
9101da177e4SLinus Torvalds {
9111da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
9121da177e4SLinus Torvalds 
9131da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) device->private;
9141da177e4SLinus Torvalds 	if (private->init_cqr_status < 0)
9151da177e4SLinus Torvalds 		return dasd_eckd_start_analysis(device);
9161da177e4SLinus Torvalds 	else
9171da177e4SLinus Torvalds 		return dasd_eckd_end_analysis(device);
9181da177e4SLinus Torvalds }
9191da177e4SLinus Torvalds 
9201da177e4SLinus Torvalds static int
9211da177e4SLinus Torvalds dasd_eckd_fill_geometry(struct dasd_device *device, struct hd_geometry *geo)
9221da177e4SLinus Torvalds {
9231da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
9241da177e4SLinus Torvalds 
9251da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) device->private;
9261da177e4SLinus Torvalds 	if (dasd_check_blocksize(device->bp_block) == 0) {
9271da177e4SLinus Torvalds 		geo->sectors = recs_per_track(&private->rdc_data,
9281da177e4SLinus Torvalds 					      0, device->bp_block);
9291da177e4SLinus Torvalds 	}
9301da177e4SLinus Torvalds 	geo->cylinders = private->rdc_data.no_cyl;
9311da177e4SLinus Torvalds 	geo->heads = private->rdc_data.trk_per_cyl;
9321da177e4SLinus Torvalds 	return 0;
9331da177e4SLinus Torvalds }
9341da177e4SLinus Torvalds 
9351da177e4SLinus Torvalds static struct dasd_ccw_req *
9361da177e4SLinus Torvalds dasd_eckd_format_device(struct dasd_device * device,
9371da177e4SLinus Torvalds 			struct format_data_t * fdata)
9381da177e4SLinus Torvalds {
9391da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
9401da177e4SLinus Torvalds 	struct dasd_ccw_req *fcp;
9411da177e4SLinus Torvalds 	struct eckd_count *ect;
9421da177e4SLinus Torvalds 	struct ccw1 *ccw;
9431da177e4SLinus Torvalds 	void *data;
9441da177e4SLinus Torvalds 	int rpt, cyl, head;
9451da177e4SLinus Torvalds 	int cplength, datasize;
9461da177e4SLinus Torvalds 	int i;
9471da177e4SLinus Torvalds 
9481da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) device->private;
9491da177e4SLinus Torvalds 	rpt = recs_per_track(&private->rdc_data, 0, fdata->blksize);
9501da177e4SLinus Torvalds 	cyl = fdata->start_unit / private->rdc_data.trk_per_cyl;
9511da177e4SLinus Torvalds 	head = fdata->start_unit % private->rdc_data.trk_per_cyl;
9521da177e4SLinus Torvalds 
9531da177e4SLinus Torvalds 	/* Sanity checks. */
9541da177e4SLinus Torvalds 	if (fdata->start_unit >=
9551da177e4SLinus Torvalds 	    (private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl)) {
9561da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_INFO, device, "Track no %d too big!",
9571da177e4SLinus Torvalds 			    fdata->start_unit);
9581da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
9591da177e4SLinus Torvalds 	}
9601da177e4SLinus Torvalds 	if (fdata->start_unit > fdata->stop_unit) {
9611da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_INFO, device, "Track %d reached! ending.",
9621da177e4SLinus Torvalds 			    fdata->start_unit);
9631da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
9641da177e4SLinus Torvalds 	}
9651da177e4SLinus Torvalds 	if (dasd_check_blocksize(fdata->blksize) != 0) {
9661da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_WARNING, device,
9671da177e4SLinus Torvalds 			    "Invalid blocksize %d...terminating!",
9681da177e4SLinus Torvalds 			    fdata->blksize);
9691da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
9701da177e4SLinus Torvalds 	}
9711da177e4SLinus Torvalds 
9721da177e4SLinus Torvalds 	/*
9731da177e4SLinus Torvalds 	 * fdata->intensity is a bit string that tells us what to do:
9741da177e4SLinus Torvalds 	 *   Bit 0: write record zero
9751da177e4SLinus Torvalds 	 *   Bit 1: write home address, currently not supported
9761da177e4SLinus Torvalds 	 *   Bit 2: invalidate tracks
9771da177e4SLinus Torvalds 	 *   Bit 3: use OS/390 compatible disk layout (cdl)
9781da177e4SLinus Torvalds 	 * Only some bit combinations do make sense.
9791da177e4SLinus Torvalds 	 */
9801da177e4SLinus Torvalds 	switch (fdata->intensity) {
9811da177e4SLinus Torvalds 	case 0x00:	/* Normal format */
9821da177e4SLinus Torvalds 	case 0x08:	/* Normal format, use cdl. */
9831da177e4SLinus Torvalds 		cplength = 2 + rpt;
9841da177e4SLinus Torvalds 		datasize = sizeof(struct DE_eckd_data) +
9851da177e4SLinus Torvalds 			sizeof(struct LO_eckd_data) +
9861da177e4SLinus Torvalds 			rpt * sizeof(struct eckd_count);
9871da177e4SLinus Torvalds 		break;
9881da177e4SLinus Torvalds 	case 0x01:	/* Write record zero and format track. */
9891da177e4SLinus Torvalds 	case 0x09:	/* Write record zero and format track, use cdl. */
9901da177e4SLinus Torvalds 		cplength = 3 + rpt;
9911da177e4SLinus Torvalds 		datasize = sizeof(struct DE_eckd_data) +
9921da177e4SLinus Torvalds 			sizeof(struct LO_eckd_data) +
9931da177e4SLinus Torvalds 			sizeof(struct eckd_count) +
9941da177e4SLinus Torvalds 			rpt * sizeof(struct eckd_count);
9951da177e4SLinus Torvalds 		break;
9961da177e4SLinus Torvalds 	case 0x04:	/* Invalidate track. */
9971da177e4SLinus Torvalds 	case 0x0c:	/* Invalidate track, use cdl. */
9981da177e4SLinus Torvalds 		cplength = 3;
9991da177e4SLinus Torvalds 		datasize = sizeof(struct DE_eckd_data) +
10001da177e4SLinus Torvalds 			sizeof(struct LO_eckd_data) +
10011da177e4SLinus Torvalds 			sizeof(struct eckd_count);
10021da177e4SLinus Torvalds 		break;
10031da177e4SLinus Torvalds 	default:
10041da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_WARNING, device, "Invalid flags 0x%x.",
10051da177e4SLinus Torvalds 			    fdata->intensity);
10061da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
10071da177e4SLinus Torvalds 	}
10081da177e4SLinus Torvalds 	/* Allocate the format ccw request. */
10091da177e4SLinus Torvalds 	fcp = dasd_smalloc_request(dasd_eckd_discipline.name,
10101da177e4SLinus Torvalds 				   cplength, datasize, device);
10111da177e4SLinus Torvalds 	if (IS_ERR(fcp))
10121da177e4SLinus Torvalds 		return fcp;
10131da177e4SLinus Torvalds 
10141da177e4SLinus Torvalds 	data = fcp->data;
10151da177e4SLinus Torvalds 	ccw = fcp->cpaddr;
10161da177e4SLinus Torvalds 
10171da177e4SLinus Torvalds 	switch (fdata->intensity & ~0x08) {
10181da177e4SLinus Torvalds 	case 0x00: /* Normal format. */
10191da177e4SLinus Torvalds 		define_extent(ccw++, (struct DE_eckd_data *) data,
10201da177e4SLinus Torvalds 			      fdata->start_unit, fdata->start_unit,
10211da177e4SLinus Torvalds 			      DASD_ECKD_CCW_WRITE_CKD, device);
10221da177e4SLinus Torvalds 		data += sizeof(struct DE_eckd_data);
10231da177e4SLinus Torvalds 		ccw[-1].flags |= CCW_FLAG_CC;
10241da177e4SLinus Torvalds 		locate_record(ccw++, (struct LO_eckd_data *) data,
10251da177e4SLinus Torvalds 			      fdata->start_unit, 0, rpt,
10261da177e4SLinus Torvalds 			      DASD_ECKD_CCW_WRITE_CKD, device,
10271da177e4SLinus Torvalds 			      fdata->blksize);
10281da177e4SLinus Torvalds 		data += sizeof(struct LO_eckd_data);
10291da177e4SLinus Torvalds 		break;
10301da177e4SLinus Torvalds 	case 0x01: /* Write record zero + format track. */
10311da177e4SLinus Torvalds 		define_extent(ccw++, (struct DE_eckd_data *) data,
10321da177e4SLinus Torvalds 			      fdata->start_unit, fdata->start_unit,
10331da177e4SLinus Torvalds 			      DASD_ECKD_CCW_WRITE_RECORD_ZERO,
10341da177e4SLinus Torvalds 			      device);
10351da177e4SLinus Torvalds 		data += sizeof(struct DE_eckd_data);
10361da177e4SLinus Torvalds 		ccw[-1].flags |= CCW_FLAG_CC;
10371da177e4SLinus Torvalds 		locate_record(ccw++, (struct LO_eckd_data *) data,
10381da177e4SLinus Torvalds 			      fdata->start_unit, 0, rpt + 1,
10391da177e4SLinus Torvalds 			      DASD_ECKD_CCW_WRITE_RECORD_ZERO, device,
10401da177e4SLinus Torvalds 			      device->bp_block);
10411da177e4SLinus Torvalds 		data += sizeof(struct LO_eckd_data);
10421da177e4SLinus Torvalds 		break;
10431da177e4SLinus Torvalds 	case 0x04: /* Invalidate track. */
10441da177e4SLinus Torvalds 		define_extent(ccw++, (struct DE_eckd_data *) data,
10451da177e4SLinus Torvalds 			      fdata->start_unit, fdata->start_unit,
10461da177e4SLinus Torvalds 			      DASD_ECKD_CCW_WRITE_CKD, device);
10471da177e4SLinus Torvalds 		data += sizeof(struct DE_eckd_data);
10481da177e4SLinus Torvalds 		ccw[-1].flags |= CCW_FLAG_CC;
10491da177e4SLinus Torvalds 		locate_record(ccw++, (struct LO_eckd_data *) data,
10501da177e4SLinus Torvalds 			      fdata->start_unit, 0, 1,
10511da177e4SLinus Torvalds 			      DASD_ECKD_CCW_WRITE_CKD, device, 8);
10521da177e4SLinus Torvalds 		data += sizeof(struct LO_eckd_data);
10531da177e4SLinus Torvalds 		break;
10541da177e4SLinus Torvalds 	}
10551da177e4SLinus Torvalds 	if (fdata->intensity & 0x01) {	/* write record zero */
10561da177e4SLinus Torvalds 		ect = (struct eckd_count *) data;
10571da177e4SLinus Torvalds 		data += sizeof(struct eckd_count);
10581da177e4SLinus Torvalds 		ect->cyl = cyl;
10591da177e4SLinus Torvalds 		ect->head = head;
10601da177e4SLinus Torvalds 		ect->record = 0;
10611da177e4SLinus Torvalds 		ect->kl = 0;
10621da177e4SLinus Torvalds 		ect->dl = 8;
10631da177e4SLinus Torvalds 		ccw[-1].flags |= CCW_FLAG_CC;
10641da177e4SLinus Torvalds 		ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO;
10651da177e4SLinus Torvalds 		ccw->flags = CCW_FLAG_SLI;
10661da177e4SLinus Torvalds 		ccw->count = 8;
10671da177e4SLinus Torvalds 		ccw->cda = (__u32)(addr_t) ect;
10681da177e4SLinus Torvalds 		ccw++;
10691da177e4SLinus Torvalds 	}
10701da177e4SLinus Torvalds 	if ((fdata->intensity & ~0x08) & 0x04) {	/* erase track */
10711da177e4SLinus Torvalds 		ect = (struct eckd_count *) data;
10721da177e4SLinus Torvalds 		data += sizeof(struct eckd_count);
10731da177e4SLinus Torvalds 		ect->cyl = cyl;
10741da177e4SLinus Torvalds 		ect->head = head;
10751da177e4SLinus Torvalds 		ect->record = 1;
10761da177e4SLinus Torvalds 		ect->kl = 0;
10771da177e4SLinus Torvalds 		ect->dl = 0;
10781da177e4SLinus Torvalds 		ccw[-1].flags |= CCW_FLAG_CC;
10791da177e4SLinus Torvalds 		ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD;
10801da177e4SLinus Torvalds 		ccw->flags = CCW_FLAG_SLI;
10811da177e4SLinus Torvalds 		ccw->count = 8;
10821da177e4SLinus Torvalds 		ccw->cda = (__u32)(addr_t) ect;
10831da177e4SLinus Torvalds 	} else {		/* write remaining records */
10841da177e4SLinus Torvalds 		for (i = 0; i < rpt; i++) {
10851da177e4SLinus Torvalds 			ect = (struct eckd_count *) data;
10861da177e4SLinus Torvalds 			data += sizeof(struct eckd_count);
10871da177e4SLinus Torvalds 			ect->cyl = cyl;
10881da177e4SLinus Torvalds 			ect->head = head;
10891da177e4SLinus Torvalds 			ect->record = i + 1;
10901da177e4SLinus Torvalds 			ect->kl = 0;
10911da177e4SLinus Torvalds 			ect->dl = fdata->blksize;
10921da177e4SLinus Torvalds 			/* Check for special tracks 0-1 when formatting CDL */
10931da177e4SLinus Torvalds 			if ((fdata->intensity & 0x08) &&
10941da177e4SLinus Torvalds 			    fdata->start_unit == 0) {
10951da177e4SLinus Torvalds 				if (i < 3) {
10961da177e4SLinus Torvalds 					ect->kl = 4;
10971da177e4SLinus Torvalds 					ect->dl = sizes_trk0[i] - 4;
10981da177e4SLinus Torvalds 				}
10991da177e4SLinus Torvalds 			}
11001da177e4SLinus Torvalds 			if ((fdata->intensity & 0x08) &&
11011da177e4SLinus Torvalds 			    fdata->start_unit == 1) {
11021da177e4SLinus Torvalds 				ect->kl = 44;
11031da177e4SLinus Torvalds 				ect->dl = LABEL_SIZE - 44;
11041da177e4SLinus Torvalds 			}
11051da177e4SLinus Torvalds 			ccw[-1].flags |= CCW_FLAG_CC;
11061da177e4SLinus Torvalds 			ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD;
11071da177e4SLinus Torvalds 			ccw->flags = CCW_FLAG_SLI;
11081da177e4SLinus Torvalds 			ccw->count = 8;
11091da177e4SLinus Torvalds 			ccw->cda = (__u32)(addr_t) ect;
11101da177e4SLinus Torvalds 			ccw++;
11111da177e4SLinus Torvalds 		}
11121da177e4SLinus Torvalds 	}
11131da177e4SLinus Torvalds 	fcp->device = device;
11141da177e4SLinus Torvalds 	fcp->retries = 2;	/* set retry counter to enable ERP */
11151da177e4SLinus Torvalds 	fcp->buildclk = get_clock();
11161da177e4SLinus Torvalds 	fcp->status = DASD_CQR_FILLED;
11171da177e4SLinus Torvalds 	return fcp;
11181da177e4SLinus Torvalds }
11191da177e4SLinus Torvalds 
11201da177e4SLinus Torvalds static dasd_era_t
11211da177e4SLinus Torvalds dasd_eckd_examine_error(struct dasd_ccw_req * cqr, struct irb * irb)
11221da177e4SLinus Torvalds {
11231da177e4SLinus Torvalds 	struct dasd_device *device = (struct dasd_device *) cqr->device;
11241da177e4SLinus Torvalds 	struct ccw_device *cdev = device->cdev;
11251da177e4SLinus Torvalds 
11261da177e4SLinus Torvalds 	if (irb->scsw.cstat == 0x00 &&
11271da177e4SLinus Torvalds 	    irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
11281da177e4SLinus Torvalds 		return dasd_era_none;
11291da177e4SLinus Torvalds 
11301da177e4SLinus Torvalds 	switch (cdev->id.cu_type) {
11311da177e4SLinus Torvalds 	case 0x3990:
11321da177e4SLinus Torvalds 	case 0x2105:
11331da177e4SLinus Torvalds 	case 0x2107:
11341da177e4SLinus Torvalds 	case 0x1750:
11351da177e4SLinus Torvalds 		return dasd_3990_erp_examine(cqr, irb);
11361da177e4SLinus Torvalds 	case 0x9343:
11371da177e4SLinus Torvalds 		return dasd_9343_erp_examine(cqr, irb);
11381da177e4SLinus Torvalds 	case 0x3880:
11391da177e4SLinus Torvalds 	default:
11401da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_WARNING, device, "%s",
11411da177e4SLinus Torvalds 			    "default (unknown CU type) - RECOVERABLE return");
11421da177e4SLinus Torvalds 		return dasd_era_recover;
11431da177e4SLinus Torvalds 	}
11441da177e4SLinus Torvalds }
11451da177e4SLinus Torvalds 
11461da177e4SLinus Torvalds static dasd_erp_fn_t
11471da177e4SLinus Torvalds dasd_eckd_erp_action(struct dasd_ccw_req * cqr)
11481da177e4SLinus Torvalds {
11491da177e4SLinus Torvalds 	struct dasd_device *device = (struct dasd_device *) cqr->device;
11501da177e4SLinus Torvalds 	struct ccw_device *cdev = device->cdev;
11511da177e4SLinus Torvalds 
11521da177e4SLinus Torvalds 	switch (cdev->id.cu_type) {
11531da177e4SLinus Torvalds 	case 0x3990:
11541da177e4SLinus Torvalds 	case 0x2105:
11551da177e4SLinus Torvalds 	case 0x2107:
11561da177e4SLinus Torvalds 	case 0x1750:
11571da177e4SLinus Torvalds 		return dasd_3990_erp_action;
11581da177e4SLinus Torvalds 	case 0x9343:
11591da177e4SLinus Torvalds 	case 0x3880:
11601da177e4SLinus Torvalds 	default:
11611da177e4SLinus Torvalds 		return dasd_default_erp_action;
11621da177e4SLinus Torvalds 	}
11631da177e4SLinus Torvalds }
11641da177e4SLinus Torvalds 
11651da177e4SLinus Torvalds static dasd_erp_fn_t
11661da177e4SLinus Torvalds dasd_eckd_erp_postaction(struct dasd_ccw_req * cqr)
11671da177e4SLinus Torvalds {
11681da177e4SLinus Torvalds 	return dasd_default_erp_postaction;
11691da177e4SLinus Torvalds }
11701da177e4SLinus Torvalds 
11711da177e4SLinus Torvalds static struct dasd_ccw_req *
11721da177e4SLinus Torvalds dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
11731da177e4SLinus Torvalds {
11741da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
11751da177e4SLinus Torvalds 	unsigned long *idaws;
11761da177e4SLinus Torvalds 	struct LO_eckd_data *LO_data;
11771da177e4SLinus Torvalds 	struct dasd_ccw_req *cqr;
11781da177e4SLinus Torvalds 	struct ccw1 *ccw;
11791da177e4SLinus Torvalds 	struct bio *bio;
11801da177e4SLinus Torvalds 	struct bio_vec *bv;
11811da177e4SLinus Torvalds 	char *dst;
11821da177e4SLinus Torvalds 	unsigned int blksize, blk_per_trk, off;
11831da177e4SLinus Torvalds 	int count, cidaw, cplength, datasize;
11841da177e4SLinus Torvalds 	sector_t recid, first_rec, last_rec;
11851da177e4SLinus Torvalds 	sector_t first_trk, last_trk;
11861da177e4SLinus Torvalds 	unsigned int first_offs, last_offs;
11871da177e4SLinus Torvalds 	unsigned char cmd, rcmd;
11881da177e4SLinus Torvalds 	int i;
11891da177e4SLinus Torvalds 
11901da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) device->private;
11911da177e4SLinus Torvalds 	if (rq_data_dir(req) == READ)
11921da177e4SLinus Torvalds 		cmd = DASD_ECKD_CCW_READ_MT;
11931da177e4SLinus Torvalds 	else if (rq_data_dir(req) == WRITE)
11941da177e4SLinus Torvalds 		cmd = DASD_ECKD_CCW_WRITE_MT;
11951da177e4SLinus Torvalds 	else
11961da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
11971da177e4SLinus Torvalds 	/* Calculate number of blocks/records per track. */
11981da177e4SLinus Torvalds 	blksize = device->bp_block;
11991da177e4SLinus Torvalds 	blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
12001da177e4SLinus Torvalds 	/* Calculate record id of first and last block. */
12011da177e4SLinus Torvalds 	first_rec = first_trk = req->sector >> device->s2b_shift;
12021da177e4SLinus Torvalds 	first_offs = sector_div(first_trk, blk_per_trk);
12031da177e4SLinus Torvalds 	last_rec = last_trk =
12041da177e4SLinus Torvalds 		(req->sector + req->nr_sectors - 1) >> device->s2b_shift;
12051da177e4SLinus Torvalds 	last_offs = sector_div(last_trk, blk_per_trk);
12061da177e4SLinus Torvalds 	/* Check struct bio and count the number of blocks for the request. */
12071da177e4SLinus Torvalds 	count = 0;
12081da177e4SLinus Torvalds 	cidaw = 0;
12091da177e4SLinus Torvalds 	rq_for_each_bio(bio, req) {
12101da177e4SLinus Torvalds 		bio_for_each_segment(bv, bio, i) {
12111da177e4SLinus Torvalds 			if (bv->bv_len & (blksize - 1))
12121da177e4SLinus Torvalds 				/* Eckd can only do full blocks. */
12131da177e4SLinus Torvalds 				return ERR_PTR(-EINVAL);
12141da177e4SLinus Torvalds 			count += bv->bv_len >> (device->s2b_shift + 9);
1215347a8dc3SMartin Schwidefsky #if defined(CONFIG_64BIT)
12161da177e4SLinus Torvalds 			if (idal_is_needed (page_address(bv->bv_page),
12171da177e4SLinus Torvalds 					    bv->bv_len))
12181da177e4SLinus Torvalds 				cidaw += bv->bv_len >> (device->s2b_shift + 9);
12191da177e4SLinus Torvalds #endif
12201da177e4SLinus Torvalds 		}
12211da177e4SLinus Torvalds 	}
12221da177e4SLinus Torvalds 	/* Paranoia. */
12231da177e4SLinus Torvalds 	if (count != last_rec - first_rec + 1)
12241da177e4SLinus Torvalds 		return ERR_PTR(-EINVAL);
12251da177e4SLinus Torvalds 	/* 1x define extent + 1x locate record + number of blocks */
12261da177e4SLinus Torvalds 	cplength = 2 + count;
12271da177e4SLinus Torvalds 	/* 1x define extent + 1x locate record + cidaws*sizeof(long) */
12281da177e4SLinus Torvalds 	datasize = sizeof(struct DE_eckd_data) + sizeof(struct LO_eckd_data) +
12291da177e4SLinus Torvalds 		cidaw * sizeof(unsigned long);
12301da177e4SLinus Torvalds 	/* Find out the number of additional locate record ccws for cdl. */
12311da177e4SLinus Torvalds 	if (private->uses_cdl && first_rec < 2*blk_per_trk) {
12321da177e4SLinus Torvalds 		if (last_rec >= 2*blk_per_trk)
12331da177e4SLinus Torvalds 			count = 2*blk_per_trk - first_rec;
12341da177e4SLinus Torvalds 		cplength += count;
12351da177e4SLinus Torvalds 		datasize += count*sizeof(struct LO_eckd_data);
12361da177e4SLinus Torvalds 	}
12371da177e4SLinus Torvalds 	/* Allocate the ccw request. */
12381da177e4SLinus Torvalds 	cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
12391da177e4SLinus Torvalds 				   cplength, datasize, device);
12401da177e4SLinus Torvalds 	if (IS_ERR(cqr))
12411da177e4SLinus Torvalds 		return cqr;
12421da177e4SLinus Torvalds 	ccw = cqr->cpaddr;
12431da177e4SLinus Torvalds 	/* First ccw is define extent. */
1244d54853efSMartin Schwidefsky 	if (define_extent(ccw++, cqr->data, first_trk,
1245d54853efSMartin Schwidefsky 			  last_trk, cmd, device) == -EAGAIN) {
1246d54853efSMartin Schwidefsky 		/* Clock not in sync and XRC is enabled. Try again later. */
1247d54853efSMartin Schwidefsky 		dasd_sfree_request(cqr, device);
1248d54853efSMartin Schwidefsky 		return ERR_PTR(-EAGAIN);
1249d54853efSMartin Schwidefsky 	}
12501da177e4SLinus Torvalds 	/* Build locate_record+read/write/ccws. */
12511da177e4SLinus Torvalds 	idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data));
12521da177e4SLinus Torvalds 	LO_data = (struct LO_eckd_data *) (idaws + cidaw);
12531da177e4SLinus Torvalds 	recid = first_rec;
12541da177e4SLinus Torvalds 	if (private->uses_cdl == 0 || recid > 2*blk_per_trk) {
12551da177e4SLinus Torvalds 		/* Only standard blocks so there is just one locate record. */
12561da177e4SLinus Torvalds 		ccw[-1].flags |= CCW_FLAG_CC;
12571da177e4SLinus Torvalds 		locate_record(ccw++, LO_data++, first_trk, first_offs + 1,
12581da177e4SLinus Torvalds 			      last_rec - recid + 1, cmd, device, blksize);
12591da177e4SLinus Torvalds 	}
12601da177e4SLinus Torvalds 	rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) {
12611da177e4SLinus Torvalds 		dst = page_address(bv->bv_page) + bv->bv_offset;
12621da177e4SLinus Torvalds 		if (dasd_page_cache) {
12631da177e4SLinus Torvalds 			char *copy = kmem_cache_alloc(dasd_page_cache,
1264441e143eSChristoph Lameter 						      GFP_DMA | __GFP_NOWARN);
12651da177e4SLinus Torvalds 			if (copy && rq_data_dir(req) == WRITE)
12661da177e4SLinus Torvalds 				memcpy(copy + bv->bv_offset, dst, bv->bv_len);
12671da177e4SLinus Torvalds 			if (copy)
12681da177e4SLinus Torvalds 				dst = copy + bv->bv_offset;
12691da177e4SLinus Torvalds 		}
12701da177e4SLinus Torvalds 		for (off = 0; off < bv->bv_len; off += blksize) {
12711da177e4SLinus Torvalds 			sector_t trkid = recid;
12721da177e4SLinus Torvalds 			unsigned int recoffs = sector_div(trkid, blk_per_trk);
12731da177e4SLinus Torvalds 			rcmd = cmd;
12741da177e4SLinus Torvalds 			count = blksize;
12751da177e4SLinus Torvalds 			/* Locate record for cdl special block ? */
12761da177e4SLinus Torvalds 			if (private->uses_cdl && recid < 2*blk_per_trk) {
12771da177e4SLinus Torvalds 				if (dasd_eckd_cdl_special(blk_per_trk, recid)){
12781da177e4SLinus Torvalds 					rcmd |= 0x8;
12791da177e4SLinus Torvalds 					count = dasd_eckd_cdl_reclen(recid);
1280ec5883abSHorst Hummel 					if (count < blksize &&
1281ec5883abSHorst Hummel 					    rq_data_dir(req) == READ)
12821da177e4SLinus Torvalds 						memset(dst + count, 0xe5,
12831da177e4SLinus Torvalds 						       blksize - count);
12841da177e4SLinus Torvalds 				}
12851da177e4SLinus Torvalds 				ccw[-1].flags |= CCW_FLAG_CC;
12861da177e4SLinus Torvalds 				locate_record(ccw++, LO_data++,
12871da177e4SLinus Torvalds 					      trkid, recoffs + 1,
12881da177e4SLinus Torvalds 					      1, rcmd, device, count);
12891da177e4SLinus Torvalds 			}
12901da177e4SLinus Torvalds 			/* Locate record for standard blocks ? */
12911da177e4SLinus Torvalds 			if (private->uses_cdl && recid == 2*blk_per_trk) {
12921da177e4SLinus Torvalds 				ccw[-1].flags |= CCW_FLAG_CC;
12931da177e4SLinus Torvalds 				locate_record(ccw++, LO_data++,
12941da177e4SLinus Torvalds 					      trkid, recoffs + 1,
12951da177e4SLinus Torvalds 					      last_rec - recid + 1,
12961da177e4SLinus Torvalds 					      cmd, device, count);
12971da177e4SLinus Torvalds 			}
12981da177e4SLinus Torvalds 			/* Read/write ccw. */
12991da177e4SLinus Torvalds 			ccw[-1].flags |= CCW_FLAG_CC;
13001da177e4SLinus Torvalds 			ccw->cmd_code = rcmd;
13011da177e4SLinus Torvalds 			ccw->count = count;
13021da177e4SLinus Torvalds 			if (idal_is_needed(dst, blksize)) {
13031da177e4SLinus Torvalds 				ccw->cda = (__u32)(addr_t) idaws;
13041da177e4SLinus Torvalds 				ccw->flags = CCW_FLAG_IDA;
13051da177e4SLinus Torvalds 				idaws = idal_create_words(idaws, dst, blksize);
13061da177e4SLinus Torvalds 			} else {
13071da177e4SLinus Torvalds 				ccw->cda = (__u32)(addr_t) dst;
13081da177e4SLinus Torvalds 				ccw->flags = 0;
13091da177e4SLinus Torvalds 			}
13101da177e4SLinus Torvalds 			ccw++;
13111da177e4SLinus Torvalds 			dst += blksize;
13121da177e4SLinus Torvalds 			recid++;
13131da177e4SLinus Torvalds 		}
13141da177e4SLinus Torvalds 	}
13154aff5e23SJens Axboe 	if (req->cmd_flags & REQ_FAILFAST)
13161c01b8a5SHorst Hummel 		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
13171da177e4SLinus Torvalds 	cqr->device = device;
13181da177e4SLinus Torvalds 	cqr->expires = 5 * 60 * HZ;	/* 5 minutes */
13191da177e4SLinus Torvalds 	cqr->lpm = private->path_data.ppm;
13201da177e4SLinus Torvalds 	cqr->retries = 256;
13211da177e4SLinus Torvalds 	cqr->buildclk = get_clock();
13221da177e4SLinus Torvalds 	cqr->status = DASD_CQR_FILLED;
13231da177e4SLinus Torvalds 	return cqr;
13241da177e4SLinus Torvalds }
13251da177e4SLinus Torvalds 
13261da177e4SLinus Torvalds static int
13271da177e4SLinus Torvalds dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req)
13281da177e4SLinus Torvalds {
13291da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
13301da177e4SLinus Torvalds 	struct ccw1 *ccw;
13311da177e4SLinus Torvalds 	struct bio *bio;
13321da177e4SLinus Torvalds 	struct bio_vec *bv;
13331da177e4SLinus Torvalds 	char *dst, *cda;
13341da177e4SLinus Torvalds 	unsigned int blksize, blk_per_trk, off;
13351da177e4SLinus Torvalds 	sector_t recid;
13361da177e4SLinus Torvalds 	int i, status;
13371da177e4SLinus Torvalds 
13381da177e4SLinus Torvalds 	if (!dasd_page_cache)
13391da177e4SLinus Torvalds 		goto out;
13401da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) cqr->device->private;
13411da177e4SLinus Torvalds 	blksize = cqr->device->bp_block;
13421da177e4SLinus Torvalds 	blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
13431da177e4SLinus Torvalds 	recid = req->sector >> cqr->device->s2b_shift;
13441da177e4SLinus Torvalds 	ccw = cqr->cpaddr;
13451da177e4SLinus Torvalds 	/* Skip over define extent & locate record. */
13461da177e4SLinus Torvalds 	ccw++;
13471da177e4SLinus Torvalds 	if (private->uses_cdl == 0 || recid > 2*blk_per_trk)
13481da177e4SLinus Torvalds 		ccw++;
13491da177e4SLinus Torvalds 	rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) {
13501da177e4SLinus Torvalds 		dst = page_address(bv->bv_page) + bv->bv_offset;
13511da177e4SLinus Torvalds 		for (off = 0; off < bv->bv_len; off += blksize) {
13521da177e4SLinus Torvalds 			/* Skip locate record. */
13531da177e4SLinus Torvalds 			if (private->uses_cdl && recid <= 2*blk_per_trk)
13541da177e4SLinus Torvalds 				ccw++;
13551da177e4SLinus Torvalds 			if (dst) {
13561da177e4SLinus Torvalds 				if (ccw->flags & CCW_FLAG_IDA)
13571da177e4SLinus Torvalds 					cda = *((char **)((addr_t) ccw->cda));
13581da177e4SLinus Torvalds 				else
13591da177e4SLinus Torvalds 					cda = (char *)((addr_t) ccw->cda);
13601da177e4SLinus Torvalds 				if (dst != cda) {
13611da177e4SLinus Torvalds 					if (rq_data_dir(req) == READ)
13621da177e4SLinus Torvalds 						memcpy(dst, cda, bv->bv_len);
13631da177e4SLinus Torvalds 					kmem_cache_free(dasd_page_cache,
13641da177e4SLinus Torvalds 					    (void *)((addr_t)cda & PAGE_MASK));
13651da177e4SLinus Torvalds 				}
13661da177e4SLinus Torvalds 				dst = NULL;
13671da177e4SLinus Torvalds 			}
13681da177e4SLinus Torvalds 			ccw++;
13691da177e4SLinus Torvalds 			recid++;
13701da177e4SLinus Torvalds 		}
13711da177e4SLinus Torvalds 	}
13721da177e4SLinus Torvalds out:
13731da177e4SLinus Torvalds 	status = cqr->status == DASD_CQR_DONE;
13741da177e4SLinus Torvalds 	dasd_sfree_request(cqr, cqr->device);
13751da177e4SLinus Torvalds 	return status;
13761da177e4SLinus Torvalds }
13771da177e4SLinus Torvalds 
13781da177e4SLinus Torvalds static int
13791da177e4SLinus Torvalds dasd_eckd_fill_info(struct dasd_device * device,
13801da177e4SLinus Torvalds 		    struct dasd_information2_t * info)
13811da177e4SLinus Torvalds {
13821da177e4SLinus Torvalds 	struct dasd_eckd_private *private;
13831da177e4SLinus Torvalds 
13841da177e4SLinus Torvalds 	private = (struct dasd_eckd_private *) device->private;
13851da177e4SLinus Torvalds 	info->label_block = 2;
13861da177e4SLinus Torvalds 	info->FBA_layout = private->uses_cdl ? 0 : 1;
13871da177e4SLinus Torvalds 	info->format = private->uses_cdl ? DASD_FORMAT_CDL : DASD_FORMAT_LDL;
13881da177e4SLinus Torvalds 	info->characteristics_size = sizeof(struct dasd_eckd_characteristics);
13891da177e4SLinus Torvalds 	memcpy(info->characteristics, &private->rdc_data,
13901da177e4SLinus Torvalds 	       sizeof(struct dasd_eckd_characteristics));
13911da177e4SLinus Torvalds 	info->confdata_size = sizeof (struct dasd_eckd_confdata);
13921da177e4SLinus Torvalds 	memcpy(info->configuration_data, &private->conf_data,
13931da177e4SLinus Torvalds 	       sizeof (struct dasd_eckd_confdata));
13941da177e4SLinus Torvalds 	return 0;
13951da177e4SLinus Torvalds }
13961da177e4SLinus Torvalds 
13971da177e4SLinus Torvalds /*
13981da177e4SLinus Torvalds  * SECTION: ioctl functions for eckd devices.
13991da177e4SLinus Torvalds  */
14001da177e4SLinus Torvalds 
14011da177e4SLinus Torvalds /*
14021da177e4SLinus Torvalds  * Release device ioctl.
14031da177e4SLinus Torvalds  * Buils a channel programm to releases a prior reserved
14041da177e4SLinus Torvalds  * (see dasd_eckd_reserve) device.
14051da177e4SLinus Torvalds  */
14061da177e4SLinus Torvalds static int
14071107ccfbSChristoph Hellwig dasd_eckd_release(struct dasd_device *device)
14081da177e4SLinus Torvalds {
14091da177e4SLinus Torvalds 	struct dasd_ccw_req *cqr;
14101da177e4SLinus Torvalds 	int rc;
14111da177e4SLinus Torvalds 
14121da177e4SLinus Torvalds 	if (!capable(CAP_SYS_ADMIN))
14131da177e4SLinus Torvalds 		return -EACCES;
14141da177e4SLinus Torvalds 
14151da177e4SLinus Torvalds 	cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
14161da177e4SLinus Torvalds 				   1, 32, device);
14171da177e4SLinus Torvalds 	if (IS_ERR(cqr)) {
14181da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_WARNING, device, "%s",
14191da177e4SLinus Torvalds 			    "Could not allocate initialization request");
14201da177e4SLinus Torvalds 		return PTR_ERR(cqr);
14211da177e4SLinus Torvalds 	}
14221da177e4SLinus Torvalds 	cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RELEASE;
14231da177e4SLinus Torvalds         cqr->cpaddr->flags |= CCW_FLAG_SLI;
14241da177e4SLinus Torvalds         cqr->cpaddr->count = 32;
14251da177e4SLinus Torvalds 	cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
14261da177e4SLinus Torvalds 	cqr->device = device;
14271da177e4SLinus Torvalds 	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
14281c01b8a5SHorst Hummel 	set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
1429336c340bSHorst Hummel 	cqr->retries = 2;	/* set retry counter to enable basic ERP */
14301da177e4SLinus Torvalds 	cqr->expires = 2 * HZ;
14311da177e4SLinus Torvalds 	cqr->buildclk = get_clock();
14321da177e4SLinus Torvalds 	cqr->status = DASD_CQR_FILLED;
14331da177e4SLinus Torvalds 
14341da177e4SLinus Torvalds 	rc = dasd_sleep_on_immediatly(cqr);
14351da177e4SLinus Torvalds 
14361da177e4SLinus Torvalds 	dasd_sfree_request(cqr, cqr->device);
14371da177e4SLinus Torvalds 	return rc;
14381da177e4SLinus Torvalds }
14391da177e4SLinus Torvalds 
14401da177e4SLinus Torvalds /*
14411da177e4SLinus Torvalds  * Reserve device ioctl.
14421da177e4SLinus Torvalds  * Options are set to 'synchronous wait for interrupt' and
14431da177e4SLinus Torvalds  * 'timeout the request'. This leads to a terminate IO if
14441da177e4SLinus Torvalds  * the interrupt is outstanding for a certain time.
14451da177e4SLinus Torvalds  */
14461da177e4SLinus Torvalds static int
14471107ccfbSChristoph Hellwig dasd_eckd_reserve(struct dasd_device *device)
14481da177e4SLinus Torvalds {
14491da177e4SLinus Torvalds 	struct dasd_ccw_req *cqr;
14501da177e4SLinus Torvalds 	int rc;
14511da177e4SLinus Torvalds 
14521da177e4SLinus Torvalds 	if (!capable(CAP_SYS_ADMIN))
14531da177e4SLinus Torvalds 		return -EACCES;
14541da177e4SLinus Torvalds 
14551da177e4SLinus Torvalds 	cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
14561da177e4SLinus Torvalds 				   1, 32, device);
14571da177e4SLinus Torvalds 	if (IS_ERR(cqr)) {
14581da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_WARNING, device, "%s",
14591da177e4SLinus Torvalds 			    "Could not allocate initialization request");
14601da177e4SLinus Torvalds 		return PTR_ERR(cqr);
14611da177e4SLinus Torvalds 	}
14621da177e4SLinus Torvalds 	cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RESERVE;
14631da177e4SLinus Torvalds         cqr->cpaddr->flags |= CCW_FLAG_SLI;
14641da177e4SLinus Torvalds         cqr->cpaddr->count = 32;
14651da177e4SLinus Torvalds 	cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
14661da177e4SLinus Torvalds 	cqr->device = device;
14671da177e4SLinus Torvalds 	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
14681c01b8a5SHorst Hummel 	set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
1469336c340bSHorst Hummel 	cqr->retries = 2;	/* set retry counter to enable basic ERP */
14701da177e4SLinus Torvalds 	cqr->expires = 2 * HZ;
14711da177e4SLinus Torvalds 	cqr->buildclk = get_clock();
14721da177e4SLinus Torvalds 	cqr->status = DASD_CQR_FILLED;
14731da177e4SLinus Torvalds 
14741da177e4SLinus Torvalds 	rc = dasd_sleep_on_immediatly(cqr);
14751da177e4SLinus Torvalds 
14761da177e4SLinus Torvalds 	dasd_sfree_request(cqr, cqr->device);
14771da177e4SLinus Torvalds 	return rc;
14781da177e4SLinus Torvalds }
14791da177e4SLinus Torvalds 
14801da177e4SLinus Torvalds /*
14811da177e4SLinus Torvalds  * Steal lock ioctl - unconditional reserve device.
14821da177e4SLinus Torvalds  * Buils a channel programm to break a device's reservation.
14831da177e4SLinus Torvalds  * (unconditional reserve)
14841da177e4SLinus Torvalds  */
14851da177e4SLinus Torvalds static int
14861107ccfbSChristoph Hellwig dasd_eckd_steal_lock(struct dasd_device *device)
14871da177e4SLinus Torvalds {
14881da177e4SLinus Torvalds 	struct dasd_ccw_req *cqr;
14891da177e4SLinus Torvalds 	int rc;
14901da177e4SLinus Torvalds 
14911da177e4SLinus Torvalds 	if (!capable(CAP_SYS_ADMIN))
14921da177e4SLinus Torvalds 		return -EACCES;
14931da177e4SLinus Torvalds 
14941da177e4SLinus Torvalds 	cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
14951da177e4SLinus Torvalds 				   1, 32, device);
14961da177e4SLinus Torvalds 	if (IS_ERR(cqr)) {
14971da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_WARNING, device, "%s",
14981da177e4SLinus Torvalds 			    "Could not allocate initialization request");
14991da177e4SLinus Torvalds 		return PTR_ERR(cqr);
15001da177e4SLinus Torvalds 	}
15011da177e4SLinus Torvalds 	cqr->cpaddr->cmd_code = DASD_ECKD_CCW_SLCK;
15021da177e4SLinus Torvalds         cqr->cpaddr->flags |= CCW_FLAG_SLI;
15031da177e4SLinus Torvalds         cqr->cpaddr->count = 32;
15041da177e4SLinus Torvalds 	cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
15051da177e4SLinus Torvalds 	cqr->device = device;
15061da177e4SLinus Torvalds 	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
15071c01b8a5SHorst Hummel 	set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
1508336c340bSHorst Hummel 	cqr->retries = 2;	/* set retry counter to enable basic ERP */
15091da177e4SLinus Torvalds 	cqr->expires = 2 * HZ;
15101da177e4SLinus Torvalds 	cqr->buildclk = get_clock();
15111da177e4SLinus Torvalds 	cqr->status = DASD_CQR_FILLED;
15121da177e4SLinus Torvalds 
15131da177e4SLinus Torvalds 	rc = dasd_sleep_on_immediatly(cqr);
15141da177e4SLinus Torvalds 
15151da177e4SLinus Torvalds 	dasd_sfree_request(cqr, cqr->device);
15161da177e4SLinus Torvalds 	return rc;
15171da177e4SLinus Torvalds }
15181da177e4SLinus Torvalds 
15191da177e4SLinus Torvalds /*
15201da177e4SLinus Torvalds  * Read performance statistics
15211da177e4SLinus Torvalds  */
15221da177e4SLinus Torvalds static int
15231107ccfbSChristoph Hellwig dasd_eckd_performance(struct dasd_device *device, void __user *argp)
15241da177e4SLinus Torvalds {
15251da177e4SLinus Torvalds 	struct dasd_psf_prssd_data *prssdp;
15261da177e4SLinus Torvalds 	struct dasd_rssd_perf_stats_t *stats;
15271da177e4SLinus Torvalds 	struct dasd_ccw_req *cqr;
15281da177e4SLinus Torvalds 	struct ccw1 *ccw;
15291da177e4SLinus Torvalds 	int rc;
15301da177e4SLinus Torvalds 
15311da177e4SLinus Torvalds 	cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
15321da177e4SLinus Torvalds 				   1 /* PSF */  + 1 /* RSSD */ ,
15331da177e4SLinus Torvalds 				   (sizeof (struct dasd_psf_prssd_data) +
15341da177e4SLinus Torvalds 				    sizeof (struct dasd_rssd_perf_stats_t)),
15351da177e4SLinus Torvalds 				   device);
15361da177e4SLinus Torvalds 	if (IS_ERR(cqr)) {
15371da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_WARNING, device, "%s",
15381da177e4SLinus Torvalds 			    "Could not allocate initialization request");
15391da177e4SLinus Torvalds 		return PTR_ERR(cqr);
15401da177e4SLinus Torvalds 	}
15411da177e4SLinus Torvalds 	cqr->device = device;
15421da177e4SLinus Torvalds 	cqr->retries = 0;
15431da177e4SLinus Torvalds 	cqr->expires = 10 * HZ;
15441da177e4SLinus Torvalds 
15451da177e4SLinus Torvalds 	/* Prepare for Read Subsystem Data */
15461da177e4SLinus Torvalds 	prssdp = (struct dasd_psf_prssd_data *) cqr->data;
15471da177e4SLinus Torvalds 	memset(prssdp, 0, sizeof (struct dasd_psf_prssd_data));
15481da177e4SLinus Torvalds 	prssdp->order = PSF_ORDER_PRSSD;
15491da177e4SLinus Torvalds 	prssdp->suborder = 0x01;	/* Perfomance Statistics */
15501da177e4SLinus Torvalds 	prssdp->varies[1] = 0x01;	/* Perf Statistics for the Subsystem */
15511da177e4SLinus Torvalds 
15521da177e4SLinus Torvalds 	ccw = cqr->cpaddr;
15531da177e4SLinus Torvalds 	ccw->cmd_code = DASD_ECKD_CCW_PSF;
15541da177e4SLinus Torvalds 	ccw->count = sizeof (struct dasd_psf_prssd_data);
15551da177e4SLinus Torvalds 	ccw->flags |= CCW_FLAG_CC;
15561da177e4SLinus Torvalds 	ccw->cda = (__u32)(addr_t) prssdp;
15571da177e4SLinus Torvalds 
15581da177e4SLinus Torvalds 	/* Read Subsystem Data - Performance Statistics */
15591da177e4SLinus Torvalds 	stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
15601da177e4SLinus Torvalds 	memset(stats, 0, sizeof (struct dasd_rssd_perf_stats_t));
15611da177e4SLinus Torvalds 
15621da177e4SLinus Torvalds 	ccw++;
15631da177e4SLinus Torvalds 	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
15641da177e4SLinus Torvalds 	ccw->count = sizeof (struct dasd_rssd_perf_stats_t);
15651da177e4SLinus Torvalds 	ccw->cda = (__u32)(addr_t) stats;
15661da177e4SLinus Torvalds 
15671da177e4SLinus Torvalds 	cqr->buildclk = get_clock();
15681da177e4SLinus Torvalds 	cqr->status = DASD_CQR_FILLED;
15691da177e4SLinus Torvalds 	rc = dasd_sleep_on(cqr);
15701da177e4SLinus Torvalds 	if (rc == 0) {
15711da177e4SLinus Torvalds 		/* Prepare for Read Subsystem Data */
15721da177e4SLinus Torvalds 		prssdp = (struct dasd_psf_prssd_data *) cqr->data;
15731da177e4SLinus Torvalds 		stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
15741107ccfbSChristoph Hellwig 		if (copy_to_user(argp, stats,
15751107ccfbSChristoph Hellwig 				 sizeof(struct dasd_rssd_perf_stats_t)))
15761107ccfbSChristoph Hellwig 			rc = -EFAULT;
15771da177e4SLinus Torvalds 	}
15781da177e4SLinus Torvalds 	dasd_sfree_request(cqr, cqr->device);
15791da177e4SLinus Torvalds 	return rc;
15801da177e4SLinus Torvalds }
15811da177e4SLinus Torvalds 
15821da177e4SLinus Torvalds /*
15831da177e4SLinus Torvalds  * Get attributes (cache operations)
15841da177e4SLinus Torvalds  * Returnes the cache attributes used in Define Extend (DE).
15851da177e4SLinus Torvalds  */
15861da177e4SLinus Torvalds static int
15871107ccfbSChristoph Hellwig dasd_eckd_get_attrib(struct dasd_device *device, void __user *argp)
15881da177e4SLinus Torvalds {
15891107ccfbSChristoph Hellwig 	struct dasd_eckd_private *private =
15901107ccfbSChristoph Hellwig 		(struct dasd_eckd_private *)device->private;
15911107ccfbSChristoph Hellwig 	struct attrib_data_t attrib = private->attrib;
15921da177e4SLinus Torvalds 	int rc;
15931da177e4SLinus Torvalds 
15941da177e4SLinus Torvalds         if (!capable(CAP_SYS_ADMIN))
15951da177e4SLinus Torvalds                 return -EACCES;
15961107ccfbSChristoph Hellwig 	if (!argp)
15971da177e4SLinus Torvalds                 return -EINVAL;
15981da177e4SLinus Torvalds 
15991107ccfbSChristoph Hellwig 	rc = 0;
16001107ccfbSChristoph Hellwig 	if (copy_to_user(argp, (long *) &attrib,
16011107ccfbSChristoph Hellwig 			 sizeof (struct attrib_data_t)))
16021107ccfbSChristoph Hellwig 		rc = -EFAULT;
16031da177e4SLinus Torvalds 
16041da177e4SLinus Torvalds 	return rc;
16051da177e4SLinus Torvalds }
16061da177e4SLinus Torvalds 
16071da177e4SLinus Torvalds /*
16081da177e4SLinus Torvalds  * Set attributes (cache operations)
16091da177e4SLinus Torvalds  * Stores the attributes for cache operation to be used in Define Extend (DE).
16101da177e4SLinus Torvalds  */
16111da177e4SLinus Torvalds static int
16121107ccfbSChristoph Hellwig dasd_eckd_set_attrib(struct dasd_device *device, void __user *argp)
16131da177e4SLinus Torvalds {
16141107ccfbSChristoph Hellwig 	struct dasd_eckd_private *private =
16151107ccfbSChristoph Hellwig 		(struct dasd_eckd_private *)device->private;
16161da177e4SLinus Torvalds 	struct attrib_data_t attrib;
16171da177e4SLinus Torvalds 
16181da177e4SLinus Torvalds 	if (!capable(CAP_SYS_ADMIN))
16191da177e4SLinus Torvalds 		return -EACCES;
16201107ccfbSChristoph Hellwig 	if (!argp)
16211da177e4SLinus Torvalds 		return -EINVAL;
16221da177e4SLinus Torvalds 
16231107ccfbSChristoph Hellwig 	if (copy_from_user(&attrib, argp, sizeof(struct attrib_data_t)))
16241da177e4SLinus Torvalds 		return -EFAULT;
16251da177e4SLinus Torvalds 	private->attrib = attrib;
16261da177e4SLinus Torvalds 
16271da177e4SLinus Torvalds 	DEV_MESSAGE(KERN_INFO, device,
16281da177e4SLinus Torvalds 		    "cache operation mode set to %x (%i cylinder prestage)",
16291da177e4SLinus Torvalds 		    private->attrib.operation, private->attrib.nr_cyl);
16301da177e4SLinus Torvalds 	return 0;
16311da177e4SLinus Torvalds }
16321da177e4SLinus Torvalds 
16331107ccfbSChristoph Hellwig static int
16341107ccfbSChristoph Hellwig dasd_eckd_ioctl(struct dasd_device *device, unsigned int cmd, void __user *argp)
16351107ccfbSChristoph Hellwig {
16361107ccfbSChristoph Hellwig 	switch (cmd) {
16371107ccfbSChristoph Hellwig 	case BIODASDGATTR:
16381107ccfbSChristoph Hellwig 		return dasd_eckd_get_attrib(device, argp);
16391107ccfbSChristoph Hellwig 	case BIODASDSATTR:
16401107ccfbSChristoph Hellwig 		return dasd_eckd_set_attrib(device, argp);
16411107ccfbSChristoph Hellwig 	case BIODASDPSRD:
16421107ccfbSChristoph Hellwig 		return dasd_eckd_performance(device, argp);
16431107ccfbSChristoph Hellwig 	case BIODASDRLSE:
16441107ccfbSChristoph Hellwig 		return dasd_eckd_release(device);
16451107ccfbSChristoph Hellwig 	case BIODASDRSRV:
16461107ccfbSChristoph Hellwig 		return dasd_eckd_reserve(device);
16471107ccfbSChristoph Hellwig 	case BIODASDSLCK:
16481107ccfbSChristoph Hellwig 		return dasd_eckd_steal_lock(device);
16491107ccfbSChristoph Hellwig 	default:
16501107ccfbSChristoph Hellwig 		return -ENOIOCTLCMD;
16511107ccfbSChristoph Hellwig 	}
16521107ccfbSChristoph Hellwig }
16531107ccfbSChristoph Hellwig 
16541da177e4SLinus Torvalds /*
1655445b5b49SHorst Hummel  * Dump the range of CCWs into 'page' buffer
1656445b5b49SHorst Hummel  * and return number of printed chars.
1657445b5b49SHorst Hummel  */
16584d284cacSHeiko Carstens static int
1659445b5b49SHorst Hummel dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page)
1660445b5b49SHorst Hummel {
1661445b5b49SHorst Hummel 	int len, count;
1662445b5b49SHorst Hummel 	char *datap;
1663445b5b49SHorst Hummel 
1664445b5b49SHorst Hummel 	len = 0;
1665445b5b49SHorst Hummel 	while (from <= to) {
1666445b5b49SHorst Hummel 		len += sprintf(page + len, KERN_ERR PRINTK_HEADER
1667445b5b49SHorst Hummel 			       " CCW %p: %08X %08X DAT:",
1668445b5b49SHorst Hummel 			       from, ((int *) from)[0], ((int *) from)[1]);
1669445b5b49SHorst Hummel 
1670445b5b49SHorst Hummel 		/* get pointer to data (consider IDALs) */
1671445b5b49SHorst Hummel 		if (from->flags & CCW_FLAG_IDA)
1672445b5b49SHorst Hummel 			datap = (char *) *((addr_t *) (addr_t) from->cda);
1673445b5b49SHorst Hummel 		else
1674445b5b49SHorst Hummel 			datap = (char *) ((addr_t) from->cda);
1675445b5b49SHorst Hummel 
1676445b5b49SHorst Hummel 		/* dump data (max 32 bytes) */
1677445b5b49SHorst Hummel 		for (count = 0; count < from->count && count < 32; count++) {
1678445b5b49SHorst Hummel 			if (count % 8 == 0) len += sprintf(page + len, " ");
1679445b5b49SHorst Hummel 			if (count % 4 == 0) len += sprintf(page + len, " ");
1680445b5b49SHorst Hummel 			len += sprintf(page + len, "%02x", datap[count]);
1681445b5b49SHorst Hummel 		}
1682445b5b49SHorst Hummel 		len += sprintf(page + len, "\n");
1683445b5b49SHorst Hummel 		from++;
1684445b5b49SHorst Hummel 	}
1685445b5b49SHorst Hummel 	return len;
1686445b5b49SHorst Hummel }
1687445b5b49SHorst Hummel 
1688445b5b49SHorst Hummel /*
16891da177e4SLinus Torvalds  * Print sense data and related channel program.
16901da177e4SLinus Torvalds  * Parts are printed because printk buffer is only 1024 bytes.
16911da177e4SLinus Torvalds  */
16921da177e4SLinus Torvalds static void
16931da177e4SLinus Torvalds dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
16941da177e4SLinus Torvalds 		     struct irb *irb)
16951da177e4SLinus Torvalds {
16961da177e4SLinus Torvalds 	char *page;
1697445b5b49SHorst Hummel 	struct ccw1 *first, *last, *fail, *from, *to;
1698445b5b49SHorst Hummel 	int len, sl, sct;
16991da177e4SLinus Torvalds 
17001da177e4SLinus Torvalds 	page = (char *) get_zeroed_page(GFP_ATOMIC);
17011da177e4SLinus Torvalds 	if (page == NULL) {
17021da177e4SLinus Torvalds 		DEV_MESSAGE(KERN_ERR, device, " %s",
17031da177e4SLinus Torvalds 			    "No memory to dump sense data");
17041da177e4SLinus Torvalds 		return;
17051da177e4SLinus Torvalds 	}
1706445b5b49SHorst Hummel 	/* dump the sense data */
17071da177e4SLinus Torvalds 	len = sprintf(page,  KERN_ERR PRINTK_HEADER
17081da177e4SLinus Torvalds 		      " I/O status report for device %s:\n",
17091da177e4SLinus Torvalds 		      device->cdev->dev.bus_id);
17101da177e4SLinus Torvalds 	len += sprintf(page + len, KERN_ERR PRINTK_HEADER
17111da177e4SLinus Torvalds 		       " in req: %p CS: 0x%02X DS: 0x%02X\n", req,
17121da177e4SLinus Torvalds 		       irb->scsw.cstat, irb->scsw.dstat);
17131da177e4SLinus Torvalds 	len += sprintf(page + len, KERN_ERR PRINTK_HEADER
17141da177e4SLinus Torvalds 		       " device %s: Failing CCW: %p\n",
17151da177e4SLinus Torvalds 		       device->cdev->dev.bus_id,
17161da177e4SLinus Torvalds 		       (void *) (addr_t) irb->scsw.cpa);
17171da177e4SLinus Torvalds 	if (irb->esw.esw0.erw.cons) {
17181da177e4SLinus Torvalds 		for (sl = 0; sl < 4; sl++) {
17191da177e4SLinus Torvalds 			len += sprintf(page + len, KERN_ERR PRINTK_HEADER
17201da177e4SLinus Torvalds 				       " Sense(hex) %2d-%2d:",
17211da177e4SLinus Torvalds 				       (8 * sl), ((8 * sl) + 7));
17221da177e4SLinus Torvalds 
17231da177e4SLinus Torvalds 			for (sct = 0; sct < 8; sct++) {
17241da177e4SLinus Torvalds 				len += sprintf(page + len, " %02x",
17251da177e4SLinus Torvalds 					       irb->ecw[8 * sl + sct]);
17261da177e4SLinus Torvalds 			}
17271da177e4SLinus Torvalds 			len += sprintf(page + len, "\n");
17281da177e4SLinus Torvalds 		}
17291da177e4SLinus Torvalds 
17301da177e4SLinus Torvalds 		if (irb->ecw[27] & DASD_SENSE_BIT_0) {
17311da177e4SLinus Torvalds 			/* 24 Byte Sense Data */
1732445b5b49SHorst Hummel 			sprintf(page + len, KERN_ERR PRINTK_HEADER
17331da177e4SLinus Torvalds 				" 24 Byte: %x MSG %x, "
17341da177e4SLinus Torvalds 				"%s MSGb to SYSOP\n",
17351da177e4SLinus Torvalds 				irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
17361da177e4SLinus Torvalds 				irb->ecw[1] & 0x10 ? "" : "no");
17371da177e4SLinus Torvalds 		} else {
17381da177e4SLinus Torvalds 			/* 32 Byte Sense Data */
1739445b5b49SHorst Hummel 			sprintf(page + len, KERN_ERR PRINTK_HEADER
17401da177e4SLinus Torvalds 				" 32 Byte: Format: %x "
17411da177e4SLinus Torvalds 				"Exception class %x\n",
17421da177e4SLinus Torvalds 				irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
17431da177e4SLinus Torvalds 		}
17441da177e4SLinus Torvalds 	} else {
1745445b5b49SHorst Hummel 		sprintf(page + len, KERN_ERR PRINTK_HEADER
17461da177e4SLinus Torvalds 			" SORRY - NO VALID SENSE AVAILABLE\n");
17471da177e4SLinus Torvalds 	}
1748445b5b49SHorst Hummel 	printk("%s", page);
17491da177e4SLinus Torvalds 
1750445b5b49SHorst Hummel 	/* dump the Channel Program (max 140 Bytes per line) */
1751445b5b49SHorst Hummel 	/* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */
1752445b5b49SHorst Hummel 	first = req->cpaddr;
1753445b5b49SHorst Hummel 	for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
1754445b5b49SHorst Hummel 	to = min(first + 6, last);
17551da177e4SLinus Torvalds 	len = sprintf(page,  KERN_ERR PRINTK_HEADER
17561da177e4SLinus Torvalds 		      " Related CP in req: %p\n", req);
1757445b5b49SHorst Hummel 	dasd_eckd_dump_ccw_range(first, to, page + len);
1758445b5b49SHorst Hummel 	printk("%s", page);
17591da177e4SLinus Torvalds 
1760445b5b49SHorst Hummel 	/* print failing CCW area (maximum 4) */
1761445b5b49SHorst Hummel 	/* scsw->cda is either valid or zero  */
17621da177e4SLinus Torvalds 	len = 0;
1763445b5b49SHorst Hummel 	from = ++to;
1764445b5b49SHorst Hummel 	fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */
1765445b5b49SHorst Hummel 	if (from <  fail - 2) {
1766445b5b49SHorst Hummel 		from = fail - 2;     /* there is a gap - print header */
1767445b5b49SHorst Hummel 		len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
17681da177e4SLinus Torvalds 	}
1769445b5b49SHorst Hummel 	to = min(fail + 1, last);
1770445b5b49SHorst Hummel 	len += dasd_eckd_dump_ccw_range(from, to, page + len);
17711da177e4SLinus Torvalds 
1772445b5b49SHorst Hummel 	/* print last CCWs (maximum 2) */
1773445b5b49SHorst Hummel 	from = max(from, ++to);
1774445b5b49SHorst Hummel 	if (from < last - 1) {
1775445b5b49SHorst Hummel 		from = last - 1;     /* there is a gap - print header */
17761da177e4SLinus Torvalds 		len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
17771da177e4SLinus Torvalds 	}
1778445b5b49SHorst Hummel 	len += dasd_eckd_dump_ccw_range(from, last, page + len);
17791da177e4SLinus Torvalds 	if (len > 0)
1780445b5b49SHorst Hummel 		printk("%s", page);
17811da177e4SLinus Torvalds 	free_page((unsigned long) page);
17821da177e4SLinus Torvalds }
17831da177e4SLinus Torvalds 
17841da177e4SLinus Torvalds /*
17851da177e4SLinus Torvalds  * max_blocks is dependent on the amount of storage that is available
17861da177e4SLinus Torvalds  * in the static io buffer for each device. Currently each device has
17871da177e4SLinus Torvalds  * 8192 bytes (=2 pages). For 64 bit one dasd_mchunkt_t structure has
17881da177e4SLinus Torvalds  * 24 bytes, the struct dasd_ccw_req has 136 bytes and each block can use
17891da177e4SLinus Torvalds  * up to 16 bytes (8 for the ccw and 8 for the idal pointer). In
17901da177e4SLinus Torvalds  * addition we have one define extent ccw + 16 bytes of data and one
17911da177e4SLinus Torvalds  * locate record ccw + 16 bytes of data. That makes:
17921da177e4SLinus Torvalds  * (8192 - 24 - 136 - 8 - 16 - 8 - 16) / 16 = 499 blocks at maximum.
17931da177e4SLinus Torvalds  * We want to fit two into the available memory so that we can immediately
17941da177e4SLinus Torvalds  * start the next request if one finishes off. That makes 249.5 blocks
17951da177e4SLinus Torvalds  * for one request. Give a little safety and the result is 240.
17961da177e4SLinus Torvalds  */
17971da177e4SLinus Torvalds static struct dasd_discipline dasd_eckd_discipline = {
17981da177e4SLinus Torvalds 	.owner = THIS_MODULE,
17991da177e4SLinus Torvalds 	.name = "ECKD",
18001da177e4SLinus Torvalds 	.ebcname = "ECKD",
18011da177e4SLinus Torvalds 	.max_blocks = 240,
18021da177e4SLinus Torvalds 	.check_device = dasd_eckd_check_characteristics,
18031da177e4SLinus Torvalds 	.do_analysis = dasd_eckd_do_analysis,
18041da177e4SLinus Torvalds 	.fill_geometry = dasd_eckd_fill_geometry,
18051da177e4SLinus Torvalds 	.start_IO = dasd_start_IO,
18061da177e4SLinus Torvalds 	.term_IO = dasd_term_IO,
18071da177e4SLinus Torvalds 	.format_device = dasd_eckd_format_device,
18081da177e4SLinus Torvalds 	.examine_error = dasd_eckd_examine_error,
18091da177e4SLinus Torvalds 	.erp_action = dasd_eckd_erp_action,
18101da177e4SLinus Torvalds 	.erp_postaction = dasd_eckd_erp_postaction,
18111da177e4SLinus Torvalds 	.build_cp = dasd_eckd_build_cp,
18121da177e4SLinus Torvalds 	.free_cp = dasd_eckd_free_cp,
18131da177e4SLinus Torvalds 	.dump_sense = dasd_eckd_dump_sense,
18141da177e4SLinus Torvalds 	.fill_info = dasd_eckd_fill_info,
18151107ccfbSChristoph Hellwig 	.ioctl = dasd_eckd_ioctl,
18161da177e4SLinus Torvalds };
18171da177e4SLinus Torvalds 
18181da177e4SLinus Torvalds static int __init
18191da177e4SLinus Torvalds dasd_eckd_init(void)
18201da177e4SLinus Torvalds {
18211da177e4SLinus Torvalds 	ASCEBC(dasd_eckd_discipline.ebcname, 4);
182240545573SHorst Hummel 	return ccw_driver_register(&dasd_eckd_driver);
18231da177e4SLinus Torvalds }
18241da177e4SLinus Torvalds 
18251da177e4SLinus Torvalds static void __exit
18261da177e4SLinus Torvalds dasd_eckd_cleanup(void)
18271da177e4SLinus Torvalds {
18281da177e4SLinus Torvalds 	ccw_driver_unregister(&dasd_eckd_driver);
18291da177e4SLinus Torvalds }
18301da177e4SLinus Torvalds 
18311da177e4SLinus Torvalds module_init(dasd_eckd_init);
18321da177e4SLinus Torvalds module_exit(dasd_eckd_cleanup);
1833