/* * linux/drivers/block/ide.c Version 3.15 May 5, 1995 * * Copyright (C) 1994, 1995 Linus Torvalds & authors (see below) */ /* * This is the dual IDE interface driver, as evolved from hd.c. * It supports up to two IDE interfaces, on one or two IRQs (usually 14 & 15). * There can be up to two drives per interface, as per the ATA-2 spec. * * Primary i/f: ide0: major=3; (hda) minor=0, (hdb) minor=64 * Secondary i/f: ide1: major=22; (hdc or hd1a) minor=0, (hdd or hd1b) minor=64 * * From hd.c: * | * | It traverses the request-list, using interrupts to jump between functions. * | As nearly all functions can be called within interrupts, we may not sleep. * | Special care is recommended. Have Fun! * | * | modified by Drew Eckhardt to check nr of hd's from the CMOS. * | * | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug * | in the early extended-partition checks and added DM partitions. * | * | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI). * | * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads", * | and general streamlining by Mark Lord (mlord@bnr.ca). * * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by: * * Mark Lord (mlord@bnr.ca) (IDE Perf.Pkg) * Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2") * Petri Mattila (ptjmatti@kruuna.helsinki.fi) (EIDE stuff) * Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom) * * This was a rewrite of just about everything from hd.c, though some original * code is still sprinkled about. Think of it as a major evolution, with * inspiration from lots of linux users, esp. hamish@zot.apana.org.au * * Version 1.0 ALPHA initial code, primary i/f working okay * Version 1.1 ALPHA fixes for dual i/f * Version 1.2 ALPHA first serious attempt at sharing irqs * Version 1.3 BETA dual i/f on shared irq tested & working! * Version 1.4 BETA added auto probing for irq(s) * Version 1.5 BETA added ALPHA (untested) support for IDE cd-roms, * fixed hd.c coexistence bug, other minor stuff * Version 1.6 BETA fix link error when cd-rom not configured * Version 2.0 BETA lots of minor fixes; remove annoying messages; ... * Version 2.2 BETA fixed reset_drives; major overhaul of autoprobing * Version 2.3 BETA set DEFAULT_UNMASK_INTR to 0 again; cosmetic changes * Version 2.4 BETA added debounce on reading of drive status reg, * added config flags to remove unwanted features * Version 2.5 BETA fixed problem with leftover phantom IRQ after probe, * allow "set_geometry" even when in LBA (as per spec(?)), * assorted miscellaneous tweaks. * Version 2.6 BETA more config flag stuff, another probing tweak, * (not released) multmode now defaults to status quo from boot time, * moved >16heads check to init time, rearranged reset code * added HDIO_DRIVE_CMD, removed standby/xfermode stuff * hopefully fixed ATAPI probing code, added hdx=cdrom * Version 2.7 BETA fixed invocation of cdrom_setup() * Version 2.8 BETA fixed compile error for DISK_RECOVERY_TIME>0 * fixed incorrect drive selection in DO_DRIVE_CMD (Bug!) * Version 2.9 BETA more work on ATAPI CDROM recognition * (not released) changed init order so partition checks go in sequence * Version 3.0 BETA included ide-cd.c update from Steve with Mitsumi fixes * attempt to fix byte-swap problem with Mitsumi id_info * ensure drives on second i/f get initialized on boot * preliminary compile-time support for 32bit IDE i/f chips * added check_region() and snarf_region() to probes * Version 3.1 BETA ensure drives on *both* i/f get initialized on boot * fix byte-swap problem with Mitsumi id_info * changed ide_timermask into ide_timerbit * get rid of unexpected interrupts after probing * don't wait for READY_STAT on cdrom drives * Version 3.2 BETA Ooops.. mistakenly left VLB_32BIT_IDE on by default * new ide-cd.c from Scott * Version 3.3 BETA fix compiling with PROBE_FOR_IRQS==0 * (sent to Linus) tweak in do_probe() to fix Delman's DRDY problem * Version 3.4 BETA removed "444" debug message * (sent to Linus) * Version 3.5 correct the bios_cyl field if it's too small * (linux 1.1.76) (to help fdisk with brain-dead BIOSs) * Version 3.6 cosmetic corrections to comments and stuff * (linux 1.1.77) reorganise probing code to make it understandable * added halfway retry to probing for drive identification * added "hdx=noprobe" command line option * allow setting multmode even when identification fails * Version 3.7 move set_geometry=1 from do_identify() to ide_init() * increase DRQ_WAIT to eliminate nuisance messages * wait for DRQ_STAT instead of DATA_READY during probing * (courtesy of Gary Thomas gary@efland.UU.NET) * Version 3.8 fixed byte-swapping for confused Mitsumi cdrom drives * update of ide-cd.c from Scott, allows blocksize=1024 * cdrom probe fixes, inspired by jprang@uni-duisburg.de * Version 3.9 don't use LBA if lba_capacity looks funny * correct the drive capacity calculations * fix probing for old Seagates without HD_ALTSTATUS * fix byte-ordering for some NEC cdrom drives * Version 3.10 disable multiple mode by default; was causing trouble * Version 3.11 fix mis-identification of old WD disks as cdroms * Version 3,12 simplify logic for selecting initial mult_count * (fixes problems with buggy WD drives) * Version 3.13 remove excess "multiple mode disabled" messages * Version 3.14 fix ide_error() handling of BUSY_STAT * fix byte-swapped cdrom strings (again.. arghh!) * ignore INDEX bit when checking the ALTSTATUS reg * Version 3.15 add SINGLE_THREADED flag for use with dual-CMD i/f * ignore WRERR_STAT for non-write operations * added VLB_32BIT_IO support for DC-2000A & others, * (incl. some Promise chips), courtesy of Frank Gockel * * To do: * - special 32-bit controller-type detection & support * - figure out how to support oddball "intelligent" caching cards * - reverse-engineer 3/4 drive support on fancy "Promise" cards */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /***************************************************************************** * IDE driver configuration options (play with these as desired): */ #define REALLY_SLOW_IO /* most systems can safely undef this */ #include #undef REALLY_FAST_IO /* define if ide ports are perfect */ #define INITIAL_MULT_COUNT 0 /* use zero to disable block mode */ #ifndef VLB_32BIT_IDE /* 0 for safety, 1 for 32-bit chipset */ #define VLB_32BIT_IDE 0 /* Winbond 83759F, OPTi 82C621 */ #endif #ifndef NEED_VLB_SYNC /* for VLB_32BIT_IDE on some chipsets */ #define NEED_VLB_SYNC 1 /* 0=simpler, 1=DC-2000A,PROMISE,... */ #endif #ifndef DISK_RECOVERY_TIME /* min. delay between IO for hardware */ #define DISK_RECOVERY_TIME 0 /* that needs it. */ #endif #ifndef OK_TO_RESET_CONTROLLER /* 1 needed for good error recovery */ #define OK_TO_RESET_CONTROLLER 1 /* 0 for use with AH2372A/B interface */ #endif #ifndef SUPPORT_TWO_INTERFACES /* 1 to support one/two interfaces */ #define SUPPORT_TWO_INTERFACES 1 /* 0 for a smaller, faster kernel */ #endif #ifndef OPTIMIZE_IRQS /* 1 for slightly faster code */ #define OPTIMIZE_IRQS 1 /* 0 to reduce kernel size */ #endif #ifndef SINGLE_THREADED /* 1 for dual CMD interfaces */ #define SINGLE_THREADED 0 /* 0 for concurrent i/f operation */ #endif #ifndef SUPPORT_SHARING_IRQ /* 1 to allow two IDE i/f on one IRQ */ #define SUPPORT_SHARING_IRQ 1 /* 0 to reduce kernel size */ #endif #ifndef FANCY_STATUS_DUMPS /* 1 for human-readable drive errors */ #define FANCY_STATUS_DUMPS 1 /* 0 to reduce kernel size */ #endif #define PROBE_FOR_IRQS 1 /* 0 to force use of defaults below */ #define DEFAULT_IDE0_IRQ 14 /* in case irq-probe fails */ #define DEFAULT_IDE1_IRQ 15 /* in case irq-probe fails */ /* IDE_DRIVE_CMD is used to implement many features of the hdparm utility */ #define IDE_DRIVE_CMD 99 /* (magic) undef to reduce kernel size*/ /* * "No user-serviceable parts" beyond this point :) ****************************************************************************** */ /* * Need to change these elsewhere in the kernel (someday) */ #ifndef IDE0_TIMER #define IDE0_TIMER HD_TIMER #define IDE1_TIMER HD_TIMER2 #endif /* * Ensure that various configuration flags have compatible settings */ #ifdef REALLY_SLOW_IO #undef REALLY_FAST_IO #endif #ifdef CONFIG_BLK_DEV_HD #undef SUPPORT_TWO_INTERFACES #define SUPPORT_TWO_INTERFACES 0 #endif /* CONFIG_BLK_DEV_HD */ #if SUPPORT_TWO_INTERFACES #define HWIF hwif #define DEV_HWIF (dev->hwif) #if SINGLE_THREADED #undef SUPPORT_SHARING_IRQ #define SUPPORT_SHARING_IRQ 1 #endif #else #undef SINGLE_THREADED #define SINGLE_THREADED 0 #undef OPTIMIZE_IRQS #define OPTIMIZE_IRQS 0 #undef SUPPORT_SHARING_IRQ #define SUPPORT_SHARING_IRQ 0 #ifdef CONFIG_BLK_DEV_HD #define HWIF 1 #else #define HWIF 0 #endif /* CONFIG_BLK_DEV_HD */ #define DEV_HWIF HWIF #endif /* SUPPORT_TWO_INTERFACES */ /* * Definitions for accessing IDE controller registers */ typedef unsigned char byte; /* used everywhere */ #define IDE_PORT(p,hwif) ((p)^((hwif)<<7)) /* IDE0: p^0x00 , IDE1: p^0x80 */ #ifdef REALLY_FAST_IO #define OUT_BYTE(b,p) outb((b),IDE_PORT(p,DEV_HWIF)) #define IN_BYTE(p,hwif) (byte)inb(IDE_PORT(p,hwif)) #else #define OUT_BYTE(b,p) outb_p((b),IDE_PORT(p,DEV_HWIF)) #define IN_BYTE(p,hwif) (byte)inb_p(IDE_PORT(p,hwif)) #endif /* REALLY_FAST_IO */ #if VLB_32BIT_IDE #define _OUT_SECTORS(b,n) outsl(IDE_PORT(HD_DATA,DEV_HWIF),(b),(n)<<7) #define _IN_SECTORS(b,n) insl(IDE_PORT(HD_DATA,DEV_HWIF),(b),(n)<<7) #if NEED_VLB_SYNC #define VLB_SYNC __asm__ __volatile__ ("pusha\n movl $0x01f2,%edx\n inb (%dx),%al\n inb (%dx),%al\n inb (%dx),%al\n popa\n") #define OUT_SECTORS(b,n) {cli(); VLB_SYNC; _OUT_SECTORS(b,n); if (dev->unmask) sti();} #define IN_SECTORS(b,n) {cli(); VLB_SYNC; _IN_SECTORS(b,n); if (dev->unmask) sti();} #else #define OUT_SECTORS(b,n) _OUT_SECTORS(b,n) #define IN_SECTORS(b,n) _IN_SECTORS(b,n) #endif /* NEED_VLB_SYNC */ #else #define OUT_SECTORS(b,n) outsw(IDE_PORT(HD_DATA,DEV_HWIF),(b),(n)<<8) #define IN_SECTORS(b,n) insw(IDE_PORT(HD_DATA,DEV_HWIF),(b),(n)<<8) #endif /* VLB_32BIT_IDE */ #define GET_ERR(hwif) IN_BYTE(HD_ERROR,hwif) #define GET_STAT(hwif) IN_BYTE(HD_STATUS,hwif) #define OK_STAT(stat,good,bad) (((stat)&((good)|(bad)))==(good)) #define BAD_R_STAT (BUSY_STAT | ERR_STAT) #define BAD_W_STAT (BUSY_STAT | ERR_STAT | WRERR_STAT) #define BAD_STAT (BAD_R_STAT | DRQ_STAT) #define DRIVE_READY (READY_STAT | SEEK_STAT) #define DATA_READY (DRIVE_READY | DRQ_STAT) /* * Some more useful definitions */ #define BIOS_SECTORS(dev) (dev->bios_head*dev->bios_sect*dev->bios_cyl) #define HD_NAME "hd" /* the same for both i/f; see also genhd.c */ #define PARTN_BITS 6 /* number of minor dev bits for partitions */ #define PARTN_MASK ((1< 0) static unsigned long ide_lastreq[] = {0,0}; /* completion time of last I/O */ #define SET_DISK_RECOVERY_TIMER ide_lastreq[DEV_HWIF] = read_timer(); static unsigned long read_timer(void) { unsigned long t, flags; int i; save_flags(flags); cli(); t = jiffies * 11932; outb_p(0, 0x43); i = inb_p(0x40); i |= inb(0x40) << 8; restore_flags(flags); return (t - i); } #else #define SET_DISK_RECOVERY_TIMER /* nothing */ #endif /* DISK_RECOVERY_TIME */ /* * The heart of the driver, referenced from lots of other routines: */ static void do_request (byte hwif); #define DO_REQUEST {SET_DISK_RECOVERY_TIMER do_request(DEV_HWIF);} /* * This is a macro rather than an inline to permit better gcc code. * Caller MUST do sti() before invoking WAIT_STAT() (for jiffies to work). * * This routine should get fixed to not hog the cpu during extra long waits.. * That could be done by busy-waiting for the first jiffy or two, and then * setting a timer to wake up at half second intervals thereafter, * until WAIT_WORSTCASE is achieved, before timing out. */ #define WAIT_STAT(dev,good,bad,timeout,msg,label) \ { \ byte stat; \ udelay(1); /* spec allows drive 400ns to assert "BUSY" */ \ if (GET_STAT(DEV_HWIF) & BUSY_STAT) { \ unsigned long timer = jiffies + timeout; \ do { \ if ((GET_STAT(DEV_HWIF) & BUSY_STAT) == 0) \ break; \ } while (timer > jiffies); \ } \ udelay(1); /* spec allows 400ns for status to stabilize */ \ if (!OK_STAT(stat=GET_STAT(DEV_HWIF), good, bad)) { \ ide_error(dev, msg " error", stat); \ goto label; \ } \ } /* * This should get invoked on every exit path from the driver. */ static inline void start_ide_timer (byte hwif) { if (ide_handler[HWIF] != NULL) { /* waiting for an irq? */ timer_table[ide_timer[HWIF]].expires = jiffies + WAIT_CMD; timer_active |= ide_timerbit[HWIF]; } } static void do_ide_reset (ide_dev_t *dev) { byte tmp; unsigned long timer, flags; save_flags(flags); sti(); for (tmp = 0; tmp < MAX_DRIVES; tmp++) { ide_dev_t *rdev = &ide_dev[DEV_HWIF][tmp]; rdev->special.b.set_geometry = 1; rdev->special.b.recalibrate = 1; rdev->special.b.set_multmode = 0; if (OK_TO_RESET_CONTROLLER) rdev->mult_count = 0; if (!rdev->keep_settings) { rdev->mult_req = 0; rdev->unmask = 0; } if (rdev->mult_req != rdev->mult_count) rdev->special.b.set_multmode = 1; } #if OK_TO_RESET_CONTROLLER cli(); OUT_BYTE(dev->ctl|6,HD_CMD); /* set nIEN, set SRST */ udelay(10); /* more than enough time */ OUT_BYTE(dev->ctl|2,HD_CMD); /* clear SRST */ udelay(10); /* more than enough time */ sti(); /* needed for jiffies */ for (timer = jiffies + WAIT_WORSTCASE; timer > jiffies;) { if ((GET_STAT(DEV_HWIF) & BUSY_STAT) == 0) break; } printk("%s: do_ide_reset: ", ide_name[DEV_HWIF]); /* ATAPI devices usually do *not* assert READY after a reset */ if (!OK_STAT(tmp=GET_STAT(DEV_HWIF), 0, BUSY_STAT)) { printk("timed-out, status=0x%02x\n", tmp); } else { if ((tmp = GET_ERR(DEV_HWIF)) == 1) printk("success\n"); else { printk("%s: ", ide_devname[DEV_HWIF][0]); switch (tmp & 0x7f) { case 1: printk("passed"); break; case 2: printk("formatter device error"); break; case 3: printk("sector buffer error"); break; case 4: printk("ECC circuitry error"); break; case 5: printk("controlling MPU error"); break; default:printk("error (0x%02x?)", tmp); } if (tmp & 0x80) printk("; %s: error", ide_devname[DEV_HWIF][1]); printk("\n"); } } #endif /* OK_TO_RESET_CONTROLLER */ restore_flags(flags); } /* * Clean up after success/failure of an explicit (ioctl) drive cmd */ static void end_drive_cmd (ide_dev_t *dev, byte stat, byte err) { unsigned long flags; struct request *rq = ide_cur_rq[DEV_HWIF]; byte *args = (byte *) rq->buffer; rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); if (args) { args[0] = stat; args[1] = err; args[2] = IN_BYTE(HD_NSECTOR,DEV_HWIF); } save_flags(flags); cli(); up(rq->sem); ide_cur_rq[DEV_HWIF] = NULL; restore_flags(flags); } /* * Error reporting, in human readable form (luxurious, but a memory hog). */ static byte dump_status (byte hwif, const char *msg, byte stat) { unsigned long flags; byte err = 0; ide_dev_t *dev = ide_cur_dev[HWIF]; const char *name = dev ? dev->name : ide_name[HWIF]; save_flags (flags); sti(); printk("%s: %s: status=0x%02x", name, msg, stat); #if FANCY_STATUS_DUMPS if (dev && dev->type == disk) { printk(" { "); if (stat & BUSY_STAT) printk("Busy "); else { if (stat & READY_STAT) printk("DriveReady "); if (stat & WRERR_STAT) printk("WriteFault "); if (stat & SEEK_STAT) printk("SeekComplete "); if (stat & DRQ_STAT) printk("DataRequest "); if (stat & ECC_STAT) printk("CorrectedError "); if (stat & INDEX_STAT) printk("Index "); if (stat & ERR_STAT) printk("Error "); } printk("}"); } #endif /* FANCY_STATUS_DUMPS */ printk("\n"); if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) { err = GET_ERR(HWIF); printk("%s: %s: error=0x%02x", name, msg, err); #if FANCY_STATUS_DUMPS if (dev && dev->type == disk) { printk(" { "); if (err & BBD_ERR) printk("BadSector "); if (err & ECC_ERR) printk("UncorrectableError "); if (err & ID_ERR) printk("SectorIdNotFound "); if (err & ABRT_ERR) printk("DriveStatusError "); if (err & TRK0_ERR) printk("TrackZeroNotFound "); if (err & MARK_ERR) printk("AddrMarkNotFound "); printk("}"); if (err & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) { byte cur = IN_BYTE(HD_CURRENT,HWIF); if (cur & 0x40) { /* using LBA? */ printk(", LBAsect=%ld", (unsigned long) ((cur&0xf)<<24) |(IN_BYTE(HD_HCYL,HWIF)<<16) |(IN_BYTE(HD_LCYL,HWIF)<<8) | IN_BYTE(HD_SECTOR,HWIF)); } else { printk(", CHS=%d/%d/%d", (IN_BYTE(HD_HCYL,HWIF)<<8) + IN_BYTE(HD_LCYL,HWIF), cur & 0xf, IN_BYTE(HD_SECTOR,HWIF)); } if (ide_cur_rq[HWIF]) printk(", sector=%ld", ide_cur_rq[HWIF]->sector); } } #endif /* FANCY_STATUS_DUMPS */ printk("\n"); } restore_flags (flags); return err; } /* * ide_error() takes action based on the error returned by the controller. */ #define ERROR_MAX 8 /* Max read/write errors per sector */ #define ERROR_RESET 3 /* Reset controller every 4th retry */ #define ERROR_RECAL 1 /* Recalibrate every 2nd retry */ static void ide_error (ide_dev_t *dev, const char *msg, byte stat) { struct request *rq; byte err; err = dump_status(DEV_HWIF, msg, stat); if ((rq = ide_cur_rq[DEV_HWIF]) == NULL || dev == NULL) return; #ifdef IDE_DRIVE_CMD if (rq->cmd == IDE_DRIVE_CMD) { /* never retry an explicit DRIVE_CMD */ end_drive_cmd(dev, stat, err); return; } #endif /* IDE_DRIVE_CMD */ if (stat & BUSY_STAT) { /* other bits are useless when BUSY */ rq->errors |= ERROR_RESET; } else { if (dev->type == disk && (stat & ERR_STAT)) { /* err has different meaning on cdrom */ if (err & BBD_ERR) /* retries won't help this! */ rq->errors = ERROR_MAX; else if (err & TRK0_ERR) /* help it find track zero */ rq->errors |= ERROR_RECAL; } if ((stat & DRQ_STAT) && rq->cmd == READ) { int i = dev->mult_count ? dev->mult_count<<8 : 1<<8; while (i-- > 0) /* try to flush data */ (void) IN_BYTE(HD_DATA, dev->hwif); } } if (GET_STAT(dev->hwif) & (BUSY_STAT|DRQ_STAT)) rq->errors |= ERROR_RESET; /* Mmmm.. timing problem */ if (rq->errors >= ERROR_MAX) end_request(0, DEV_HWIF); else { if ((rq->errors & ERROR_RESET) == ERROR_RESET) do_ide_reset(dev); else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) dev->special.b.recalibrate = 1; ++rq->errors; } } static void read_intr (ide_dev_t *dev) { byte stat; int i; unsigned int msect, nsect; struct request *rq; if (!OK_STAT(stat=GET_STAT(DEV_HWIF),DATA_READY,BAD_R_STAT)) { sti(); ide_error(dev, "read_intr", stat); DO_REQUEST; return; } msect = dev->mult_count; read_next: rq = ide_cur_rq[DEV_HWIF]; if (msect) { if ((nsect = rq->current_nr_sectors) > msect) nsect = msect; msect -= nsect; } else nsect = 1; IN_SECTORS(rq->buffer,nsect); #ifdef DEBUG printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n", dev->name, rq->sector, rq->sector+nsect-1, (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect); #endif rq->sector += nsect; rq->buffer += nsect<<9; rq->errors = 0; i = (rq->nr_sectors -= nsect); if ((rq->current_nr_sectors -= nsect) <= 0) end_request(1, DEV_HWIF); if (i > 0) { if (msect) goto read_next; ide_handler[DEV_HWIF] = &read_intr; return; } /* (void) GET_STAT(DEV_HWIF); */ /* hd.c did this */ DO_REQUEST; } static void write_intr (ide_dev_t *dev) { byte stat; int i; struct request *rq = ide_cur_rq[DEV_HWIF]; if (OK_STAT(stat=GET_STAT(DEV_HWIF),DRIVE_READY,BAD_W_STAT)) { #ifdef DEBUG printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n", dev->name, rq->sector, (unsigned long) rq->buffer, rq->nr_sectors-1); #endif if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) { rq->sector++; rq->buffer += 512; rq->errors = 0; i = --rq->nr_sectors; --rq->current_nr_sectors; if (rq->current_nr_sectors <= 0) end_request(1, DEV_HWIF); if (i > 0) { ide_handler[DEV_HWIF] = &write_intr; OUT_SECTORS(rq->buffer,1); return; } DO_REQUEST; return; } } sti(); ide_error(dev, "write_intr", stat); DO_REQUEST; } static void multwrite (ide_dev_t *dev) { struct request *rq = &ide_write_rq[DEV_HWIF]; unsigned int mcount = dev->mult_count; do { unsigned int nsect = rq->current_nr_sectors; if (nsect > mcount) nsect = mcount; mcount -= nsect; OUT_SECTORS(rq->buffer,nsect); #ifdef DEBUG printk("%s: multwrite: sector %ld, buffer=0x%08lx, count=%d, remaining=%ld\n", dev->name, rq->sector, (unsigned long) rq->buffer, nsect, rq->nr_sectors - nsect); #endif if ((rq->nr_sectors -= nsect) <= 0) break; if ((rq->current_nr_sectors -= nsect) == 0) { if ((rq->bh = rq->bh->b_reqnext) != NULL) { rq->current_nr_sectors = rq->bh->b_size>>9; rq->buffer = rq->bh->b_data; } else { panic("%s: buffer list corrupted\n", dev->name); break; } } else { rq->buffer += nsect << 9; } } while (mcount); } static void multwrite_intr (ide_dev_t *dev) { byte stat; int i; struct request *rq = &ide_write_rq[DEV_HWIF]; if (OK_STAT(stat=GET_STAT(DEV_HWIF),DRIVE_READY,BAD_W_STAT)) { if (stat & DRQ_STAT) { if (rq->nr_sectors) { if (dev->mult_count) multwrite(dev); ide_handler[DEV_HWIF] = &multwrite_intr; return; } } else { if (!rq->nr_sectors) { /* all done? */ rq = ide_cur_rq[DEV_HWIF]; for (i = rq->nr_sectors; i > 0;){ i -= rq->current_nr_sectors; end_request(1, DEV_HWIF); } DO_REQUEST; return; } } } sti(); ide_error(dev, "multwrite_intr", stat); DO_REQUEST; } /* * Issue a simple drive command * The drive must be selected beforehand. */ static inline void ide_cmd(ide_dev_t *dev, byte cmd, byte nsect, void (*handler)(ide_dev_t *dev)) { OUT_BYTE(dev->ctl,HD_CMD); OUT_BYTE(nsect,HD_NSECTOR); OUT_BYTE(cmd,HD_COMMAND); ide_handler[DEV_HWIF] = handler; } static void set_multmode_intr (ide_dev_t *dev) { byte stat = GET_STAT(DEV_HWIF); sti(); if (!OK_STAT(stat,READY_STAT,BAD_STAT)) { dev->mult_req = dev->mult_count = 0; dev->special.b.recalibrate = 1; (void) dump_status(DEV_HWIF, "set_multmode", stat); } else { if ((dev->mult_count = dev->mult_req)) printk (" %s: enabled %d-sector multiple mode\n", dev->name, dev->mult_count); else printk (" %s: disabled multiple mode\n", dev->name); } DO_REQUEST; } static void set_geometry_intr (ide_dev_t *dev) { byte stat = GET_STAT(DEV_HWIF); sti(); if (!OK_STAT(stat,READY_STAT,BAD_STAT)) ide_error(dev, "set_geometry_intr", stat); DO_REQUEST; } static void recal_intr (ide_dev_t *dev) { byte stat = GET_STAT(DEV_HWIF); sti(); if (!OK_STAT(stat,READY_STAT,BAD_STAT)) ide_error(dev, "recal_intr", stat); DO_REQUEST; } static void drive_cmd_intr (ide_dev_t *dev) { byte stat = GET_STAT(DEV_HWIF); sti(); if (!OK_STAT(stat,READY_STAT,BAD_STAT)) ide_error(dev, "drive_cmd", stat); /* calls end_drive_cmd() */ else end_drive_cmd (dev, stat, GET_ERR(DEV_HWIF)); DO_REQUEST; } static void timer_expiry (byte hwif) { unsigned long flags; save_flags(flags); cli(); if (ide_handler[HWIF] == NULL || (timer_active & ide_timerbit[HWIF])) { /* The drive must have responded just as the timer expired */ sti(); printk("%s: marginal timeout\n", ide_name[HWIF]); } else { ide_handler[HWIF] = NULL; disable_irq(ide_irq[HWIF]); #if SINGLE_THREADED if (single_threaded && ide_irq[HWIF] != ide_irq[HWIF^1]) disable_irq(ide_irq[HWIF^1]); #endif /* SINGLE_THREADED */ sti(); ide_error(ide_cur_dev[HWIF], "timeout", GET_STAT(HWIF)); do_request(HWIF); #if SUPPORT_SHARING_IRQ if (single_threaded) /* this line is indeed necessary */ hwif = current_hwif; #endif /* SUPPORT_SHARING_IRQ */ cli(); start_ide_timer(HWIF); enable_irq(ide_irq[HWIF]); #if SINGLE_THREADED if (single_threaded && ide_irq[HWIF] != ide_irq[HWIF^1]) enable_irq(ide_irq[HWIF^1]); #endif /* SINGLE_THREADED */ } restore_flags(flags); } static void ide0_timer_expiry (void) /* invoked from sched.c */ { timer_expiry (0); } static void ide1_timer_expiry (void) /* invoked from sched.c */ { timer_expiry (1); } static int do_special (ide_dev_t *dev) { special_t *s = &dev->special; #ifdef DEBUG printk("%s: do_special: 0x%02x\n", dev->name, s->all); #endif if (s->b.set_geometry) { s->b.set_geometry = 0; if (dev->type == disk) { OUT_BYTE(dev->sect,HD_SECTOR); OUT_BYTE(dev->cyl,HD_LCYL); OUT_BYTE(dev->cyl>>8,HD_HCYL); OUT_BYTE(((dev->head-1)|dev->select.all)&0xBF,HD_CURRENT); ide_cmd(dev, WIN_SPECIFY, dev->sect, &set_geometry_intr); } } else if (s->b.recalibrate) { s->b.recalibrate = 0; if (dev->type == disk) ide_cmd(dev,WIN_RESTORE,dev->sect,&recal_intr); } else if (s->b.set_multmode) { if (dev->type == disk) { if (dev->id && dev->mult_req > dev->id->max_multsect) dev->mult_req = dev->id->max_multsect; ide_cmd(dev,WIN_SETMULT,dev->mult_req,&set_multmode_intr); } else { dev->mult_req = 0; printk("%s: multmode not supported by this device\n", dev->name); } s->b.set_multmode = 0; } else { if (s->all) { printk("%s: bad special flag: 0x%02x\n", dev->name, s->all); s->all = 0; } } return (ide_handler[DEV_HWIF] == NULL) ? 1 : 0; } #ifdef CONFIG_BLK_DEV_IDECD static byte wait_stat (ide_dev_t *dev, byte good, byte bad, unsigned long timeout) { unsigned long flags; save_flags(flags); sti(); WAIT_STAT(dev, good, bad, timeout, "status", error); restore_flags(flags); return 0; error: restore_flags(flags); return 1; } #include "ide-cd.c" #endif /* CONFIG_BLK_DEV_IDECD */ static inline int do_rw_disk (ide_dev_t *dev, struct request *rq, unsigned long block) { OUT_BYTE(dev->ctl,HD_CMD); OUT_BYTE(rq->nr_sectors,HD_NSECTOR); if (dev->select.b.lba) { #ifdef DEBUG printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n", dev->name, (rq->cmd==READ)?"read":"writ", block, rq->nr_sectors, (unsigned long) rq->buffer); #endif OUT_BYTE(block,HD_SECTOR); OUT_BYTE(block>>=8,HD_LCYL); OUT_BYTE(block>>=8,HD_HCYL); OUT_BYTE(((block>>8)&0x0f)|dev->select.all,HD_CURRENT); } else { unsigned int sect,head,cyl,track; track = block / dev->sect; sect = block % dev->sect + 1; OUT_BYTE(sect,HD_SECTOR); head = track % dev->head; cyl = track / dev->head; OUT_BYTE(cyl,HD_LCYL); OUT_BYTE(cyl>>8,HD_HCYL); OUT_BYTE(head|dev->select.all,HD_CURRENT); #ifdef DEBUG printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n", dev->name, (rq->cmd==READ)?"read":"writ", cyl, head, sect, rq->nr_sectors, (unsigned long) rq->buffer); #endif } if (rq->cmd == READ) { OUT_BYTE(dev->mult_count ? WIN_MULTREAD : WIN_READ, HD_COMMAND); ide_handler[DEV_HWIF] = &read_intr; return 0; } if (rq->cmd == WRITE) { OUT_BYTE(dev->wpcom,HD_PRECOMP); /* for ancient drives */ OUT_BYTE(dev->mult_count ? WIN_MULTWRITE : WIN_WRITE, HD_COMMAND); WAIT_STAT(dev, DATA_READY, BAD_W_STAT, WAIT_DRQ, "DRQ", error); if (!dev->unmask) cli(); if (dev->mult_count) { ide_write_rq[DEV_HWIF] = *rq; /* scratchpad */ multwrite(dev); ide_handler[DEV_HWIF] = &multwrite_intr; } else { OUT_SECTORS(rq->buffer,1); ide_handler[DEV_HWIF] = &write_intr; } return 0; } #ifdef IDE_DRIVE_CMD if (rq->cmd == IDE_DRIVE_CMD) { byte *args = rq->buffer; if (args) { OUT_BYTE(args[2],HD_FEATURE); ide_cmd(dev, args[0], args[1], &drive_cmd_intr); printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x\n", dev->name, args[0], args[1], args[2]); return 0; } else { #ifdef DEBUG printk("%s: DRIVE_CMD (null)\n", dev->name); #endif end_drive_cmd(dev,GET_STAT(DEV_HWIF),GET_ERR(DEV_HWIF)); return 1; } } #endif /* IDE_DRIVE_CMD */ printk("%s: bad command: %d\n", dev->name, rq->cmd); end_request(0, DEV_HWIF); error: return 1; } /* * The driver enables interrupts as much as possible. In order to do this, * (a) the device-interrupt is always masked before entry, and * (b) the timeout-interrupt is always disabled before entry. * * Interrupts are still masked (by default) whenever we are exchanging * data/cmds with a drive, because some drives seem to have very poor * tolerance for latency during I/O. For devices which don't suffer from * this problem (most don't), the ide_dev[][].unmask flag can be set to permit * other interrupts during data/cmd transfers by using the "hdparm" utility. */ static void do_request (byte hwif) { unsigned int minor, drive; unsigned long block, blockend; struct request *rq; ide_dev_t *dev; repeat: sti(); #if SUPPORT_SHARING_IRQ current_hwif = hwif; /* used *only* when single_threaded==1 */ #endif /* SUPPORT_SHARING_IRQ */ if ((rq = ide_cur_rq[HWIF]) == NULL) { rq = blk_dev[ide_major[HWIF]].current_request; if ((rq == NULL) || (rq->dev < 0)) { #if SUPPORT_SHARING_IRQ if (single_threaded) { if (sharing_single_irq && (dev = ide_cur_dev[hwif])) /* disable irq */ OUT_BYTE(dev->ctl|2,HD_CMD); rq = blk_dev[ide_major[hwif^=1]].current_request; if ((rq != NULL) && (rq->dev >= 0)) goto repeat; } #endif /* SUPPORT_SHARING_IRQ */ return; } blk_dev[ide_major[HWIF]].current_request = rq->next; ide_cur_rq[HWIF] = rq; } #ifdef DEBUG printk("%s: do_request: current=0x%08lx\n",ide_name[HWIF],(unsigned long)rq); #endif minor = MINOR(rq->dev); drive = minor >> PARTN_BITS; ide_cur_dev[HWIF] = dev = &ide_dev[HWIF][drive]; if ((MAJOR(rq->dev) != ide_major[HWIF]) || (drive >= MAX_DRIVES)) { printk("%s: bad device number: 0x%04x\n", ide_name[HWIF], rq->dev); end_request(0, HWIF); goto repeat; } if (rq->bh && !rq->bh->b_lock) { printk("%s: block not locked\n", ide_name[HWIF]); end_request(0, HWIF); goto repeat; } block = rq->sector; blockend = block + rq->nr_sectors; if ((blockend < block) || (blockend > ide_hd[HWIF][minor].nr_sects)) { printk("%s: bad access: block=%ld, count=%ld\n", dev->name, block, rq->nr_sectors); end_request(0, HWIF); goto repeat; } block += ide_hd[HWIF][minor].start_sect; #if (DISK_RECOVERY_TIME > 0) while ((read_timer() - ide_lastreq[HWIF]) < DISK_RECOVERY_TIME); #endif OUT_BYTE(dev->select.all,HD_CURRENT); #ifdef CONFIG_BLK_DEV_IDECD WAIT_STAT(dev, (dev->type == cdrom) ? 0 : READY_STAT, BUSY_STAT|DRQ_STAT, WAIT_READY, "DRDY", repeat); #else WAIT_STAT(dev, READY_STAT, BUSY_STAT|DRQ_STAT, WAIT_READY, "DRDY", repeat); #endif /* CONFIG_BLK_DEV_IDECD */ if (!dev->special.all) { #ifdef CONFIG_BLK_DEV_IDECD if (dev->type == disk) { #endif /* CONFIG_BLK_DEV_IDECD */ if (do_rw_disk(dev, rq, block)) goto repeat; #ifdef CONFIG_BLK_DEV_IDECD } else { if (do_rw_cdrom(dev, block)) goto repeat; } #endif /* CONFIG_BLK_DEV_IDECD */ } else { if (do_special(dev)) goto repeat; } } /* * This is a macro rather than an inline function to * prevent gcc from over-optimizing accesses to current_hwif, * which may have a different value on exit from do_request(). */ #define DO_IDE_REQUEST(hwif) \ { \ if (ide_handler[hwif] == NULL) { \ disable_irq(ide_irq[hwif]); \ if (single_threaded && ide_irq[hwif] != ide_irq[hwif^1]) \ disable_irq(ide_irq[hwif^1]); \ do_request(hwif); \ cli(); \ start_ide_timer(hwif); \ enable_irq(ide_irq[hwif]); \ if (single_threaded && ide_irq[hwif] != ide_irq[hwif^1]) \ enable_irq(ide_irq[hwif^1]); \ } \ } #if SUPPORT_TWO_INTERFACES static void do_ide0_request (void) /* invoked with cli() */ { DO_IDE_REQUEST(0); } static void do_ide1_request (void) /* invoked with cli() */ { DO_IDE_REQUEST(1); } #else #define do_ide1_request do_ide0_request static void do_ide0_request (void) /* invoked with cli() */ { DO_IDE_REQUEST(HWIF); } #endif /* SUPPORT_TWO_INTERFACES */ #if SUPPORT_SHARING_IRQ static void do_shared_request (void) /* invoked with cli() */ { DO_IDE_REQUEST(current_hwif); } #endif /* SUPPORT_SHARING_IRQ */ /* * There's nothing really useful we can do with an unexpected interrupt, * other than reading the status register (to clear it), and logging it. * There should be no way that an irq can happen before we're ready for it, * so we needn't worry much about losing an "important" interrupt here. * * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the * drive enters "idle", "standby", or "sleep" mode, so if the status looks * "good", we just ignore the interrupt completely. */ static void unexpected_intr (byte hwif) { byte stat; if (!OK_STAT(stat=GET_STAT(HWIF), DRIVE_READY, BAD_STAT)) (void) dump_status(HWIF, "unexpected_intr", stat); #if SUPPORT_SHARING_IRQ if (single_threaded) { if (!OK_STAT(stat=GET_STAT(hwif^1), DRIVE_READY, BAD_STAT)) (void) dump_status(hwif^1, "unexpected_intr", stat); } #endif /* SUPPORT_SHARING_IRQ */ } /* * This is a macro rather than an inline function to * prevent gcc from over-optimizing accesses to current_hwif, * which may have a different value on exit from handler(). */ #define IDE_INTR(hwif) \ { \ ide_dev_t *dev; \ void (*handler)(ide_dev_t *); \ \ timer_active &= ~ide_timerbit[hwif]; \ if ((handler = ide_handler[hwif]) != NULL) { \ ide_handler[hwif] = NULL; \ dev = ide_cur_dev[hwif]; \ if (dev->unmask) \ sti(); \ handler(dev); \ } else \ unexpected_intr(hwif); \ cli(); \ } #if SINGLE_THREADED /* entry point for all interrupts when single_threaded==1 */ static void ide_seq_intr (int irq, struct pt_regs *regs) { byte hwif = (irq != ide_irq[0]); IDE_INTR(HWIF); start_ide_timer(current_hwif); } #endif /* SINGLE_THREADED */ #if OPTIMIZE_IRQS /* entry point for all interrupts on ide0 when single_threaded==0 */ static void ide0_intr (int irq, struct pt_regs *regs) { IDE_INTR(0); start_ide_timer(0); } /* entry point for all interrupts on ide1 when single_threaded==0 */ static void ide1_intr (int irq, struct pt_regs *regs) { IDE_INTR(1); start_ide_timer(1); } #else /* OPTIMIZE_IRQS */ #define ide0_intr ide_intr #define ide1_intr ide_intr /* entry point for all interrupts when single_threaded==0 */ static void ide_intr (int irq, struct pt_regs *regs) { #if SUPPORT_TWO_INTERFACES byte hwif = (irq != ide_irq[0]); #endif /* SUPPORT_TWO_INTERFACES */ IDE_INTR(HWIF); start_ide_timer(HWIF); } #endif /* OPTIMIZE_IRQS */ #if SUPPORT_SHARING_IRQ /* entry point for all interrupts on ide0/ide1 when sharing_single_irq==1 */ static void ide_shared_intr (int irq, struct pt_regs * regs) { IDE_INTR(current_hwif); start_ide_timer(current_hwif); } #endif /* SUPPORT_SHARING_IRQ */ static ide_dev_t *get_info_ptr (int i_rdev) { unsigned int drive = DEVICE_NR(i_rdev); ide_dev_t *dev; if (drive < MAX_DRIVES) { switch (MAJOR(i_rdev)) { case IDE0_MAJOR: dev = &ide_dev[0][drive]; if (dev->present) return dev; break; case IDE1_MAJOR: dev = &ide_dev[1][drive]; if (dev->present) return dev; break; } } return NULL; } static int ide_open(struct inode * inode, struct file * filp) { ide_dev_t *dev; unsigned long flags; if ((dev = get_info_ptr(inode->i_rdev)) == NULL) return -ENODEV; save_flags(flags); cli(); while (dev->busy) sleep_on(&dev->wqueue); dev->usage++; restore_flags(flags); #ifdef CONFIG_BLK_DEV_IDECD if (dev->type == cdrom) return cdrom_open (inode, filp, dev); #endif /* CONFIG_BLK_DEV_IDECD */ return 0; } /* * Releasing a block device means we sync() it, so that it can safely * be forgotten about... */ static void ide_release(struct inode * inode, struct file * file) { ide_dev_t *dev; if ((dev = get_info_ptr(inode->i_rdev)) != NULL) { sync_dev(inode->i_rdev); dev->usage--; #ifdef CONFIG_BLK_DEV_IDECD if (dev->type == cdrom) cdrom_release (inode, file, dev); #endif /* CONFIG_BLK_DEV_IDECD */ } } /* * This routine is called to flush all partitions and partition tables * for a changed disk, and then re-read the new partition table. * If we are revalidating a disk because of a media change, then we * enter with usage == 0. If we are using an ioctl, we automatically have * usage == 1 (we need an open channel to use an ioctl :-), so this * is our limit. */ static int revalidate_disk(int i_rdev) { unsigned int i, major, start, drive = DEVICE_NR(i_rdev); ide_dev_t *dev; struct gendisk *gd; long flags; if ((dev = get_info_ptr(i_rdev)) == NULL) return -ENODEV; save_flags(flags); cli(); if (dev->busy || (dev->usage > 1)) { restore_flags(flags); return -EBUSY; }; dev->busy = 1; restore_flags(flags); gd = &ide_gendisk[DEV_HWIF]; major = ide_major[DEV_HWIF] << 8; start = drive << PARTN_BITS; for (i = 0; i < (1<part[minor].start_sect = 0; gd->part[minor].nr_sects = 0; }; gd->part[start].nr_sects = ide_capacity[DEV_HWIF][drive]; resetup_one_dev(gd, drive); dev->busy = 0; wake_up(&dev->wqueue); return 0; } #ifdef IDE_DRIVE_CMD /* * This function issues a specific IDE drive command onto the * tail of the request queue, and waits for it to be completed. * If arg is NULL, it goes through all the motions, * but without actually sending a command to the drive. */ static int do_drive_cmd(int dev, char *args) { unsigned long flags; unsigned int major = MAJOR(dev); struct request rq, *cur_rq; struct blk_dev_struct *bdev; struct semaphore sem = MUTEX_LOCKED; /* build up a special request, and add it to the queue */ rq.buffer = args; rq.cmd = IDE_DRIVE_CMD; rq.errors = 0; rq.sector = 0; rq.nr_sectors = 0; rq.current_nr_sectors = 0; rq.sem = &sem; rq.bh = NULL; rq.bhtail = NULL; rq.next = NULL; rq.dev = dev; bdev = &blk_dev[major]; save_flags(flags); cli(); cur_rq = bdev->current_request; if (cur_rq == NULL) { /* empty request list? */ bdev->current_request = &rq; /* service ours immediately */ bdev->request_fn(); } else { while (cur_rq->next != NULL) /* find end of request list */ cur_rq = cur_rq->next; cur_rq->next = &rq; /* add rq to the end */ } down(&sem); /* wait for it to be serviced */ restore_flags(flags); return rq.errors ? -EIO : 0; /* return -EIO if errors */ } #endif /* IDE_DRIVE_CMD */ static int write_fs_long (unsigned long useraddr, long value) { int err; if (NULL == (long *)useraddr) return -EINVAL; if ((err = verify_area(VERIFY_WRITE, (long *)useraddr, sizeof(long)))) return err; put_fs_long((unsigned)value, (long *) useraddr); return 0; } static int ide_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct hd_geometry *loc = (struct hd_geometry *) arg; int err; ide_dev_t *dev; unsigned long flags; if (!inode || !inode->i_rdev) return -EINVAL; if ((dev = get_info_ptr(inode->i_rdev)) == NULL) return -ENODEV; switch (cmd) { case HDIO_GETGEO: if (!loc || dev->type != disk) return -EINVAL; err = verify_area(VERIFY_WRITE, loc, sizeof(*loc)); if (err) return err; put_fs_byte(dev->bios_head, (char *) &loc->heads); put_fs_byte(dev->bios_sect, (char *) &loc->sectors); put_fs_word(dev->bios_cyl, (short *) &loc->cylinders); put_fs_long((unsigned)ide_hd[DEV_HWIF][MINOR(inode->i_rdev)].start_sect, (long *) &loc->start); return 0; case BLKFLSBUF: if(!suser()) return -EACCES; fsync_dev(inode->i_rdev); invalidate_buffers(inode->i_rdev); return 0; case BLKRASET: if(!suser()) return -EACCES; if(arg > 0xff) return -EINVAL; read_ahead[MAJOR(inode->i_rdev)] = arg; return 0; case BLKRAGET: return write_fs_long(arg, read_ahead[MAJOR(inode->i_rdev)]); case BLKGETSIZE: /* Return device size */ return write_fs_long(arg, ide_hd[DEV_HWIF][MINOR(inode->i_rdev)].nr_sects); case BLKRRPART: /* Re-read partition tables */ return revalidate_disk(inode->i_rdev); case HDIO_GET_KEEPSETTINGS: return write_fs_long(arg, dev->keep_settings); case HDIO_GET_UNMASKINTR: return write_fs_long(arg, dev->unmask); case HDIO_GET_MULTCOUNT: return write_fs_long(arg, dev->mult_count); case HDIO_GET_IDENTITY: if (!arg || (MINOR(inode->i_rdev) & PARTN_MASK)) return -EINVAL; if (dev->id == NULL) return -ENOMSG; err = verify_area(VERIFY_WRITE, (char *)arg, sizeof(*dev->id)); if (err) return err; memcpy_tofs((char *)arg, (char *)dev->id, sizeof(*dev->id)); return 0; case HDIO_SET_KEEPSETTINGS: if (!suser()) return -EACCES; if ((arg > 1) || (MINOR(inode->i_rdev) & PARTN_MASK)) return -EINVAL; save_flags(flags); cli(); dev->keep_settings = arg; restore_flags(flags); return 0; case HDIO_SET_UNMASKINTR: if (!suser()) return -EACCES; if ((arg > 1) || (MINOR(inode->i_rdev) & PARTN_MASK)) return -EINVAL; save_flags(flags); cli(); dev->unmask = arg; restore_flags(flags); return 0; case HDIO_SET_MULTCOUNT: if (!suser()) return -EACCES; if (MINOR(inode->i_rdev) & PARTN_MASK) return -EINVAL; if ((dev->id != NULL) && (arg > dev->id->max_multsect)) return -EINVAL; save_flags(flags); cli(); if (dev->special.b.set_multmode) { restore_flags(flags); return -EBUSY; } dev->mult_req = arg; dev->special.b.set_multmode = 1; restore_flags(flags); #ifdef IDE_DRIVE_CMD do_drive_cmd (inode->i_rdev, NULL); return (dev->mult_count == arg) ? 0 : -EIO; #else return 0; #endif /* IDE_DRIVE_CMD */ #ifdef IDE_DRIVE_CMD case HDIO_DRIVE_CMD: { unsigned long args; if (NULL == (long *) arg) err = do_drive_cmd(inode->i_rdev,NULL); else { if (!(err = verify_area(VERIFY_WRITE,(long *)arg,sizeof(long)))) { args = get_fs_long((long *)arg); err = do_drive_cmd(inode->i_rdev,(char *)&args); put_fs_long(args,(long *)arg); } } return err; } #endif /* IDE_DRIVE_CMD */ RO_IOCTLS(inode->i_rdev, arg); default: #ifdef CONFIG_BLK_DEV_IDECD if (dev->type == cdrom) return ide_cdrom_ioctl(dev, inode, file, cmd, arg); #endif /* CONFIG_BLK_DEV_IDECD */ return -EPERM; } } #ifdef CONFIG_BLK_DEV_IDECD static int ide_check_media_change (dev_t full_dev) { ide_dev_t *dev; if ((dev = get_info_ptr(full_dev)) == NULL) return -ENODEV; if (dev->type != cdrom) return 0; return cdrom_check_media_change (dev); } #endif /* CONFIG_BLK_DEV_IDECD */ static void fixstring (byte *s, int bytecount, int byteswap) { byte *p, *end = &s[bytecount &= ~1]; /* bytecount must be even */ if (byteswap) { /* convert from big-endian to little-endian */ for (p = end ; p != s;) { unsigned short *pp = (unsigned short *) (p -= 2); *pp = (*pp >> 8) | (*pp << 8); } } p = s; /* strip leading blanks */ while (s != end && *s == ' ') ++s; /* compress internal blanks and strip trailing blanks */ while (s != end && *s) { if (*s++ != ' ' || (s != end && *s && *s != ' ')) *p++ = *(s-1); } /* wipe out trailing garbage */ while (p != end) *p++ = '\0'; } static int lba_capacity_is_ok (struct hd_driveid *id) /* * Returns: 1 if lba_capacity looks sensible * 0 otherwise */ { unsigned long lba_sects = id->lba_capacity; unsigned long chs_sects = id->cyls * id->heads * id->sectors; unsigned long _10_percent = chs_sects / 10; /* perform a rough sanity check on lba_sects: within 10% is "okay" */ if ((lba_sects - chs_sects) < _10_percent) return 1; /* lba_capacity is good */ /* some drives have the word order reversed */ lba_sects = (lba_sects << 16) | (lba_sects >> 16); if ((lba_sects - chs_sects) < _10_percent) { id->lba_capacity = lba_sects; /* fix it */ return 1; /* lba_capacity is (now) good */ } return 0; /* lba_capacity value is bad */ } static unsigned long probe_mem_start; /* used by drive/irq probing routines */ static void do_identify (ide_dev_t *dev, byte cmd) { int bswap; struct hd_driveid *id; unsigned long capacity, check; id = dev->id = (struct hd_driveid *) probe_mem_start; /* kmalloc() */ probe_mem_start += 512; IN_SECTORS(id,1); /* read 512 bytes of id info */ sti(); /* * WIN_IDENTIFY returns little-endian info, * WIN_PIDENTIFY *usually* returns little-endian info. */ bswap = 1; if (cmd == WIN_PIDENTIFY) { if ((id->model[0] == 'N' && id->model[1] == 'E') || (id->model[0] == 'F' && id->model[1] == 'X')) bswap = 0; /* NEC and *some* Mitsumi units */ } /* Vertos drives may still be weird */ fixstring (id->model, sizeof(id->model), bswap); fixstring (id->fw_rev, sizeof(id->fw_rev), bswap); fixstring (id->serial_no, sizeof(id->serial_no), bswap); /* * Check for an ATAPI device */ if (cmd == WIN_PIDENTIFY) { #ifdef CONFIG_BLK_DEV_IDECD byte type = (id->config >> 8) & 0x0f; #endif /* CONFIG_BLK_DEV_IDECD */ printk("%s: %s, ATAPI,", dev->name, id->model); #ifdef CONFIG_BLK_DEV_IDECD if (type == 0 || type == 5) printk(" CDROM drive\n"); else printk(" UNKNOWN device\n"); dev->type = cdrom; /* until we do it "correctly" above */ dev->present = 1; #else printk(unsupported); #endif /* CONFIG_BLK_DEV_IDECD */ return; } dev->type = disk; /* Extract geometry if we did not already have one for the drive */ if (!dev->present) { dev->present = 1; dev->cyl = dev->bios_cyl = id->cyls; dev->head = dev->bios_head = id->heads; dev->sect = dev->bios_sect = id->sectors; } /* Handle logical geometry translation by the drive */ if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) { /* * Extract the physical drive geometry for our use. * Note that we purposely do *not* update the bios info. * This way, programs that use it (like fdisk) will * still have the same logical view as the BIOS does, * which keeps the partition table from being screwed. * * An exception to this is the cylinder count, * which we reexamine later on to correct for 1024 limitations. */ dev->cyl = id->cur_cyls; dev->head = id->cur_heads; dev->sect = id->cur_sectors; capacity = dev->cyl * dev->head * dev->sect; /* check for word-swapped "capacity" field in id information */ check = (id->cur_capacity0 << 16) | id->cur_capacity1; if (check == capacity) /* was it swapped? */ *((int *)&id->cur_capacity0) = capacity; /* fix it */ } /* Use physical geometry if what we have still makes no sense */ if ((!dev->head || dev->head > 16) && id->heads && id->heads <= 16) { dev->cyl = id->cyls; dev->head = id->heads; dev->sect = id->sectors; } /* Correct the number of cyls if the bios value is too small */ if (dev->sect == dev->bios_sect && dev->head == dev->bios_head) { if (dev->cyl > dev->bios_cyl) dev->bios_cyl = dev->cyl; } /* Determine capacity, and use LBA if the drive properly supports it */ if ((id->capability & 2) && lba_capacity_is_ok(id)) { dev->select.b.lba = 1; capacity = id->lba_capacity; } else { capacity = dev->cyl * dev->head * dev->sect; } ide_capacity[DEV_HWIF][dev->select.b.drive] = capacity; printk ("%s: %.40s, %ldMB w/%dKB Cache, %sCHS=%d/%d/%d", dev->name, id->model, capacity/2048L, id->buf_size/2, dev->select.b.lba ? "LBA, " : "", dev->bios_cyl, dev->bios_head, dev->bios_sect); dev->mult_count = 0; if (id->max_multsect) { dev->mult_req = INITIAL_MULT_COUNT; if (dev->mult_req > id->max_multsect) dev->mult_req = id->max_multsect; if (dev->mult_req || ((id->multsect_valid & 1) && id->multsect)) dev->special.b.set_multmode = 1; printk(", MaxMult=%d", id->max_multsect); } printk("\n"); } static void delay_10ms (void) { unsigned long timer = jiffies + 2; while (timer > jiffies); } static int try_to_identify (ide_dev_t *dev, byte cmd) /* * Returns: 0 device was identified * 1 device timed-out (no response to identify request) * 2 device aborted the command (refused to identify itself) */ { int hd_status, rc; unsigned long timeout; #if PROBE_FOR_IRQS int irqs = 0; static byte irq_probed[2] = {0,0}; #endif /* PROBE_FOR_IRQS */ OUT_BYTE(dev->ctl|2,HD_CMD); /* disable device irq */ #if PROBE_FOR_IRQS if (!irq_probed[DEV_HWIF]) { /* already probed for IRQ? */ irqs = probe_irq_on(); /* start monitoring irqs */ OUT_BYTE(dev->ctl,HD_CMD); /* enable device irq */ } #endif /* PROBE_FOR_IRQS */ delay_10ms(); /* take a deep breath */ if ((IN_BYTE(HD_ALTSTATUS,DEV_HWIF) ^ IN_BYTE(HD_STATUS,DEV_HWIF)) & ~INDEX_STAT) { hd_status = HD_STATUS; /* an ancient Seagate drive */ printk("%s: probing with STATUS instead of ALTSTATUS\n", dev->name); } else hd_status = HD_ALTSTATUS; /* use non-intrusive polling */ OUT_BYTE(cmd,HD_COMMAND); /* ask drive for ID */ timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2; timeout += jiffies; do { if (jiffies > timeout) { #if PROBE_FOR_IRQS if (!irq_probed[DEV_HWIF]) (void) probe_irq_off(irqs); #endif /* PROBE_FOR_IRQS */ return 1; /* drive timed-out */ } delay_10ms(); /* give drive a breather */ } while (IN_BYTE(hd_status,DEV_HWIF) & BUSY_STAT); delay_10ms(); /* wait for IRQ and DRQ_STAT */ if (OK_STAT(GET_STAT(DEV_HWIF),DRQ_STAT,BAD_R_STAT)) { cli(); /* some systems need this */ do_identify(dev, cmd); /* drive returned ID */ rc = 0; /* success */ } else rc = 2; /* drive refused ID */ #if PROBE_FOR_IRQS if (!irq_probed[DEV_HWIF]) { irqs = probe_irq_off(irqs); /* get irq number */ if (irqs > 0) { irq_probed[DEV_HWIF] = 1; ide_irq[DEV_HWIF] = irqs; } else /* Mmmm.. multiple IRQs */ printk("%s: IRQ probe failed (%d)\n", dev->name, irqs); } #endif /* PROBE_FOR_IRQS */ return rc; } /* * This routine has the difficult job of finding a drive if it exists, * without getting hung up if it doesn't exist, without trampling on * ethernet cards, and without leaving any IRQs dangling to haunt us later. * * If a drive is "known" to exist (from CMOS or kernel parameters), * but does not respond right away, the probe will "hang in there" * for the maximum wait time (about 30 seconds), otherwise it will * exit much more quickly. */ static int do_probe (ide_dev_t *dev, byte cmd) /* * Returns: 0 device was identified * 1 device timed-out (no response to identify request) * 2 device aborted the command (refused to identify itself) * 3 bad status from device (possible for ATAPI drives) * 4 probe was not attempted */ { int rc; #ifdef CONFIG_BLK_DEV_IDECD if (dev->present) { /* avoid waiting for inappropriate probes */ if ((dev->type == disk) ^ (cmd == WIN_IDENTIFY)) return 4; } #endif /* CONFIG_BLK_DEV_IDECD */ #if DEBUG printk("probing for %s: present=%d, type=%s, probetype=%s\n", dev->name, dev->present, dev->type ? "cdrom":"disk", (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI"); #endif OUT_BYTE(dev->select.all,HD_CURRENT); /* select target drive */ delay_10ms(); /* wait for BUSY_STAT */ if (IN_BYTE(HD_CURRENT,DEV_HWIF) != dev->select.all && !dev->present) { OUT_BYTE(0xa0,HD_CURRENT); /* exit with drive0 selected */ return 3; /* no i/f present: avoid killing ethernet cards */ } if (OK_STAT(GET_STAT(DEV_HWIF),READY_STAT,BUSY_STAT) || dev->present || cmd == WIN_PIDENTIFY) { if ((rc = try_to_identify(dev, cmd))) /* send cmd and wait */ rc = try_to_identify(dev, cmd); /* failed: try again */ if (rc == 1) printk("%s: no response (status = 0x%02x)\n", dev->name, GET_STAT(DEV_HWIF)); OUT_BYTE(dev->ctl|2,HD_CMD); /* disable device irq */ delay_10ms(); (void) GET_STAT(DEV_HWIF); /* ensure drive irq is clear */ } else { rc = 3; /* not present or maybe ATAPI */ } if (dev->select.b.drive == 1) { OUT_BYTE(0xa0,HD_CURRENT); /* exit with drive0 selected */ delay_10ms(); OUT_BYTE(dev->ctl|2,HD_CMD); /* disable device irq */ delay_10ms(); (void) GET_STAT(DEV_HWIF); /* ensure drive irq is clear */ } return rc; } static byte probe_for_drive (ide_dev_t *dev) /* * Returns: 0 no device was found * 1 device was found (note: dev->present might still be 0) */ { if (dev->dont_probe) /* skip probing? */ return dev->present; if (do_probe(dev, WIN_IDENTIFY) >= 2) { /* if !(success || timed-out) */ #ifdef CONFIG_BLK_DEV_IDECD (void) do_probe(dev, WIN_PIDENTIFY); /* look for ATAPI device */ #endif /* CONFIG_BLK_DEV_IDECD */ } if (!dev->present) return 0; /* drive not found */ if (dev->id == NULL) { /* identification failed? */ if (dev->type == disk) { printk ("%s: non-IDE device, CHS=%d/%d/%d\n", dev->name, dev->cyl, dev->head, dev->sect); } #ifdef CONFIG_BLK_DEV_IDECD else if (dev->type == cdrom) { printk("%s: ATAPI cdrom (?)\n", dev->name); } #endif /* CONFIG_BLK_DEV_IDECD */ else { dev->present = 0; /* nuke it */ return 1; /* drive was found */ } } #ifdef CONFIG_BLK_DEV_IDECD if (dev->type == cdrom) cdrom_setup(dev); #endif /* CONFIG_BLK_DEV_IDECD */ if (dev->type == disk && !dev->select.b.lba) { if (!dev->head || dev->head > 16) { printk("%s: cannot handle disk with %d physical heads\n", dev->name, dev->head); dev->present = 0; } } return 1; /* drive was found */ } static void probe_for_drives (byte hwif) { ide_dev_t *devs = &ide_dev[HWIF][0]; /* for convenience */ if (check_region(IDE_PORT(HD_DATA,HWIF),8) || check_region(IDE_PORT(HD_CMD,HWIF),1)) { if (devs[0].present || devs[1].present) printk("ERROR: "); printk("%s: port(s) already in use\n", ide_name[HWIF]); devs[0].present = 0; devs[1].present = 0; } else { unsigned long flags; save_flags(flags); sti(); /* needed for jiffies and irq probing */ /* second drive should only exist if first drive was found */ if (probe_for_drive(&devs[0]) || devs[1].present) (void) probe_for_drive(&devs[1]); #if PROBE_FOR_IRQS (void) probe_irq_off(probe_irq_on()); /* clear dangling irqs */ #endif /* PROBE_FOR_IRQS */ if (devs[0].present || devs[1].present) { request_region(IDE_PORT(HD_DATA,HWIF),8,ide_name[HWIF]); request_region(IDE_PORT(HD_CMD,HWIF),1,ide_name[HWIF]); } restore_flags(flags); } } static int next_drive = 0; /* used by the ide_setup() routines below */ void ide_setup(char *str, int *ints) { ide_dev_t *dev; const char *p[] = {"cyls","heads","sects","wpcom","irq"}; int i, hwif, drive = next_drive++; #ifdef CONFIG_BLK_DEV_HD extern void hd_setup(char *, int *); if (drive < 2) { hd_setup (str, ints); return; } #endif /* CONFIG_BLK_DEV_HD */ hwif = (drive > 1); printk("%s: ", ide_name[hwif]); if (drive > 3) { printk("too many drives defined\n"); return; } drive = drive & 1; printk("%s: ", ide_devname[hwif][drive]); if (!SUPPORT_TWO_INTERFACES && hwif != HWIF) { printk(unsupported); return; } dev = &ide_dev[hwif][drive]; if (dev->present) printk("(redefined) "); if (ints[0] == 0) { if (!strcmp(str,"noprobe")) { printk("noprobe\n"); dev->dont_probe = 1; /* don't probe for this drive */ return; } #ifdef CONFIG_BLK_DEV_IDECD if (!strcmp(str,"cdrom")) { printk("cdrom\n"); dev->present = 1; /* force autoprobe to find it */ dev->type = cdrom; return; } #endif /* CONFIG_BLK_DEV_IDECD */ } if (ints[0] < 3 || ints[0] > 5) { printk("bad parms, expected: cyls,heads,sects[,wpcom[,irq]]\n"); } else { for (i=0; i++ < ints[0];) printk("%s=%d%c",p[i-1],ints[i],itype = disk; dev->cyl = dev->bios_cyl = ints[1]; dev->head = dev->bios_head = ints[2]; dev->ctl = (ints[2] > 8 ? 8 : 0); dev->sect = dev->bios_sect = ints[3]; dev->wpcom = (ints[0] >= 4) ? ints[4] : 0; if (ints[0] >= 5) ide_irq[HWIF] = ints[5]; ide_capacity[HWIF][drive] = BIOS_SECTORS(dev); dev->present = 1; } } void hda_setup(char *str, int *ints) { next_drive = 0; ide_setup (str, ints); } void hdb_setup(char *str, int *ints) { next_drive = 1; ide_setup (str, ints); } void hdc_setup(char *str, int *ints) { next_drive = 2; ide_setup (str, ints); } void hdd_setup(char *str, int *ints) { next_drive = 3; ide_setup (str, ints); } #ifndef CONFIG_BLK_DEV_HD /* * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc * controller that is BIOS compatible with ST-506, and thus showing up in our * BIOS table, but not register compatible, and therefore not present in CMOS. * * Furthermore, we will assume that our ST-506 drives are the primary * drives in the system -- the ones reflected as drive 1 or 2. The first * drive is stored in the high nibble of CMOS byte 0x12, the second in the low * nibble. This will be either a 4 bit drive type or 0xf indicating use byte * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value * means we have an AT controller hard disk for that drive. */ extern struct drive_info_struct drive_info; static void probe_cmos_for_drives (void) { byte drive, cmos_disks, *BIOS = (byte *) &drive_info; outb_p(0x12,0x70); /* specify CMOS address 0x12 */ cmos_disks = inb_p(0x71); /* read the data from 0x12 */ /* Extract drive geometry from CMOS+BIOS if not already setup */ for (drive = 0; drive < MAX_DRIVES; drive++) { ide_dev_t *dev = &ide_dev[0][drive]; if ((cmos_disks & (0xf0 >> (drive*4))) && !dev->present) { dev->cyl = dev->bios_cyl = *(unsigned short *)BIOS; dev->head = dev->bios_head = * (BIOS+2); dev->sect = dev->bios_sect = * (BIOS+14); dev->wpcom = (*(unsigned short *)(BIOS+5))>>2; dev->ctl = *(BIOS+8); dev->wpcom = 0; dev->type = disk; dev->present = 1; ide_capacity[0][drive] = BIOS_SECTORS(dev); } BIOS += 16; } } #endif /* CONFIG_BLK_DEV_HD */ static void init_ide_data (byte hwif) { int drive; for (drive = 0; drive < (MAX_DRIVES<select.all = (drive<<4)|0xa0; dev->hwif = hwif; dev->unmask = 0; dev->busy = 0; dev->mult_count = 0; /* set by do_identify() */ dev->mult_req = 0; /* set by do_identify() */ dev->usage = 0; dev->id = NULL; dev->ctl = 0x08; dev->wqueue = NULL; dev->special.all = 0; dev->special.b.recalibrate = 1; dev->special.b.set_geometry = 1; dev->keep_settings = 0; ide_hd[hwif][drive<name = ide_devname[hwif][drive]; } } /* * This is the harddisk IRQ description. The SA_INTERRUPT in sa_flags * means we enter the IRQ-handler with interrupts disabled: this is bad for * interrupt latency, but anything else has led to problems on some * machines. We enable interrupts as much as we can safely do in most places. */ static byte setup_irq (byte hwif) { static byte rc = 0; unsigned long flags; const char *msg = "", *primary_secondary[] = {"primary", "secondary"}; void (*handler)(int, struct pt_regs *) = HWIF ? &ide1_intr : &ide0_intr; #if SUPPORT_SHARING_IRQ if (sharing_single_irq) { if (HWIF != 0 && !rc) { /* IRQ already allocated? */ msg = " (shared with ide0)"; goto done; } handler = &ide_shared_intr; } #if SINGLE_THREADED else if (single_threaded) { handler = &ide_seq_intr; if (HWIF != 0) msg = " (single-threaded with ide0)"; } #endif /* SINGLE_THREADED */ #endif /* SUPPORT_SHARING_IRQ */ save_flags(flags); cli(); if ((rc = request_irq(ide_irq[HWIF],handler,SA_INTERRUPT,ide_name[HWIF]))) msg = ": FAILED! unable to allocate IRQ"; restore_flags(flags); #if SUPPORT_SHARING_IRQ done: #endif /* SUPPORT_SHARING_IRQ */ printk("%s: %s interface on irq %d%s\n", ide_name[HWIF], primary_secondary[HWIF], ide_irq[HWIF], msg); return rc; } static void ide_geninit(byte hwif) { static int drive; for (drive = 0; drive < MAX_DRIVES; drive++) { ide_dev_t *dev = &ide_dev[HWIF][drive]; if (dev->present) { ide_hd[HWIF][drive<type == cdrom) ide_hd[HWIF][drive< 0;) { if (ide_gendisk[hwif].nr_real != 0 && !setup_irq(hwif)) { const char *name = ide_name[HWIF]; unsigned int major = ide_major[HWIF]; if (register_blkdev(major, name, &ide_fops)) { printk("%s: unable to get major number %d\n", name, major); } else { timer_table[ide_timer[HWIF]].fn = HWIF ? ide1_timer_expiry : ide0_timer_expiry; #if SUPPORT_SHARING_IRQ if (single_threaded) blk_dev[major].request_fn = &do_shared_request; else #endif /* SUPPORT_SHARING_IRQ */ blk_dev[major].request_fn = HWIF ? &do_ide1_request : &do_ide0_request; read_ahead[major] = 8; /* (4kB) */ ide_gendisk[HWIF].next = gendisk_head; gendisk_head = &ide_gendisk[HWIF]; } } } return mem_start; }