xref: /csrg-svn/sys/i386/stand/wd.c (revision 41085)
1*41085Swilliam /*-
2*41085Swilliam  * Copyright (c) 1990 The Regents of the University of California.
3*41085Swilliam  * All rights reserved.
4*41085Swilliam  *
5*41085Swilliam  * This code is derived from software contributed to Berkeley by
6*41085Swilliam  * William Jolitz.
7*41085Swilliam  *
8*41085Swilliam  * %sccs.include.noredist.c%
9*41085Swilliam  *
10*41085Swilliam  *	@(#)wd.c	7.1 (Berkeley) 04/24/90
11*41085Swilliam  */
12*41085Swilliam 
13*41085Swilliam /*  device driver for winchester disk  */
14*41085Swilliam 
15*41085Swilliam #include "../h/param.h"
16*41085Swilliam #include "../h/inode.h"
17*41085Swilliam #include "../h/fs.h"
18*41085Swilliam #include "../h/dkbad.h"
19*41085Swilliam #include "../h/disk.h"
20*41085Swilliam #include "../isa/atio.h"
21*41085Swilliam #include "../isa/wdreg.h"
22*41085Swilliam #include "saio.h"
23*41085Swilliam 
24*41085Swilliam #define	NWD		2	/* number of hard disk units supported, max 2 */
25*41085Swilliam #define	RETRIES		5	/* number of retries before giving up */
26*41085Swilliam 
27*41085Swilliam int noretries = 0;
28*41085Swilliam int wdquiet = 0;
29*41085Swilliam #ifdef	WDDEBUG
30*41085Swilliam int wdinfoflag = 0;
31*41085Swilliam #endif
32*41085Swilliam 
33*41085Swilliam #ifdef	SMALL
34*41085Swilliam extern struct disklabel disklabel;
35*41085Swilliam #else
36*41085Swilliam struct disklabel wdsizes[NWD];
37*41085Swilliam extern struct disklabel *dlp;
38*41085Swilliam #endif
39*41085Swilliam int cyloffset;
40*41085Swilliam 
41*41085Swilliam /*
42*41085Swilliam  * Record for the bad block forwarding code.
43*41085Swilliam  * This is initialized to be empty until the bad-sector table
44*41085Swilliam  * is read from the disk.
45*41085Swilliam  */
46*41085Swilliam #define TRKSEC(trk,sec)	((trk << 8) + sec)
47*41085Swilliam 
48*41085Swilliam struct	dkbad	dkbad[NWD];
49*41085Swilliam 
50*41085Swilliam wdopen(io)
51*41085Swilliam 	register struct iob *io;
52*41085Swilliam {
53*41085Swilliam         register struct disklabel *dd;
54*41085Swilliam 	int unit, partition;
55*41085Swilliam 
56*41085Swilliam 	unit = minor_unit(minor(io->i_ino.i_dev));
57*41085Swilliam 	partition = minor_partition(minor(io->i_ino.i_dev));
58*41085Swilliam #ifdef SMALL
59*41085Swilliam         dd = &disklabel;
60*41085Swilliam #else
61*41085Swilliam         dd = &wdsizes[unit];
62*41085Swilliam 	if (partition >= 8)
63*41085Swilliam                 _stop("Invalid partition number");
64*41085Swilliam #endif
65*41085Swilliam         if (wdinit(io))
66*41085Swilliam                 _stop("wd initialization error");
67*41085Swilliam }
68*41085Swilliam 
69*41085Swilliam wdstrategy(io,func)
70*41085Swilliam 	register struct iob *io;
71*41085Swilliam {
72*41085Swilliam 	register int iosize;    /* number of sectors to do IO for this loop */
73*41085Swilliam 	register daddr_t sector;
74*41085Swilliam 	int nblocks, cyloff;
75*41085Swilliam 	int unit, partition;
76*41085Swilliam 	char *address;
77*41085Swilliam 	register struct disklabel *dd;
78*41085Swilliam 
79*41085Swilliam 	unit = minor_unit(minor(io->i_ino.i_dev));
80*41085Swilliam 	partition = minor_partition(minor(io->i_ino.i_dev));
81*41085Swilliam 	if ((unsigned)unit >= NWD) {
82*41085Swilliam 		printf("wd: unit %d\n", unit);
83*41085Swilliam 		return(-1);
84*41085Swilliam 	}
85*41085Swilliam #ifdef	SMALL
86*41085Swilliam 	dd = &disklabel;
87*41085Swilliam #else
88*41085Swilliam 	dd = &wdsizes[unit];
89*41085Swilliam #endif
90*41085Swilliam         iosize = io->i_cc / dd->dk_secsize;
91*41085Swilliam 	/*
92*41085Swilliam 	 * Convert PGSIZE "blocks" to sectors.
93*41085Swilliam 	 * Note: doing the conversions this way limits the partition size
94*41085Swilliam 	 * to about 8 million sectors (1-8 Gb).
95*41085Swilliam 	 */
96*41085Swilliam 	sector = (unsigned long) io->i_bn * DEV_BSIZE / dd->dk_secsize;
97*41085Swilliam 	nblocks = dd->dk_partition[partition].nblocks;
98*41085Swilliam 	cyloff = dd->dk_partition[partition].cyloff;
99*41085Swilliam         if (iosize < 0 || sector + iosize > nblocks || sector < 0) {
100*41085Swilliam #ifdef WDDEBUG
101*41085Swilliam 		printf("bn = %d; sectors = %d; partition = %d; fssize = %d\n",
102*41085Swilliam 			io->i_bn, iosize, partition, nblocks);
103*41085Swilliam #endif
104*41085Swilliam                 printf("wdstrategy - I/O out of filesystem boundaries\n");
105*41085Swilliam 		return(-1);
106*41085Swilliam 	}
107*41085Swilliam 	if (io->i_bn * DEV_BSIZE % dd->dk_secsize) {
108*41085Swilliam 		printf("wdstrategy - transfer starts in midsector\n");
109*41085Swilliam 		return(-1);
110*41085Swilliam 	}
111*41085Swilliam         if (io->i_cc % dd->dk_secsize) {
112*41085Swilliam 		printf("wd: transfer of partial sector\n");
113*41085Swilliam 		return(-1);
114*41085Swilliam 	}
115*41085Swilliam 
116*41085Swilliam 	address = io->i_ma;
117*41085Swilliam 	sector += cyloff * dd->dk_secpercyl;
118*41085Swilliam         while (iosize > 0) {
119*41085Swilliam                 if (wdio(func, unit, sector, address))
120*41085Swilliam                         return(-1);
121*41085Swilliam 		iosize--;
122*41085Swilliam 		sector++;
123*41085Swilliam                 address += dd->dk_secsize;
124*41085Swilliam         }
125*41085Swilliam         return(io->i_cc);
126*41085Swilliam }
127*41085Swilliam 
128*41085Swilliam /*
129*41085Swilliam  * Routine to do a one-sector I/O operation, and wait for it
130*41085Swilliam  * to complete.
131*41085Swilliam  */
132*41085Swilliam wdio(func, unit, blknm, addr)
133*41085Swilliam         short *addr;
134*41085Swilliam {
135*41085Swilliam 	struct disklabel *dd;
136*41085Swilliam 	int wdc = IO_WD0;
137*41085Swilliam 	struct bt_bad *bt_ptr;
138*41085Swilliam         int    i;
139*41085Swilliam 	int retries = 0;
140*41085Swilliam         long    cylin, head, sector;
141*41085Swilliam         u_char opcode;
142*41085Swilliam 
143*41085Swilliam #ifdef	SMALL
144*41085Swilliam 	dd = &disklabel;
145*41085Swilliam #else
146*41085Swilliam 	dd = &wdsizes[unit];
147*41085Swilliam #endif
148*41085Swilliam         if (func == WRITE)
149*41085Swilliam                 opcode = WDCC_WRITE;
150*41085Swilliam         else
151*41085Swilliam                 opcode = WDCC_READ;
152*41085Swilliam 
153*41085Swilliam         /* Calculate data for output.           */
154*41085Swilliam         cylin = blknm / dd->dk_secpercyl;
155*41085Swilliam         head = (blknm % dd->dk_secpercyl) / dd->dk_nsectors;
156*41085Swilliam         sector = blknm % dd->dk_nsectors + 1;
157*41085Swilliam 
158*41085Swilliam #ifdef notyet
159*41085Swilliam 	/*
160*41085Swilliam 	 * See if the current block is in the bad block list.
161*41085Swilliam 	 */
162*41085Swilliam 	if (blknm > 7)	/* should be BBSIZE */
163*41085Swilliam 	    for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
164*41085Swilliam 		if (bt_ptr->bt_cyl > cylin)
165*41085Swilliam 			/* Sorted list, and we passed our cylinder. quit. */
166*41085Swilliam 			break;
167*41085Swilliam 		if (bt_ptr->bt_cyl == cylin &&
168*41085Swilliam 			bt_ptr->bt_trksec == (head << 8) + sector) {
169*41085Swilliam 			/*
170*41085Swilliam 			 * Found bad block.  Calculate new block addr.
171*41085Swilliam 			 * This starts at the end of the disk (skip the
172*41085Swilliam 			 * last track which is used for the bad block list),
173*41085Swilliam 			 * and works backwards to the front of the disk.
174*41085Swilliam 			 */
175*41085Swilliam #ifdef WDDEBUG
176*41085Swilliam 			if (wdinfoflag)
177*41085Swilliam 			    printf("--- badblock code -> Old = %d; ",
178*41085Swilliam 				blknm);
179*41085Swilliam #endif
180*41085Swilliam 			blknm = dd->dk_secperunit - dd->dk_nsectors
181*41085Swilliam 				- (bt_ptr - dkbad[unit].bt_bad) - 1;
182*41085Swilliam 			cylin = blknm / dd->dk_secpercyl;
183*41085Swilliam 			head = (blknm % dd->dk_secpercyl) / dd->dk_nsectors;
184*41085Swilliam 			sector = blknm % dd->dk_nsectors;
185*41085Swilliam #ifdef WDDEBUG
186*41085Swilliam 			if (wdinfoflag)
187*41085Swilliam 			    printf("new = %d\n", blknm);
188*41085Swilliam #endif
189*41085Swilliam 			break;
190*41085Swilliam 		}
191*41085Swilliam 	}
192*41085Swilliam 
193*41085Swilliam #endif
194*41085Swilliam retry:
195*41085Swilliam printf("sec %d sdh %x cylin %d\n", sector
196*41085Swilliam 	, WDSD_IBM | (unit<<4) | (head & 0xf), cylin);
197*41085Swilliam 	outb(wdc+wd_precomp, 0xff);
198*41085Swilliam 	outb(wdc+wd_seccnt, 1);
199*41085Swilliam 	outb(wdc+wd_sector, sector);
200*41085Swilliam 	outb(wdc+wd_cyl_lo, cylin);
201*41085Swilliam 	outb(wdc+wd_cyl_hi, cylin >> 8);
202*41085Swilliam 
203*41085Swilliam 	/* Set up the SDH register (select drive).     */
204*41085Swilliam 	outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
205*41085Swilliam 	while ((inb(wdc+wd_altsts) & WDCS_READY) == 0) ;
206*41085Swilliam 
207*41085Swilliam 	outb(wdc+wd_command, opcode);
208*41085Swilliam 	while (opcode == WDCC_READ && (inb(wdc+wd_altsts) & WDCS_BUSY))
209*41085Swilliam 		;
210*41085Swilliam 	/* Did we get an error?         */
211*41085Swilliam 	if (opcode == WDCC_READ && (inb(wdc+wd_altsts) & WDCS_ERR))
212*41085Swilliam 		goto error;
213*41085Swilliam 
214*41085Swilliam 	/* Ready to remove data?        */
215*41085Swilliam 	while ((inb(wdc+wd_altsts) & WDCS_DRQ) == 0) ;
216*41085Swilliam 
217*41085Swilliam 	if (opcode == WDCC_READ)
218*41085Swilliam 		insw(wdc+wd_data,addr,256);
219*41085Swilliam 	else	outsw(wdc+wd_data,addr,256);
220*41085Swilliam 
221*41085Swilliam 	/* Check data request (should be done).         */
222*41085Swilliam 	if (inb(wdc+wd_altsts) & WDCS_DRQ) goto error;
223*41085Swilliam 
224*41085Swilliam 	while (opcode == WDCC_WRITE && (inb(wdc+wd_altsts) & WDCS_BUSY)) ;
225*41085Swilliam 
226*41085Swilliam 	if (inb(wdc+wd_altsts) & WDCS_ERR) goto error;
227*41085Swilliam 
228*41085Swilliam /*for (i=0; i < 256 ; i++){
229*41085Swilliam 	if((i%20) == 0) printf("\n");
230*41085Swilliam 	printf("%x ", addr[i]);
231*41085Swilliam }
232*41085Swilliam i=getchar();*/
233*41085Swilliam 
234*41085Swilliam         return (0);
235*41085Swilliam error:
236*41085Swilliam 	wddumpregs(wdc);
237*41085Swilliam 	if (++retries < RETRIES)
238*41085Swilliam 		goto retry;
239*41085Swilliam 	if (!wdquiet)
240*41085Swilliam 	    printf("wd%d: hard %s error: sector %d, status %b error %b\n", unit,
241*41085Swilliam 		opcode == WDCC_READ? "read" : "write", blknm,
242*41085Swilliam 		inb(wdc+wd_status), WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
243*41085Swilliam 	return (-1);
244*41085Swilliam }
245*41085Swilliam 
246*41085Swilliam wdinit(io)
247*41085Swilliam 	struct iob *io;
248*41085Swilliam {
249*41085Swilliam 	int wdc = IO_WD0;
250*41085Swilliam 	struct disklabel *dd;
251*41085Swilliam         unsigned int   unit;
252*41085Swilliam 	struct dkbad *db;
253*41085Swilliam 	int i, errcnt = 0;
254*41085Swilliam 	char buf[512];
255*41085Swilliam 	static open[NWD];
256*41085Swilliam 
257*41085Swilliam 	/* reset controller */
258*41085Swilliam 	outb(wdc+wd_ctlr, 4); wait(10); outb(wdc+wd_ctlr, 0);
259*41085Swilliam 	wdwait();
260*41085Swilliam 
261*41085Swilliam 	unit = minor_unit(minor(io->i_ino.i_dev));
262*41085Swilliam #ifdef	SMALL
263*41085Swilliam 	dd = &disklabel;
264*41085Swilliam 	outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
265*41085Swilliam 	wdwait();
266*41085Swilliam #else
267*41085Swilliam 	dd = &wdsizes[unit];
268*41085Swilliam 	if (open[unit]) return(0);
269*41085Swilliam /*
270*41085Swilliam code to tell disk controller geometry of disk
271*41085Swilliam not currently used
272*41085Swilliam 
273*41085Swilliam 	outb(wdc+wd_sdh, 0xa7);
274*41085Swilliam wdwait();
275*41085Swilliam 	outb(wdc+wd_seccnt, 17);
276*41085Swilliam 	outb(wdc+wd_cyl_lo, 0);
277*41085Swilliam 	outb(wdc+wd_command, 0x91);
278*41085Swilliam 	wdwait();*/
279*41085Swilliam 
280*41085Swilliam tryagainrecal:
281*41085Swilliam 	/* set SDH, step rate, do restore */
282*41085Swilliam 	outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
283*41085Swilliam 	wdwait();
284*41085Swilliam 	outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
285*41085Swilliam 	wdwait();
286*41085Swilliam 	if ((i = inb(wdc+wd_status)) & WDCS_ERR) {
287*41085Swilliam 		printf("wd%d: recal status %b error %b\n",
288*41085Swilliam 			unit, i, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
289*41085Swilliam 		wddumpregs(wdc);
290*41085Swilliam 		if (++errcnt < 10)
291*41085Swilliam 			goto tryagainrecal;
292*41085Swilliam 		return(-1);
293*41085Swilliam 	}
294*41085Swilliam #endif	SMALL
295*41085Swilliam #ifndef	SMALL
296*41085Swilliam 	errcnt = 0;
297*41085Swilliam retry:
298*41085Swilliam cyloffset = 290;
299*41085Swilliam 	/*
300*41085Swilliam 	 * Read in sector 0 to get the pack label and geometry.
301*41085Swilliam 	 */
302*41085Swilliam 	outb(wdc+wd_precomp, 0xff);	/* sometimes this is head bit 3 */
303*41085Swilliam 	outb(wdc+wd_seccnt, 1);
304*41085Swilliam 	outb(wdc+wd_sector, 1);
305*41085Swilliam 	outb(wdc+wd_cyl_lo, (cyloffset & 0xff));
306*41085Swilliam 	outb(wdc+wd_cyl_hi, (cyloffset >> 8));
307*41085Swilliam 	outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
308*41085Swilliam 	wdwait();
309*41085Swilliam 	outb(wdc+wd_command, WDCC_READ);
310*41085Swilliam 	wdwait();
311*41085Swilliam 	if ((i = inb(wdc+wd_status)) & WDCS_ERR) {
312*41085Swilliam 		wddumpregs(wdc);
313*41085Swilliam 		if (++errcnt < RETRIES)
314*41085Swilliam 			goto retry;
315*41085Swilliam 		if (!wdquiet)
316*41085Swilliam 		    printf("wd%d: reading label, status %b error %b\n",
317*41085Swilliam 			unit, i, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
318*41085Swilliam 		return(-1);
319*41085Swilliam 	}
320*41085Swilliam 
321*41085Swilliam 	/* Ready to remove data?        */
322*41085Swilliam 	while ((inb(wdc+wd_altsts) & WDCS_DRQ) == 0) ;
323*41085Swilliam 
324*41085Swilliam 	i = insw(wdc+wd_data, buf, 256);
325*41085Swilliam 
326*41085Swilliam 	/*printf("magic %x,insw %x, %x\n",
327*41085Swilliam 	((struct disklabel *) (buf + LABELOFFSET))->dk_magic, i, buf);*/
328*41085Swilliam 	if (((struct disklabel *) (buf + LABELOFFSET))->dk_magic == DISKMAGIC) {
329*41085Swilliam 		*dd = * (struct disklabel *) (buf + LABELOFFSET);
330*41085Swilliam 		open[unit] = 1;
331*41085Swilliam 	} else {
332*41085Swilliam 		if (!wdquiet)
333*41085Swilliam 			printf("wd%d: bad disk label\n", unit);
334*41085Swilliam 		if (io->i_flgs & F_FILE) return(-1);
335*41085Swilliam 		dkbad[unit].bt_bad[0].bt_cyl = -1;
336*41085Swilliam 		dd->dk_secpercyl = 1999999 ; dd->dk_nsectors = 17 ;
337*41085Swilliam 		dd->dk_secsize = 512;
338*41085Swilliam 		outb(wdc+wd_precomp, 0xff);	/* force head 3 bit off */
339*41085Swilliam /**dd = *dlp;
340*41085Swilliam open[unit] = 1;*/
341*41085Swilliam 		return (0) ;
342*41085Swilliam 	}
343*41085Swilliam 	dkbad[unit].bt_bad[0].bt_cyl = -1;
344*41085Swilliam 	outb(wdc+wd_precomp, dd->dk_precompcyl / 4);
345*41085Swilliam #endif	SMALL
346*41085Swilliam 
347*41085Swilliam #ifdef notyet
348*41085Swilliam 	/*
349*41085Swilliam 	 * Read bad sector table into memory.
350*41085Swilliam 	 */
351*41085Swilliam 	i = 0;
352*41085Swilliam 	do {
353*41085Swilliam 		int blknm = dd->dk_secperunit - dd->dk_nsectors + i;
354*41085Swilliam 		errcnt = wdio(READ, unit, blknm, buf);
355*41085Swilliam 	} while (errcnt && (i += 2) < 10 && i < dd->dk_nsectors);
356*41085Swilliam 	db = (struct dkbad *)(buf);
357*41085Swilliam 	if (errcnt == 0 && db->bt_mbz == 0 && db->bt_flag == DKBAD_MAGIC)
358*41085Swilliam 		dkbad[unit] = *db;
359*41085Swilliam 	else {
360*41085Swilliam 		if (!wdquiet)
361*41085Swilliam 			printf("wd%d: error in bad-sector file\n", unit);
362*41085Swilliam 		dkbad[unit].bt_bad[0].bt_cyl = -1;
363*41085Swilliam 	}
364*41085Swilliam #endif
365*41085Swilliam 	return(0);
366*41085Swilliam }
367*41085Swilliam 
368*41085Swilliam wdwait()
369*41085Swilliam {
370*41085Swilliam 	register wdc = IO_WD0;
371*41085Swilliam 	register i = 0;
372*41085Swilliam 
373*41085Swilliam 	while (inb(wdc+wd_altsts) & WDCS_BUSY)
374*41085Swilliam 		;
375*41085Swilliam 	while ((inb(wdc+wd_altsts) & WDCS_READY) == 0)
376*41085Swilliam 		if (i++ > 100000)
377*41085Swilliam 			return(-1);
378*41085Swilliam 	return(0);
379*41085Swilliam }
380*41085Swilliam 
381*41085Swilliam 
382*41085Swilliam wddumpregs(wdc){
383*41085Swilliam 
384*41085Swilliam printf("err %x ", inb(wdc+wd_error));
385*41085Swilliam printf("seccnt %d ", inb(wdc+wd_seccnt));
386*41085Swilliam printf("sector %d ", inb(wdc+wd_sector));
387*41085Swilliam printf("cyl %d:", inb(wdc+wd_cyl_lo));
388*41085Swilliam printf("%d ", inb(wdc+wd_cyl_hi));
389*41085Swilliam printf("sdh %x ", inb(wdc+wd_sdh));
390*41085Swilliam printf("sts %x ", inb(wdc+wd_status));
391*41085Swilliam printf("alt %x ", inb(wdc+wd_altsts));
392*41085Swilliam printf("dig %x\n", inb(wdc+wd_digin));
393*41085Swilliam 
394*41085Swilliam }
395