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