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