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