xref: /csrg-svn/sys/i386/stand/wd.c (revision 48827)
141085Swilliam /*-
241085Swilliam  * Copyright (c) 1990 The Regents of the University of California.
341085Swilliam  * All rights reserved.
441085Swilliam  *
541085Swilliam  * This code is derived from software contributed to Berkeley by
641085Swilliam  * William Jolitz.
741085Swilliam  *
8*48827Swilliam  * %sccs.include.redist.c%
941085Swilliam  *
10*48827Swilliam  *	@(#)wd.c	7.2 (Berkeley) 04/28/91
1141085Swilliam  */
1241085Swilliam 
1341085Swilliam /*  device driver for winchester disk  */
1441085Swilliam 
15*48827Swilliam #include "param.h"
16*48827Swilliam #include "dkbad.h"
17*48827Swilliam #include "disklabel.h"
18*48827Swilliam #include "i386/isa/isa.h"
19*48827Swilliam #include "i386/isa/wdreg.h"
2041085Swilliam #include "saio.h"
2141085Swilliam 
2241085Swilliam #define	NWD		2	/* number of hard disk units supported, max 2 */
2341085Swilliam #define	RETRIES		5	/* number of retries before giving up */
2441085Swilliam 
25*48827Swilliam int noretries, wdquiet;
26*48827Swilliam /*#define WDDEBUG*/
2741085Swilliam 
2841085Swilliam #ifdef	SMALL
2941085Swilliam extern struct disklabel disklabel;
3041085Swilliam #else
3141085Swilliam struct disklabel wdsizes[NWD];
3241085Swilliam #endif
3341085Swilliam 
34*48827Swilliam extern cyloffset ;		/* bootstrap's idea of cylinder for disklabel */
35*48827Swilliam 
3641085Swilliam /*
3741085Swilliam  * Record for the bad block forwarding code.
3841085Swilliam  * This is initialized to be empty until the bad-sector table
3941085Swilliam  * is read from the disk.
4041085Swilliam  */
4141085Swilliam #define TRKSEC(trk,sec)	((trk << 8) + sec)
4241085Swilliam 
4341085Swilliam struct	dkbad	dkbad[NWD];
44*48827Swilliam static wdcport;
4541085Swilliam 
4641085Swilliam wdopen(io)
4741085Swilliam 	register struct iob *io;
4841085Swilliam {
4941085Swilliam         register struct disklabel *dd;
5041085Swilliam 
51*48827Swilliam #ifdef WDDEBUG
52*48827Swilliam 	printf("wdopen ");
53*48827Swilliam #endif
5441085Swilliam #ifdef SMALL
5541085Swilliam         dd = &disklabel;
5641085Swilliam #else
57*48827Swilliam         dd = &wdsizes[io->i_unit];
58*48827Swilliam 	if (io->i_part > 8)
5941085Swilliam                 _stop("Invalid partition number");
60*48827Swilliam 	if(io->i_ctlr > 1)
61*48827Swilliam                 _stop("Invalid controller number");
6241085Swilliam #endif
6341085Swilliam         if (wdinit(io))
6441085Swilliam                 _stop("wd initialization error");
65*48827Swilliam 	io->i_boff = dd->d_partitions[io->i_part].p_offset ;
66*48827Swilliam 	return(0);
6741085Swilliam }
6841085Swilliam 
6941085Swilliam wdstrategy(io,func)
7041085Swilliam 	register struct iob *io;
7141085Swilliam {
7241085Swilliam 	register int iosize;    /* number of sectors to do IO for this loop */
7341085Swilliam 	register daddr_t sector;
7441085Swilliam 	int nblocks, cyloff;
7541085Swilliam 	int unit, partition;
7641085Swilliam 	char *address;
7741085Swilliam 	register struct disklabel *dd;
7841085Swilliam 
79*48827Swilliam 	unit = io->i_unit;
80*48827Swilliam 	partition = io->i_part;
81*48827Swilliam #ifdef WDDEBUG
82*48827Swilliam 	printf("wdstrat %d %d ", unit, partition);
83*48827Swilliam #endif
8441085Swilliam #ifdef	SMALL
8541085Swilliam 	dd = &disklabel;
8641085Swilliam #else
8741085Swilliam 	dd = &wdsizes[unit];
8841085Swilliam #endif
89*48827Swilliam         iosize = io->i_cc / dd->d_secsize;
9041085Swilliam 	/*
9141085Swilliam 	 * Convert PGSIZE "blocks" to sectors.
9241085Swilliam 	 * Note: doing the conversions this way limits the partition size
9341085Swilliam 	 * to about 8 million sectors (1-8 Gb).
9441085Swilliam 	 */
95*48827Swilliam 	sector = (unsigned long) io->i_bn * DEV_BSIZE / dd->d_secsize;
96*48827Swilliam 	nblocks = dd->d_partitions[partition].p_size;
97*48827Swilliam #ifndef SMALL
9841085Swilliam         if (iosize < 0 || sector + iosize > nblocks || sector < 0) {
9941085Swilliam #ifdef WDDEBUG
10041085Swilliam 		printf("bn = %d; sectors = %d; partition = %d; fssize = %d\n",
10141085Swilliam 			io->i_bn, iosize, partition, nblocks);
10241085Swilliam #endif
10341085Swilliam                 printf("wdstrategy - I/O out of filesystem boundaries\n");
10441085Swilliam 		return(-1);
10541085Swilliam 	}
106*48827Swilliam 	if (io->i_bn * DEV_BSIZE % dd->d_secsize) {
10741085Swilliam 		printf("wdstrategy - transfer starts in midsector\n");
10841085Swilliam 		return(-1);
10941085Swilliam 	}
110*48827Swilliam         if (io->i_cc % dd->d_secsize) {
11141085Swilliam 		printf("wd: transfer of partial sector\n");
11241085Swilliam 		return(-1);
11341085Swilliam 	}
114*48827Swilliam #endif
11541085Swilliam 
11641085Swilliam 	address = io->i_ma;
11741085Swilliam         while (iosize > 0) {
11841085Swilliam                 if (wdio(func, unit, sector, address))
11941085Swilliam                         return(-1);
12041085Swilliam 		iosize--;
12141085Swilliam 		sector++;
122*48827Swilliam                 address += dd->d_secsize;
12341085Swilliam         }
12441085Swilliam         return(io->i_cc);
12541085Swilliam }
12641085Swilliam 
12741085Swilliam /*
12841085Swilliam  * Routine to do a one-sector I/O operation, and wait for it
12941085Swilliam  * to complete.
13041085Swilliam  */
13141085Swilliam wdio(func, unit, blknm, addr)
13241085Swilliam         short *addr;
13341085Swilliam {
13441085Swilliam 	struct disklabel *dd;
135*48827Swilliam 	register wdc = wdcport;
13641085Swilliam 	struct bt_bad *bt_ptr;
13741085Swilliam         int    i;
13841085Swilliam 	int retries = 0;
13941085Swilliam         long    cylin, head, sector;
140*48827Swilliam         u_char opcode, erro;
14141085Swilliam 
14241085Swilliam #ifdef	SMALL
14341085Swilliam 	dd = &disklabel;
14441085Swilliam #else
14541085Swilliam 	dd = &wdsizes[unit];
14641085Swilliam #endif
14741085Swilliam         if (func == WRITE)
14841085Swilliam                 opcode = WDCC_WRITE;
14941085Swilliam         else
15041085Swilliam                 opcode = WDCC_READ;
15141085Swilliam 
15241085Swilliam         /* Calculate data for output.           */
153*48827Swilliam         cylin = blknm / dd->d_secpercyl;
154*48827Swilliam         head = (blknm % dd->d_secpercyl) / dd->d_nsectors;
155*48827Swilliam         sector = blknm % dd->d_nsectors;
15641085Swilliam 
15741085Swilliam 	/*
15841085Swilliam 	 * See if the current block is in the bad block list.
15941085Swilliam 	 */
160*48827Swilliam 	if (blknm > BBSIZE/DEV_BSIZE)	/* should be BBSIZE */
16141085Swilliam 	    for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
16241085Swilliam 		if (bt_ptr->bt_cyl > cylin)
16341085Swilliam 			/* Sorted list, and we passed our cylinder. quit. */
16441085Swilliam 			break;
16541085Swilliam 		if (bt_ptr->bt_cyl == cylin &&
16641085Swilliam 			bt_ptr->bt_trksec == (head << 8) + sector) {
16741085Swilliam 			/*
16841085Swilliam 			 * Found bad block.  Calculate new block addr.
16941085Swilliam 			 * This starts at the end of the disk (skip the
17041085Swilliam 			 * last track which is used for the bad block list),
17141085Swilliam 			 * and works backwards to the front of the disk.
17241085Swilliam 			 */
17341085Swilliam #ifdef WDDEBUG
17441085Swilliam 			    printf("--- badblock code -> Old = %d; ",
17541085Swilliam 				blknm);
17641085Swilliam #endif
177*48827Swilliam 			blknm = dd->d_secperunit - dd->d_nsectors
17841085Swilliam 				- (bt_ptr - dkbad[unit].bt_bad) - 1;
179*48827Swilliam 			cylin = blknm / dd->d_secpercyl;
180*48827Swilliam 			head = (blknm % dd->d_secpercyl) / dd->d_nsectors;
181*48827Swilliam 			sector = blknm % dd->d_nsectors;
18241085Swilliam #ifdef WDDEBUG
18341085Swilliam 			    printf("new = %d\n", blknm);
18441085Swilliam #endif
18541085Swilliam 			break;
18641085Swilliam 		}
18741085Swilliam 	}
18841085Swilliam 
189*48827Swilliam         sector += 1;
190*48827Swilliam retry:
191*48827Swilliam #ifdef WDDEBUG
192*48827Swilliam 	printf("sec %d sdh %x cylin %d ", sector,
193*48827Swilliam 		WDSD_IBM | (unit<<4) | (head & 0xf), cylin);
19441085Swilliam #endif
19541085Swilliam 	outb(wdc+wd_precomp, 0xff);
19641085Swilliam 	outb(wdc+wd_seccnt, 1);
19741085Swilliam 	outb(wdc+wd_sector, sector);
19841085Swilliam 	outb(wdc+wd_cyl_lo, cylin);
19941085Swilliam 	outb(wdc+wd_cyl_hi, cylin >> 8);
20041085Swilliam 
20141085Swilliam 	/* Set up the SDH register (select drive).     */
20241085Swilliam 	outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
203*48827Swilliam 	while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
20441085Swilliam 
20541085Swilliam 	outb(wdc+wd_command, opcode);
206*48827Swilliam 	while (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_BUSY))
20741085Swilliam 		;
20841085Swilliam 	/* Did we get an error?         */
209*48827Swilliam 	if (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_ERR))
21041085Swilliam 		goto error;
21141085Swilliam 
21241085Swilliam 	/* Ready to remove data?        */
213*48827Swilliam 	while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
21441085Swilliam 
21541085Swilliam 	if (opcode == WDCC_READ)
21641085Swilliam 		insw(wdc+wd_data,addr,256);
21741085Swilliam 	else	outsw(wdc+wd_data,addr,256);
21841085Swilliam 
21941085Swilliam 	/* Check data request (should be done).         */
220*48827Swilliam 	if (inb(wdc+wd_status) & WDCS_DRQ) goto error;
22141085Swilliam 
222*48827Swilliam 	while (opcode == WDCC_WRITE && (inb(wdc+wd_status) & WDCS_BUSY)) ;
22341085Swilliam 
224*48827Swilliam 	if (inb(wdc+wd_status) & WDCS_ERR) goto error;
22541085Swilliam 
226*48827Swilliam #ifdef WDDEBUG
227*48827Swilliam printf("+");
228*48827Swilliam #endif
22941085Swilliam         return (0);
23041085Swilliam error:
231*48827Swilliam 	erro = inb(wdc+wd_error);
23241085Swilliam 	if (++retries < RETRIES)
23341085Swilliam 		goto retry;
23441085Swilliam 	if (!wdquiet)
235*48827Swilliam #ifdef	SMALL
236*48827Swilliam 	    printf("wd%d: hard error: sector %d status %x error %x\n", unit,
237*48827Swilliam 		blknm, inb(wdc+wd_status), erro);
238*48827Swilliam #else
239*48827Swilliam 	    printf("wd%d: hard %s error: sector %d status %b error %b\n", unit,
24041085Swilliam 		opcode == WDCC_READ? "read" : "write", blknm,
241*48827Swilliam 		inb(wdc+wd_status), WDCS_BITS, erro, WDERR_BITS);
242*48827Swilliam #endif
24341085Swilliam 	return (-1);
24441085Swilliam }
24541085Swilliam 
24641085Swilliam wdinit(io)
24741085Swilliam 	struct iob *io;
24841085Swilliam {
249*48827Swilliam 	register wdc;
25041085Swilliam 	struct disklabel *dd;
25141085Swilliam         unsigned int   unit;
25241085Swilliam 	struct dkbad *db;
25341085Swilliam 	int i, errcnt = 0;
25441085Swilliam 	char buf[512];
25541085Swilliam 	static open[NWD];
25641085Swilliam 
257*48827Swilliam 	unit = io->i_unit;
258*48827Swilliam 	if (open[unit]) return(0);
25941085Swilliam 
260*48827Swilliam 	wdcport = io->i_ctlr ? IO_WD2 : IO_WD1;
261*48827Swilliam 	wdc = wdcport;
262*48827Swilliam 
26341085Swilliam #ifdef	SMALL
26441085Swilliam 	dd = &disklabel;
265*48827Swilliam #else
266*48827Swilliam 	/* reset controller */
267*48827Swilliam 	outb(wdc+wd_ctlr, 12); wait(10); outb(wdc+wd_ctlr, 8);
26841085Swilliam 	wdwait();
269*48827Swilliam 
27041085Swilliam 	dd = &wdsizes[unit];
27141085Swilliam 
27241085Swilliam tryagainrecal:
273*48827Swilliam 	/* set SDH, step rate, do restore to recalibrate drive */
27441085Swilliam 	outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
27541085Swilliam 	wdwait();
27641085Swilliam 	outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
27741085Swilliam 	wdwait();
27841085Swilliam 	if ((i = inb(wdc+wd_status)) & WDCS_ERR) {
279*48827Swilliam /*#ifdef SMALL
280*48827Swilliam 		printf("wd%d: recal status %x error %x\n",
281*48827Swilliam 			unit, i, inb(wdc+wd_error));
282*48827Swilliam #else*/
28341085Swilliam 		printf("wd%d: recal status %b error %b\n",
28441085Swilliam 			unit, i, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
285*48827Swilliam /*#endif*/
28641085Swilliam 		if (++errcnt < 10)
28741085Swilliam 			goto tryagainrecal;
28841085Swilliam 		return(-1);
28941085Swilliam 	}
290*48827Swilliam 
291*48827Swilliam 	/*
292*48827Swilliam 	 * Some controllers require this (after a recal they
293*48827Swilliam 	 * revert to a logical translation mode to compensate for
294*48827Swilliam 	 * dos limitation on 10-bit cylinders -- *shudder* -wfj)
295*48827Swilliam 	 * note: cylinders *must* be fewer than or equal to 8 to
296*48827Swilliam 	 * compensate for some IDE drives that latch this for all time.
297*48827Swilliam 	 */
298*48827Swilliam 	outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + 8 -1);
299*48827Swilliam 	outb(wdc+wd_seccnt, 35 );
300*48827Swilliam 	outb(wdc+wd_cyl_lo, 1224);
301*48827Swilliam 	outb(wdc+wd_cyl_hi, 1224/256);
302*48827Swilliam 	outb(wdc+wd_command, 0x91);
303*48827Swilliam 	while (inb(wdc+wd_status) & WDCS_BUSY) ;
304*48827Swilliam 
30541085Swilliam 	errcnt = 0;
30641085Swilliam retry:
30741085Swilliam 	/*
308*48827Swilliam 	 * Read in LABELSECTOR to get the pack label and geometry.
30941085Swilliam 	 */
31041085Swilliam 	outb(wdc+wd_precomp, 0xff);	/* sometimes this is head bit 3 */
31141085Swilliam 	outb(wdc+wd_seccnt, 1);
312*48827Swilliam 	outb(wdc+wd_sector, LABELSECTOR + 1);
31341085Swilliam 	outb(wdc+wd_cyl_lo, (cyloffset & 0xff));
31441085Swilliam 	outb(wdc+wd_cyl_hi, (cyloffset >> 8));
31541085Swilliam 	outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
31641085Swilliam 	wdwait();
31741085Swilliam 	outb(wdc+wd_command, WDCC_READ);
31841085Swilliam 	wdwait();
31941085Swilliam 	if ((i = inb(wdc+wd_status)) & WDCS_ERR) {
320*48827Swilliam 		int err;
321*48827Swilliam 
322*48827Swilliam 		err = inb(wdc+wd_error);
32341085Swilliam 		if (++errcnt < RETRIES)
32441085Swilliam 			goto retry;
32541085Swilliam 		if (!wdquiet)
326*48827Swilliam /*#ifdef SMALL
327*48827Swilliam 		    printf("wd%d: reading label, status %x error %x\n",
328*48827Swilliam 			unit, i, err);
329*48827Swilliam #else*/
33041085Swilliam 		    printf("wd%d: reading label, status %b error %b\n",
331*48827Swilliam 			unit, i, WDCS_BITS, err, WDERR_BITS);
332*48827Swilliam /*#endif*/
33341085Swilliam 		return(-1);
33441085Swilliam 	}
33541085Swilliam 
33641085Swilliam 	/* Ready to remove data?        */
337*48827Swilliam 	while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
33841085Swilliam 
33941085Swilliam 	i = insw(wdc+wd_data, buf, 256);
34041085Swilliam 
341*48827Swilliam #ifdef WDDEBUG
342*48827Swilliam 	printf("magic %x,insw %x, %x\n",
343*48827Swilliam 	((struct disklabel *) (buf + LABELOFFSET))->d_magic, i, buf);
344*48827Swilliam #endif
345*48827Swilliam 	if (((struct disklabel *) (buf + LABELOFFSET))->d_magic == DISKMAGIC) {
34641085Swilliam 		*dd = * (struct disklabel *) (buf + LABELOFFSET);
34741085Swilliam 		open[unit] = 1;
34841085Swilliam 	} else {
34941085Swilliam 		if (!wdquiet)
35041085Swilliam 			printf("wd%d: bad disk label\n", unit);
35141085Swilliam 		if (io->i_flgs & F_FILE) return(-1);
35241085Swilliam 		dkbad[unit].bt_bad[0].bt_cyl = -1;
353*48827Swilliam 		dd->d_secpercyl = 1999999 ; dd->d_nsectors = 17 ;
354*48827Swilliam 		dd->d_secsize = 512;
35541085Swilliam 		outb(wdc+wd_precomp, 0xff);	/* force head 3 bit off */
35641085Swilliam 		return (0) ;
35741085Swilliam 	}
358*48827Swilliam #endif SMALL
359*48827Swilliam #ifdef SMALL
360*48827Swilliam #ifdef WDDEBUG
361*48827Swilliam 	printf("magic %x sect %d\n", dd->d_magic, dd->d_nsectors);
362*48827Swilliam #endif
36341085Swilliam #endif	SMALL
36441085Swilliam 
365*48827Swilliam 
366*48827Swilliam 	/* now that we know the disk geometry, tell the controller */
367*48827Swilliam 	outb(wdc+wd_cyl_lo, dd->d_ncylinders);
368*48827Swilliam 	outb(wdc+wd_cyl_hi, (dd->d_ncylinders)>>8);
369*48827Swilliam 	outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + dd->d_ntracks-1);
370*48827Swilliam 	outb(wdc+wd_seccnt, dd->d_nsectors);
371*48827Swilliam 	outb(wdc+wd_command, 0x91);
372*48827Swilliam 	while (inb(wdc+wd_status) & WDCS_BUSY) ;
373*48827Swilliam 
374*48827Swilliam 	dkbad[unit].bt_bad[0].bt_cyl = -1;
375*48827Swilliam 	outb(wdc+wd_precomp, dd->d_precompcyl / 4);
376*48827Swilliam 
37741085Swilliam 	/*
37841085Swilliam 	 * Read bad sector table into memory.
37941085Swilliam 	 */
38041085Swilliam 	i = 0;
38141085Swilliam 	do {
382*48827Swilliam 		int blknm = dd->d_secperunit - dd->d_nsectors + i;
38341085Swilliam 		errcnt = wdio(READ, unit, blknm, buf);
384*48827Swilliam 	} while (errcnt && (i += 2) < 10 && i < dd->d_nsectors);
38541085Swilliam 	db = (struct dkbad *)(buf);
386*48827Swilliam #define DKBAD_MAGIC 0x4321
38741085Swilliam 	if (errcnt == 0 && db->bt_mbz == 0 && db->bt_flag == DKBAD_MAGIC)
38841085Swilliam 		dkbad[unit] = *db;
38941085Swilliam 	else {
39041085Swilliam 		if (!wdquiet)
39141085Swilliam 			printf("wd%d: error in bad-sector file\n", unit);
39241085Swilliam 		dkbad[unit].bt_bad[0].bt_cyl = -1;
39341085Swilliam 	}
39441085Swilliam 	return(0);
39541085Swilliam }
39641085Swilliam 
39741085Swilliam wdwait()
39841085Swilliam {
399*48827Swilliam 	register wdc = wdcport;
40041085Swilliam 	register i = 0;
40141085Swilliam 
402*48827Swilliam 	while (inb(wdc+wd_status) & WDCS_BUSY)
40341085Swilliam 		;
404*48827Swilliam 	while ((inb(wdc+wd_status) & WDCS_READY) == 0)
40541085Swilliam 		if (i++ > 100000)
40641085Swilliam 			return(-1);
40741085Swilliam 	return(0);
40841085Swilliam }
409