Commit | Line | Data |
---|---|---|
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 |
51 | int noretries, wdquiet; |
52 | /*#define WDDEBUG*/ | |
edfe6365 WN |
53 | |
54 | #ifdef SMALL | |
55 | extern struct disklabel disklabel; | |
56 | #else | |
57 | struct disklabel wdsizes[NWD]; | |
edfe6365 | 58 | #endif |
af359dea C |
59 | |
60 | extern 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 | ||
69 | struct dkbad dkbad[NWD]; | |
af359dea | 70 | static wdcport; |
edfe6365 WN |
71 | |
72 | wdopen(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 | ||
95 | wdstrategy(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 | */ | |
157 | wdio(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 | 216 | retry: |
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 |
253 | printf("+"); | |
254 | #endif | |
edfe6365 WN |
255 | return (0); |
256 | error: | |
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 | ||
272 | wdinit(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 | |
298 | tryagainrecal: | |
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; |
332 | retry: | |
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 | ||
423 | wdwait() | |
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 | } |