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