141085Swilliam /*- 241085Swilliam * Copyright (c) 1990 The Regents of the University of California. 341085Swilliam * All rights reserved. 441085Swilliam * 541085Swilliam * This code is derived from software contributed to Berkeley by 641085Swilliam * William Jolitz. 741085Swilliam * 8*48827Swilliam * %sccs.include.redist.c% 941085Swilliam * 10*48827Swilliam * @(#)wd.c 7.2 (Berkeley) 04/28/91 1141085Swilliam */ 1241085Swilliam 1341085Swilliam /* device driver for winchester disk */ 1441085Swilliam 15*48827Swilliam #include "param.h" 16*48827Swilliam #include "dkbad.h" 17*48827Swilliam #include "disklabel.h" 18*48827Swilliam #include "i386/isa/isa.h" 19*48827Swilliam #include "i386/isa/wdreg.h" 2041085Swilliam #include "saio.h" 2141085Swilliam 2241085Swilliam #define NWD 2 /* number of hard disk units supported, max 2 */ 2341085Swilliam #define RETRIES 5 /* number of retries before giving up */ 2441085Swilliam 25*48827Swilliam int noretries, wdquiet; 26*48827Swilliam /*#define WDDEBUG*/ 2741085Swilliam 2841085Swilliam #ifdef SMALL 2941085Swilliam extern struct disklabel disklabel; 3041085Swilliam #else 3141085Swilliam struct disklabel wdsizes[NWD]; 3241085Swilliam #endif 3341085Swilliam 34*48827Swilliam extern cyloffset ; /* bootstrap's idea of cylinder for disklabel */ 35*48827Swilliam 3641085Swilliam /* 3741085Swilliam * Record for the bad block forwarding code. 3841085Swilliam * This is initialized to be empty until the bad-sector table 3941085Swilliam * is read from the disk. 4041085Swilliam */ 4141085Swilliam #define TRKSEC(trk,sec) ((trk << 8) + sec) 4241085Swilliam 4341085Swilliam struct dkbad dkbad[NWD]; 44*48827Swilliam static wdcport; 4541085Swilliam 4641085Swilliam wdopen(io) 4741085Swilliam register struct iob *io; 4841085Swilliam { 4941085Swilliam register struct disklabel *dd; 5041085Swilliam 51*48827Swilliam #ifdef WDDEBUG 52*48827Swilliam printf("wdopen "); 53*48827Swilliam #endif 5441085Swilliam #ifdef SMALL 5541085Swilliam dd = &disklabel; 5641085Swilliam #else 57*48827Swilliam dd = &wdsizes[io->i_unit]; 58*48827Swilliam if (io->i_part > 8) 5941085Swilliam _stop("Invalid partition number"); 60*48827Swilliam if(io->i_ctlr > 1) 61*48827Swilliam _stop("Invalid controller number"); 6241085Swilliam #endif 6341085Swilliam if (wdinit(io)) 6441085Swilliam _stop("wd initialization error"); 65*48827Swilliam io->i_boff = dd->d_partitions[io->i_part].p_offset ; 66*48827Swilliam return(0); 6741085Swilliam } 6841085Swilliam 6941085Swilliam wdstrategy(io,func) 7041085Swilliam register struct iob *io; 7141085Swilliam { 7241085Swilliam register int iosize; /* number of sectors to do IO for this loop */ 7341085Swilliam register daddr_t sector; 7441085Swilliam int nblocks, cyloff; 7541085Swilliam int unit, partition; 7641085Swilliam char *address; 7741085Swilliam register struct disklabel *dd; 7841085Swilliam 79*48827Swilliam unit = io->i_unit; 80*48827Swilliam partition = io->i_part; 81*48827Swilliam #ifdef WDDEBUG 82*48827Swilliam printf("wdstrat %d %d ", unit, partition); 83*48827Swilliam #endif 8441085Swilliam #ifdef SMALL 8541085Swilliam dd = &disklabel; 8641085Swilliam #else 8741085Swilliam dd = &wdsizes[unit]; 8841085Swilliam #endif 89*48827Swilliam iosize = io->i_cc / dd->d_secsize; 9041085Swilliam /* 9141085Swilliam * Convert PGSIZE "blocks" to sectors. 9241085Swilliam * Note: doing the conversions this way limits the partition size 9341085Swilliam * to about 8 million sectors (1-8 Gb). 9441085Swilliam */ 95*48827Swilliam sector = (unsigned long) io->i_bn * DEV_BSIZE / dd->d_secsize; 96*48827Swilliam nblocks = dd->d_partitions[partition].p_size; 97*48827Swilliam #ifndef SMALL 9841085Swilliam if (iosize < 0 || sector + iosize > nblocks || sector < 0) { 9941085Swilliam #ifdef WDDEBUG 10041085Swilliam printf("bn = %d; sectors = %d; partition = %d; fssize = %d\n", 10141085Swilliam io->i_bn, iosize, partition, nblocks); 10241085Swilliam #endif 10341085Swilliam printf("wdstrategy - I/O out of filesystem boundaries\n"); 10441085Swilliam return(-1); 10541085Swilliam } 106*48827Swilliam if (io->i_bn * DEV_BSIZE % dd->d_secsize) { 10741085Swilliam printf("wdstrategy - transfer starts in midsector\n"); 10841085Swilliam return(-1); 10941085Swilliam } 110*48827Swilliam if (io->i_cc % dd->d_secsize) { 11141085Swilliam printf("wd: transfer of partial sector\n"); 11241085Swilliam return(-1); 11341085Swilliam } 114*48827Swilliam #endif 11541085Swilliam 11641085Swilliam address = io->i_ma; 11741085Swilliam while (iosize > 0) { 11841085Swilliam if (wdio(func, unit, sector, address)) 11941085Swilliam return(-1); 12041085Swilliam iosize--; 12141085Swilliam sector++; 122*48827Swilliam address += dd->d_secsize; 12341085Swilliam } 12441085Swilliam return(io->i_cc); 12541085Swilliam } 12641085Swilliam 12741085Swilliam /* 12841085Swilliam * Routine to do a one-sector I/O operation, and wait for it 12941085Swilliam * to complete. 13041085Swilliam */ 13141085Swilliam wdio(func, unit, blknm, addr) 13241085Swilliam short *addr; 13341085Swilliam { 13441085Swilliam struct disklabel *dd; 135*48827Swilliam register wdc = wdcport; 13641085Swilliam struct bt_bad *bt_ptr; 13741085Swilliam int i; 13841085Swilliam int retries = 0; 13941085Swilliam long cylin, head, sector; 140*48827Swilliam u_char opcode, erro; 14141085Swilliam 14241085Swilliam #ifdef SMALL 14341085Swilliam dd = &disklabel; 14441085Swilliam #else 14541085Swilliam dd = &wdsizes[unit]; 14641085Swilliam #endif 14741085Swilliam if (func == WRITE) 14841085Swilliam opcode = WDCC_WRITE; 14941085Swilliam else 15041085Swilliam opcode = WDCC_READ; 15141085Swilliam 15241085Swilliam /* Calculate data for output. */ 153*48827Swilliam cylin = blknm / dd->d_secpercyl; 154*48827Swilliam head = (blknm % dd->d_secpercyl) / dd->d_nsectors; 155*48827Swilliam sector = blknm % dd->d_nsectors; 15641085Swilliam 15741085Swilliam /* 15841085Swilliam * See if the current block is in the bad block list. 15941085Swilliam */ 160*48827Swilliam if (blknm > BBSIZE/DEV_BSIZE) /* should be BBSIZE */ 16141085Swilliam for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) { 16241085Swilliam if (bt_ptr->bt_cyl > cylin) 16341085Swilliam /* Sorted list, and we passed our cylinder. quit. */ 16441085Swilliam break; 16541085Swilliam if (bt_ptr->bt_cyl == cylin && 16641085Swilliam bt_ptr->bt_trksec == (head << 8) + sector) { 16741085Swilliam /* 16841085Swilliam * Found bad block. Calculate new block addr. 16941085Swilliam * This starts at the end of the disk (skip the 17041085Swilliam * last track which is used for the bad block list), 17141085Swilliam * and works backwards to the front of the disk. 17241085Swilliam */ 17341085Swilliam #ifdef WDDEBUG 17441085Swilliam printf("--- badblock code -> Old = %d; ", 17541085Swilliam blknm); 17641085Swilliam #endif 177*48827Swilliam blknm = dd->d_secperunit - dd->d_nsectors 17841085Swilliam - (bt_ptr - dkbad[unit].bt_bad) - 1; 179*48827Swilliam cylin = blknm / dd->d_secpercyl; 180*48827Swilliam head = (blknm % dd->d_secpercyl) / dd->d_nsectors; 181*48827Swilliam sector = blknm % dd->d_nsectors; 18241085Swilliam #ifdef WDDEBUG 18341085Swilliam printf("new = %d\n", blknm); 18441085Swilliam #endif 18541085Swilliam break; 18641085Swilliam } 18741085Swilliam } 18841085Swilliam 189*48827Swilliam sector += 1; 190*48827Swilliam retry: 191*48827Swilliam #ifdef WDDEBUG 192*48827Swilliam printf("sec %d sdh %x cylin %d ", sector, 193*48827Swilliam WDSD_IBM | (unit<<4) | (head & 0xf), cylin); 19441085Swilliam #endif 19541085Swilliam outb(wdc+wd_precomp, 0xff); 19641085Swilliam outb(wdc+wd_seccnt, 1); 19741085Swilliam outb(wdc+wd_sector, sector); 19841085Swilliam outb(wdc+wd_cyl_lo, cylin); 19941085Swilliam outb(wdc+wd_cyl_hi, cylin >> 8); 20041085Swilliam 20141085Swilliam /* Set up the SDH register (select drive). */ 20241085Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); 203*48827Swilliam while ((inb(wdc+wd_status) & WDCS_READY) == 0) ; 20441085Swilliam 20541085Swilliam outb(wdc+wd_command, opcode); 206*48827Swilliam while (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_BUSY)) 20741085Swilliam ; 20841085Swilliam /* Did we get an error? */ 209*48827Swilliam if (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_ERR)) 21041085Swilliam goto error; 21141085Swilliam 21241085Swilliam /* Ready to remove data? */ 213*48827Swilliam while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; 21441085Swilliam 21541085Swilliam if (opcode == WDCC_READ) 21641085Swilliam insw(wdc+wd_data,addr,256); 21741085Swilliam else outsw(wdc+wd_data,addr,256); 21841085Swilliam 21941085Swilliam /* Check data request (should be done). */ 220*48827Swilliam if (inb(wdc+wd_status) & WDCS_DRQ) goto error; 22141085Swilliam 222*48827Swilliam while (opcode == WDCC_WRITE && (inb(wdc+wd_status) & WDCS_BUSY)) ; 22341085Swilliam 224*48827Swilliam if (inb(wdc+wd_status) & WDCS_ERR) goto error; 22541085Swilliam 226*48827Swilliam #ifdef WDDEBUG 227*48827Swilliam printf("+"); 228*48827Swilliam #endif 22941085Swilliam return (0); 23041085Swilliam error: 231*48827Swilliam erro = inb(wdc+wd_error); 23241085Swilliam if (++retries < RETRIES) 23341085Swilliam goto retry; 23441085Swilliam if (!wdquiet) 235*48827Swilliam #ifdef SMALL 236*48827Swilliam printf("wd%d: hard error: sector %d status %x error %x\n", unit, 237*48827Swilliam blknm, inb(wdc+wd_status), erro); 238*48827Swilliam #else 239*48827Swilliam printf("wd%d: hard %s error: sector %d status %b error %b\n", unit, 24041085Swilliam opcode == WDCC_READ? "read" : "write", blknm, 241*48827Swilliam inb(wdc+wd_status), WDCS_BITS, erro, WDERR_BITS); 242*48827Swilliam #endif 24341085Swilliam return (-1); 24441085Swilliam } 24541085Swilliam 24641085Swilliam wdinit(io) 24741085Swilliam struct iob *io; 24841085Swilliam { 249*48827Swilliam register wdc; 25041085Swilliam struct disklabel *dd; 25141085Swilliam unsigned int unit; 25241085Swilliam struct dkbad *db; 25341085Swilliam int i, errcnt = 0; 25441085Swilliam char buf[512]; 25541085Swilliam static open[NWD]; 25641085Swilliam 257*48827Swilliam unit = io->i_unit; 258*48827Swilliam if (open[unit]) return(0); 25941085Swilliam 260*48827Swilliam wdcport = io->i_ctlr ? IO_WD2 : IO_WD1; 261*48827Swilliam wdc = wdcport; 262*48827Swilliam 26341085Swilliam #ifdef SMALL 26441085Swilliam dd = &disklabel; 265*48827Swilliam #else 266*48827Swilliam /* reset controller */ 267*48827Swilliam outb(wdc+wd_ctlr, 12); wait(10); outb(wdc+wd_ctlr, 8); 26841085Swilliam wdwait(); 269*48827Swilliam 27041085Swilliam dd = &wdsizes[unit]; 27141085Swilliam 27241085Swilliam tryagainrecal: 273*48827Swilliam /* set SDH, step rate, do restore to recalibrate drive */ 27441085Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 27541085Swilliam wdwait(); 27641085Swilliam outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); 27741085Swilliam wdwait(); 27841085Swilliam if ((i = inb(wdc+wd_status)) & WDCS_ERR) { 279*48827Swilliam /*#ifdef SMALL 280*48827Swilliam printf("wd%d: recal status %x error %x\n", 281*48827Swilliam unit, i, inb(wdc+wd_error)); 282*48827Swilliam #else*/ 28341085Swilliam printf("wd%d: recal status %b error %b\n", 28441085Swilliam unit, i, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS); 285*48827Swilliam /*#endif*/ 28641085Swilliam if (++errcnt < 10) 28741085Swilliam goto tryagainrecal; 28841085Swilliam return(-1); 28941085Swilliam } 290*48827Swilliam 291*48827Swilliam /* 292*48827Swilliam * Some controllers require this (after a recal they 293*48827Swilliam * revert to a logical translation mode to compensate for 294*48827Swilliam * dos limitation on 10-bit cylinders -- *shudder* -wfj) 295*48827Swilliam * note: cylinders *must* be fewer than or equal to 8 to 296*48827Swilliam * compensate for some IDE drives that latch this for all time. 297*48827Swilliam */ 298*48827Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + 8 -1); 299*48827Swilliam outb(wdc+wd_seccnt, 35 ); 300*48827Swilliam outb(wdc+wd_cyl_lo, 1224); 301*48827Swilliam outb(wdc+wd_cyl_hi, 1224/256); 302*48827Swilliam outb(wdc+wd_command, 0x91); 303*48827Swilliam while (inb(wdc+wd_status) & WDCS_BUSY) ; 304*48827Swilliam 30541085Swilliam errcnt = 0; 30641085Swilliam retry: 30741085Swilliam /* 308*48827Swilliam * Read in LABELSECTOR to get the pack label and geometry. 30941085Swilliam */ 31041085Swilliam outb(wdc+wd_precomp, 0xff); /* sometimes this is head bit 3 */ 31141085Swilliam outb(wdc+wd_seccnt, 1); 312*48827Swilliam outb(wdc+wd_sector, LABELSECTOR + 1); 31341085Swilliam outb(wdc+wd_cyl_lo, (cyloffset & 0xff)); 31441085Swilliam outb(wdc+wd_cyl_hi, (cyloffset >> 8)); 31541085Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 31641085Swilliam wdwait(); 31741085Swilliam outb(wdc+wd_command, WDCC_READ); 31841085Swilliam wdwait(); 31941085Swilliam if ((i = inb(wdc+wd_status)) & WDCS_ERR) { 320*48827Swilliam int err; 321*48827Swilliam 322*48827Swilliam err = inb(wdc+wd_error); 32341085Swilliam if (++errcnt < RETRIES) 32441085Swilliam goto retry; 32541085Swilliam if (!wdquiet) 326*48827Swilliam /*#ifdef SMALL 327*48827Swilliam printf("wd%d: reading label, status %x error %x\n", 328*48827Swilliam unit, i, err); 329*48827Swilliam #else*/ 33041085Swilliam printf("wd%d: reading label, status %b error %b\n", 331*48827Swilliam unit, i, WDCS_BITS, err, WDERR_BITS); 332*48827Swilliam /*#endif*/ 33341085Swilliam return(-1); 33441085Swilliam } 33541085Swilliam 33641085Swilliam /* Ready to remove data? */ 337*48827Swilliam while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; 33841085Swilliam 33941085Swilliam i = insw(wdc+wd_data, buf, 256); 34041085Swilliam 341*48827Swilliam #ifdef WDDEBUG 342*48827Swilliam printf("magic %x,insw %x, %x\n", 343*48827Swilliam ((struct disklabel *) (buf + LABELOFFSET))->d_magic, i, buf); 344*48827Swilliam #endif 345*48827Swilliam if (((struct disklabel *) (buf + LABELOFFSET))->d_magic == DISKMAGIC) { 34641085Swilliam *dd = * (struct disklabel *) (buf + LABELOFFSET); 34741085Swilliam open[unit] = 1; 34841085Swilliam } else { 34941085Swilliam if (!wdquiet) 35041085Swilliam printf("wd%d: bad disk label\n", unit); 35141085Swilliam if (io->i_flgs & F_FILE) return(-1); 35241085Swilliam dkbad[unit].bt_bad[0].bt_cyl = -1; 353*48827Swilliam dd->d_secpercyl = 1999999 ; dd->d_nsectors = 17 ; 354*48827Swilliam dd->d_secsize = 512; 35541085Swilliam outb(wdc+wd_precomp, 0xff); /* force head 3 bit off */ 35641085Swilliam return (0) ; 35741085Swilliam } 358*48827Swilliam #endif SMALL 359*48827Swilliam #ifdef SMALL 360*48827Swilliam #ifdef WDDEBUG 361*48827Swilliam printf("magic %x sect %d\n", dd->d_magic, dd->d_nsectors); 362*48827Swilliam #endif 36341085Swilliam #endif SMALL 36441085Swilliam 365*48827Swilliam 366*48827Swilliam /* now that we know the disk geometry, tell the controller */ 367*48827Swilliam outb(wdc+wd_cyl_lo, dd->d_ncylinders); 368*48827Swilliam outb(wdc+wd_cyl_hi, (dd->d_ncylinders)>>8); 369*48827Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + dd->d_ntracks-1); 370*48827Swilliam outb(wdc+wd_seccnt, dd->d_nsectors); 371*48827Swilliam outb(wdc+wd_command, 0x91); 372*48827Swilliam while (inb(wdc+wd_status) & WDCS_BUSY) ; 373*48827Swilliam 374*48827Swilliam dkbad[unit].bt_bad[0].bt_cyl = -1; 375*48827Swilliam outb(wdc+wd_precomp, dd->d_precompcyl / 4); 376*48827Swilliam 37741085Swilliam /* 37841085Swilliam * Read bad sector table into memory. 37941085Swilliam */ 38041085Swilliam i = 0; 38141085Swilliam do { 382*48827Swilliam int blknm = dd->d_secperunit - dd->d_nsectors + i; 38341085Swilliam errcnt = wdio(READ, unit, blknm, buf); 384*48827Swilliam } while (errcnt && (i += 2) < 10 && i < dd->d_nsectors); 38541085Swilliam db = (struct dkbad *)(buf); 386*48827Swilliam #define DKBAD_MAGIC 0x4321 38741085Swilliam if (errcnt == 0 && db->bt_mbz == 0 && db->bt_flag == DKBAD_MAGIC) 38841085Swilliam dkbad[unit] = *db; 38941085Swilliam else { 39041085Swilliam if (!wdquiet) 39141085Swilliam printf("wd%d: error in bad-sector file\n", unit); 39241085Swilliam dkbad[unit].bt_bad[0].bt_cyl = -1; 39341085Swilliam } 39441085Swilliam return(0); 39541085Swilliam } 39641085Swilliam 39741085Swilliam wdwait() 39841085Swilliam { 399*48827Swilliam register wdc = wdcport; 40041085Swilliam register i = 0; 40141085Swilliam 402*48827Swilliam while (inb(wdc+wd_status) & WDCS_BUSY) 40341085Swilliam ; 404*48827Swilliam while ((inb(wdc+wd_status) & WDCS_READY) == 0) 40541085Swilliam if (i++ > 100000) 40641085Swilliam return(-1); 40741085Swilliam return(0); 40841085Swilliam } 409