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