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