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