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 * 848827Swilliam * %sccs.include.redist.c% 941085Swilliam * 10*49080Sbostic * @(#)wd.c 7.3 (Berkeley) 05/04/91 1141085Swilliam */ 1241085Swilliam 1341085Swilliam /* device driver for winchester disk */ 1441085Swilliam 1548827Swilliam #include "param.h" 1648827Swilliam #include "dkbad.h" 1748827Swilliam #include "disklabel.h" 1848827Swilliam #include "i386/isa/isa.h" 1948827Swilliam #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 2548827Swilliam int noretries, wdquiet; 2648827Swilliam /*#define WDDEBUG*/ 2741085Swilliam 2841085Swilliam #ifdef SMALL 2941085Swilliam extern struct disklabel disklabel; 3041085Swilliam #else 3141085Swilliam struct disklabel wdsizes[NWD]; 3241085Swilliam #endif 3341085Swilliam 3448827Swilliam extern cyloffset ; /* bootstrap's idea of cylinder for disklabel */ 3548827Swilliam 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]; 4448827Swilliam static wdcport; 4541085Swilliam 4641085Swilliam wdopen(io) 4741085Swilliam register struct iob *io; 4841085Swilliam { 4941085Swilliam register struct disklabel *dd; 5041085Swilliam 5148827Swilliam #ifdef WDDEBUG 5248827Swilliam printf("wdopen "); 5348827Swilliam #endif 5441085Swilliam #ifdef SMALL 5541085Swilliam dd = &disklabel; 5641085Swilliam #else 5748827Swilliam dd = &wdsizes[io->i_unit]; 5848827Swilliam if (io->i_part > 8) 5941085Swilliam _stop("Invalid partition number"); 6048827Swilliam if(io->i_ctlr > 1) 6148827Swilliam _stop("Invalid controller number"); 6241085Swilliam #endif 6341085Swilliam if (wdinit(io)) 6441085Swilliam _stop("wd initialization error"); 6548827Swilliam io->i_boff = dd->d_partitions[io->i_part].p_offset ; 6648827Swilliam 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 7948827Swilliam unit = io->i_unit; 8048827Swilliam partition = io->i_part; 8148827Swilliam #ifdef WDDEBUG 8248827Swilliam printf("wdstrat %d %d ", unit, partition); 8348827Swilliam #endif 8441085Swilliam #ifdef SMALL 8541085Swilliam dd = &disklabel; 8641085Swilliam #else 8741085Swilliam dd = &wdsizes[unit]; 8841085Swilliam #endif 8948827Swilliam 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 */ 9548827Swilliam sector = (unsigned long) io->i_bn * DEV_BSIZE / dd->d_secsize; 9648827Swilliam nblocks = dd->d_partitions[partition].p_size; 9748827Swilliam #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 } 10648827Swilliam if (io->i_bn * DEV_BSIZE % dd->d_secsize) { 10741085Swilliam printf("wdstrategy - transfer starts in midsector\n"); 10841085Swilliam return(-1); 10941085Swilliam } 11048827Swilliam if (io->i_cc % dd->d_secsize) { 11141085Swilliam printf("wd: transfer of partial sector\n"); 11241085Swilliam return(-1); 11341085Swilliam } 11448827Swilliam #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++; 12248827Swilliam 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; 13548827Swilliam register wdc = wdcport; 13641085Swilliam struct bt_bad *bt_ptr; 13741085Swilliam int i; 13841085Swilliam int retries = 0; 13941085Swilliam long cylin, head, sector; 14048827Swilliam u_char opcode, erro; 14141085Swilliam 14241085Swilliam #ifdef SMALL 14341085Swilliam dd = &disklabel; 14441085Swilliam #else 14541085Swilliam dd = &wdsizes[unit]; 14641085Swilliam #endif 147*49080Sbostic if (func == F_WRITE) 14841085Swilliam opcode = WDCC_WRITE; 14941085Swilliam else 15041085Swilliam opcode = WDCC_READ; 15141085Swilliam 15241085Swilliam /* Calculate data for output. */ 15348827Swilliam cylin = blknm / dd->d_secpercyl; 15448827Swilliam head = (blknm % dd->d_secpercyl) / dd->d_nsectors; 15548827Swilliam sector = blknm % dd->d_nsectors; 15641085Swilliam 15741085Swilliam /* 15841085Swilliam * See if the current block is in the bad block list. 15941085Swilliam */ 16048827Swilliam 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 17748827Swilliam blknm = dd->d_secperunit - dd->d_nsectors 17841085Swilliam - (bt_ptr - dkbad[unit].bt_bad) - 1; 17948827Swilliam cylin = blknm / dd->d_secpercyl; 18048827Swilliam head = (blknm % dd->d_secpercyl) / dd->d_nsectors; 18148827Swilliam sector = blknm % dd->d_nsectors; 18241085Swilliam #ifdef WDDEBUG 18341085Swilliam printf("new = %d\n", blknm); 18441085Swilliam #endif 18541085Swilliam break; 18641085Swilliam } 18741085Swilliam } 18841085Swilliam 18948827Swilliam sector += 1; 19048827Swilliam retry: 19148827Swilliam #ifdef WDDEBUG 19248827Swilliam printf("sec %d sdh %x cylin %d ", sector, 19348827Swilliam 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)); 20348827Swilliam while ((inb(wdc+wd_status) & WDCS_READY) == 0) ; 20441085Swilliam 20541085Swilliam outb(wdc+wd_command, opcode); 20648827Swilliam while (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_BUSY)) 20741085Swilliam ; 20841085Swilliam /* Did we get an error? */ 20948827Swilliam if (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_ERR)) 21041085Swilliam goto error; 21141085Swilliam 21241085Swilliam /* Ready to remove data? */ 21348827Swilliam 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). */ 22048827Swilliam if (inb(wdc+wd_status) & WDCS_DRQ) goto error; 22141085Swilliam 22248827Swilliam while (opcode == WDCC_WRITE && (inb(wdc+wd_status) & WDCS_BUSY)) ; 22341085Swilliam 22448827Swilliam if (inb(wdc+wd_status) & WDCS_ERR) goto error; 22541085Swilliam 22648827Swilliam #ifdef WDDEBUG 22748827Swilliam printf("+"); 22848827Swilliam #endif 22941085Swilliam return (0); 23041085Swilliam error: 23148827Swilliam erro = inb(wdc+wd_error); 23241085Swilliam if (++retries < RETRIES) 23341085Swilliam goto retry; 23441085Swilliam if (!wdquiet) 23548827Swilliam #ifdef SMALL 23648827Swilliam printf("wd%d: hard error: sector %d status %x error %x\n", unit, 23748827Swilliam blknm, inb(wdc+wd_status), erro); 23848827Swilliam #else 23948827Swilliam printf("wd%d: hard %s error: sector %d status %b error %b\n", unit, 24041085Swilliam opcode == WDCC_READ? "read" : "write", blknm, 24148827Swilliam inb(wdc+wd_status), WDCS_BITS, erro, WDERR_BITS); 24248827Swilliam #endif 24341085Swilliam return (-1); 24441085Swilliam } 24541085Swilliam 24641085Swilliam wdinit(io) 24741085Swilliam struct iob *io; 24841085Swilliam { 24948827Swilliam 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 25748827Swilliam unit = io->i_unit; 25848827Swilliam if (open[unit]) return(0); 25941085Swilliam 26048827Swilliam wdcport = io->i_ctlr ? IO_WD2 : IO_WD1; 26148827Swilliam wdc = wdcport; 26248827Swilliam 26341085Swilliam #ifdef SMALL 26441085Swilliam dd = &disklabel; 26548827Swilliam #else 26648827Swilliam /* reset controller */ 26748827Swilliam outb(wdc+wd_ctlr, 12); wait(10); outb(wdc+wd_ctlr, 8); 26841085Swilliam wdwait(); 26948827Swilliam 27041085Swilliam dd = &wdsizes[unit]; 27141085Swilliam 27241085Swilliam tryagainrecal: 27348827Swilliam /* 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) { 27948827Swilliam /*#ifdef SMALL 28048827Swilliam printf("wd%d: recal status %x error %x\n", 28148827Swilliam unit, i, inb(wdc+wd_error)); 28248827Swilliam #else*/ 28341085Swilliam printf("wd%d: recal status %b error %b\n", 28441085Swilliam unit, i, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS); 28548827Swilliam /*#endif*/ 28641085Swilliam if (++errcnt < 10) 28741085Swilliam goto tryagainrecal; 28841085Swilliam return(-1); 28941085Swilliam } 29048827Swilliam 29148827Swilliam /* 29248827Swilliam * Some controllers require this (after a recal they 29348827Swilliam * revert to a logical translation mode to compensate for 29448827Swilliam * dos limitation on 10-bit cylinders -- *shudder* -wfj) 29548827Swilliam * note: cylinders *must* be fewer than or equal to 8 to 29648827Swilliam * compensate for some IDE drives that latch this for all time. 29748827Swilliam */ 29848827Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + 8 -1); 29948827Swilliam outb(wdc+wd_seccnt, 35 ); 30048827Swilliam outb(wdc+wd_cyl_lo, 1224); 30148827Swilliam outb(wdc+wd_cyl_hi, 1224/256); 30248827Swilliam outb(wdc+wd_command, 0x91); 30348827Swilliam while (inb(wdc+wd_status) & WDCS_BUSY) ; 30448827Swilliam 30541085Swilliam errcnt = 0; 30641085Swilliam retry: 30741085Swilliam /* 30848827Swilliam * 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); 31248827Swilliam 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) { 32048827Swilliam int err; 32148827Swilliam 32248827Swilliam err = inb(wdc+wd_error); 32341085Swilliam if (++errcnt < RETRIES) 32441085Swilliam goto retry; 32541085Swilliam if (!wdquiet) 32648827Swilliam /*#ifdef SMALL 32748827Swilliam printf("wd%d: reading label, status %x error %x\n", 32848827Swilliam unit, i, err); 32948827Swilliam #else*/ 33041085Swilliam printf("wd%d: reading label, status %b error %b\n", 33148827Swilliam unit, i, WDCS_BITS, err, WDERR_BITS); 33248827Swilliam /*#endif*/ 33341085Swilliam return(-1); 33441085Swilliam } 33541085Swilliam 33641085Swilliam /* Ready to remove data? */ 33748827Swilliam while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; 33841085Swilliam 33941085Swilliam i = insw(wdc+wd_data, buf, 256); 34041085Swilliam 34148827Swilliam #ifdef WDDEBUG 34248827Swilliam printf("magic %x,insw %x, %x\n", 34348827Swilliam ((struct disklabel *) (buf + LABELOFFSET))->d_magic, i, buf); 34448827Swilliam #endif 34548827Swilliam 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; 35348827Swilliam dd->d_secpercyl = 1999999 ; dd->d_nsectors = 17 ; 35448827Swilliam dd->d_secsize = 512; 35541085Swilliam outb(wdc+wd_precomp, 0xff); /* force head 3 bit off */ 35641085Swilliam return (0) ; 35741085Swilliam } 35848827Swilliam #endif SMALL 35948827Swilliam #ifdef SMALL 36048827Swilliam #ifdef WDDEBUG 36148827Swilliam printf("magic %x sect %d\n", dd->d_magic, dd->d_nsectors); 36248827Swilliam #endif 36341085Swilliam #endif SMALL 36441085Swilliam 36548827Swilliam 36648827Swilliam /* now that we know the disk geometry, tell the controller */ 36748827Swilliam outb(wdc+wd_cyl_lo, dd->d_ncylinders); 36848827Swilliam outb(wdc+wd_cyl_hi, (dd->d_ncylinders)>>8); 36948827Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + dd->d_ntracks-1); 37048827Swilliam outb(wdc+wd_seccnt, dd->d_nsectors); 37148827Swilliam outb(wdc+wd_command, 0x91); 37248827Swilliam while (inb(wdc+wd_status) & WDCS_BUSY) ; 37348827Swilliam 37448827Swilliam dkbad[unit].bt_bad[0].bt_cyl = -1; 37548827Swilliam outb(wdc+wd_precomp, dd->d_precompcyl / 4); 37648827Swilliam 37741085Swilliam /* 37841085Swilliam * Read bad sector table into memory. 37941085Swilliam */ 38041085Swilliam i = 0; 38141085Swilliam do { 38248827Swilliam int blknm = dd->d_secperunit - dd->d_nsectors + i; 383*49080Sbostic errcnt = wdio(F_READ, unit, blknm, buf); 38448827Swilliam } while (errcnt && (i += 2) < 10 && i < dd->d_nsectors); 38541085Swilliam db = (struct dkbad *)(buf); 38648827Swilliam #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 { 39948827Swilliam register wdc = wdcport; 40041085Swilliam register i = 0; 40141085Swilliam 40248827Swilliam while (inb(wdc+wd_status) & WDCS_BUSY) 40341085Swilliam ; 40448827Swilliam while ((inb(wdc+wd_status) & WDCS_READY) == 0) 40541085Swilliam if (i++ > 100000) 40641085Swilliam return(-1); 40741085Swilliam return(0); 40841085Swilliam } 409