Commit | Line | Data |
---|---|---|
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 | ||
53 | int noretries, wdquiet; | |
54 | /* #define WDDEBUG*/ | |
55 | ||
56 | #ifdef SMALL | |
57 | extern struct disklabel disklabel; | |
58 | #else | |
59 | struct disklabel wdsizes[NWD]; | |
60 | #endif | |
61 | ||
62 | extern 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 | ||
71 | struct dkbad dkbad[NWD]; | |
72 | static wdcport; | |
73 | ||
74 | wdopen(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 | ||
98 | wdstrategy(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 | */ | |
162 | wdio(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; | |
223 | retry: | |
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 | |
261 | printf("addr %x",addr); | |
262 | #endif | |
263 | return (0); | |
264 | error: | |
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 | ||
275 | wdinit(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 */ | |
307 | tryagainrecal: | |
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; | |
336 | retry: | |
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 | ||
422 | wdwait() | |
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 | } |