diff options
Diffstat (limited to 'drivers/block/ide.c')
-rw-r--r-- | drivers/block/ide.c | 252 |
1 files changed, 143 insertions, 109 deletions
diff --git a/drivers/block/ide.c b/drivers/block/ide.c index 50af139cd..3b9a17283 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide.c Version 6.05 November 30, 1997 + * linux/drivers/block/ide.c Version 6.11 December 5, 1997 * * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) */ @@ -95,6 +95,9 @@ * added support for BIOS-enabled UltraDMA * rename all "promise" things to "pdc4030" * fix EZ-DRIVE handling on small disks + * Version 6.11 fix probe error in ide_scan_devices() + * fix ancient "jiffies" polling bugs + * mask all hwgroup interrupts on each irq entry * * Some additional driver compile-time options are in ide.h * @@ -111,18 +114,14 @@ #include <linux/kernel.h> #include <linux/timer.h> #include <linux/mm.h> -#include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/major.h> -#include <linux/blkdev.h> #include <linux/errno.h> -#include <linux/hdreg.h> #include <linux/genhd.h> #include <linux/malloc.h> #include <linux/bios32.h> #include <linux/pci.h> #include <linux/delay.h> -#include <linux/init.h> #include <asm/byteorder.h> #include <asm/irq.h> @@ -312,11 +311,12 @@ void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount) if (io_32bit) { #if SUPPORT_VLB_SYNC if (io_32bit & 2) { + unsigned long flags; + save_flags(flags); cli(); do_vlb_sync(IDE_NSECTOR_REG); insl(IDE_DATA_REG, buffer, wcount); - if (drive->unmask) - sti(); + restore_flags(flags); } else #endif /* SUPPORT_VLB_SYNC */ insl(IDE_DATA_REG, buffer, wcount); @@ -344,11 +344,12 @@ void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount) if (io_32bit) { #if SUPPORT_VLB_SYNC if (io_32bit & 2) { + unsigned long flags; + save_flags(flags); cli(); do_vlb_sync(IDE_NSECTOR_REG); outsl(IDE_DATA_REG, buffer, wcount); - if (drive->unmask) - sti(); + restore_flags(flags); } else #endif /* SUPPORT_VLB_SYNC */ outsl(IDE_DATA_REG, buffer, wcount); @@ -469,13 +470,13 @@ static void atapi_reset_pollfunc (ide_drive_t *drive) ide_hwgroup_t *hwgroup = HWGROUP(drive); byte stat; - OUT_BYTE (drive->select.all, IDE_SELECT_REG); + SELECT_DRIVE(HWIF(drive),drive); udelay (10); if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) { printk("%s: ATAPI reset complete\n", drive->name); } else { - if (jiffies < hwgroup->poll_timeout) { + if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) { ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20); return; /* continue polling */ } @@ -500,7 +501,7 @@ static void reset_pollfunc (ide_drive_t *drive) byte tmp; if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) { - if (jiffies < hwgroup->poll_timeout) { + if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) { ide_set_handler (drive, &reset_pollfunc, HZ/20); return; /* continue polling */ } @@ -542,7 +543,7 @@ static void pre_reset (ide_drive_t *drive) drive->unmask = 0; drive->io_32bit = 0; if (drive->using_dma) - HWIF(drive)->dmaproc(ide_dma_off, drive); + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); } if (drive->driver != NULL) DRIVER(drive)->pre_reset(drive); @@ -576,7 +577,7 @@ static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi) /* For an ATAPI device, first try an ATAPI SRST. */ if (drive->media != ide_disk && !do_not_try_atapi) { pre_reset(drive); - OUT_BYTE (drive->select.all, IDE_SELECT_REG); + SELECT_DRIVE(hwif,drive); udelay (20); OUT_BYTE (WIN_SRST, IDE_COMMAND_REG); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; @@ -808,7 +809,7 @@ static void drive_cmd_intr (ide_drive_t *drive) { struct request *rq = HWGROUP(drive)->rq; byte *args = (byte *) rq->buffer; - byte stat = GET_STAT(); + byte test, stat = GET_STAT(); ide_sti(); if ((stat & DRQ_STAT) && args && args[3]) { @@ -818,7 +819,10 @@ static void drive_cmd_intr (ide_drive_t *drive) drive->io_32bit = io_32bit; stat = GET_STAT(); } - if (OK_STAT(stat,READY_STAT,BAD_STAT)) + test = stat; + if (drive->media == ide_cdrom) + test = stat &~BUSY_STAT; + if (OK_STAT(test,READY_STAT,BAD_STAT)) ide_end_drive_cmd (drive, stat, GET_ERR()); else ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */ @@ -870,7 +874,7 @@ int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeou ide_sti(); timeout += jiffies; while ((stat = GET_STAT()) & BUSY_STAT) { - if (jiffies > timeout) { + if (0 < (signed long)(jiffies - timeout)) { restore_flags(flags); ide_error(drive, "status timeout", stat); return 1; @@ -1007,7 +1011,7 @@ repeat: do { if (!drive->queue) continue; - if (drive->sleep && drive->sleep > jiffies) + if (drive->sleep && 0 < (signed long)(drive->sleep - jiffies)) continue; if (!best) { best = drive; @@ -1019,7 +1023,7 @@ repeat: best = drive; } while ((drive = drive->next) != hwgroup->drive); if (best != hwgroup->drive && best && best->service_time > WAIT_MIN_SLEEP && !best->sleep && best->nice1) { - long t = (signed) (WAKEUP(best) - jiffies); + long t = (signed) (WAKEUP(best) - jiffies); /* BUGGY? */ if (t >= WAIT_MIN_SLEEP) { /* * We *may* have some time to spare, but first let's see if @@ -1029,7 +1033,7 @@ repeat: do { if (drive->sleep) /* this drive tried to be nice to us */ continue; - if (WAKEUP(drive) > jiffies - best->service_time && WAKEUP(drive) < jiffies + t) { + if (WAKEUP(drive) > (jiffies - best->service_time) && WAKEUP(drive) < (jiffies + t)) { /* BUGGY? */ ide_stall_queue(best, IDE_MIN(t, 10 * WAIT_MIN_SLEEP)); goto repeat; } @@ -1057,7 +1061,7 @@ static inline void ide_leave_hwgroup (ide_hwgroup_t *hwgroup) sleep = drive->sleep; } while ((drive = drive->next) != hwgroup->drive); if (sleep) { - if (sleep < jiffies + WAIT_MIN_SLEEP) + if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep)) sleep = jiffies + WAIT_MIN_SLEEP; hwgroup->timer.expires = sleep; add_timer(&hwgroup->timer); @@ -1189,7 +1193,7 @@ void ide_timer_expiry (unsigned long data) handler(drive); else { /* abort the operation */ if (hwgroup->hwif->dmaproc) - (void) hwgroup->hwif->dmaproc (ide_dma_abort, drive); + (void) hwgroup->hwif->dmaproc (ide_dma_end, drive); ide_error(drive, "irq timeout", GET_STAT()); } cli(); @@ -1229,7 +1233,6 @@ void ide_timer_expiry (unsigned long data) static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) { byte stat; - unsigned int unit; ide_hwif_t *hwif = hwgroup->hwif; /* @@ -1237,27 +1240,18 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) */ do { if (hwif->irq == irq) { - for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = &hwif->drives[unit]; - if (!drive->present) - continue; - SELECT_DRIVE(hwif,drive); - udelay(100); /* Ugly, but wait_stat() may not be safe here */ - if (!OK_STAT(stat=GET_STAT(), drive->ready_stat, BAD_STAT)) { - /* Try to not flood the console with msgs */ - static unsigned long last_msgtime = 0; - if ((last_msgtime + (HZ/2)) < jiffies) { - last_msgtime = jiffies; - (void) ide_dump_status(drive, "unexpected_intr", stat); - } + stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); + if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { + /* Try to not flood the console with msgs */ + static unsigned long last_msgtime = 0; + if (0 < (signed long)(jiffies - (last_msgtime + HZ))) { + last_msgtime = jiffies; + printk("%s%s: unexpected interrupt, status=0x%02x\n", + hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat); } - if ((stat & DRQ_STAT)) - try_to_flush_leftover_data(drive); } } } while ((hwif = hwif->next) != hwgroup->hwif); - SELECT_DRIVE(hwif,hwgroup->drive); /* Ugh.. probably interrupts current I/O */ - udelay(100); /* Ugly, but wait_stat() may not be safe here */ } /* @@ -1266,14 +1260,28 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) void ide_intr (int irq, void *dev_id, struct pt_regs *regs) { ide_hwgroup_t *hwgroup = dev_id; + ide_hwif_t *hwif = hwgroup->hwif; ide_handler_t *handler; - if (!ide_ack_intr (hwgroup->hwif->io_ports[IDE_STATUS_OFFSET], - hwgroup->hwif->io_ports[IDE_IRQ_OFFSET])) + if (!ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET])) return; - - if (irq == hwgroup->hwif->irq && (handler = hwgroup->handler) != NULL) { + do { + if (hwif->irq != irq) disable_irq(hwif->irq); + } while ((hwif = hwif->next) != hwgroup->hwif); + if (irq == hwif->irq && (handler = hwgroup->handler) != NULL) { ide_drive_t *drive = hwgroup->drive; +#if 1 /* temporary, remove later -- FIXME */ + { + struct request *rq = hwgroup->rq; + if (rq != NULL + &&( MAJOR(rq->rq_dev) != HWIF(drive)->major + || (MINOR(rq->rq_dev) >> PARTN_BITS) != drive->select.b.unit)) + { + printk("ide_intr: got IRQ from wrong device: email mlord@pobox.com!!\n"); + return; + } + } +#endif /* temporary */ hwgroup->handler = NULL; del_timer(&(hwgroup->timer)); if (drive->unmask) @@ -1289,6 +1297,10 @@ void ide_intr (int irq, void *dev_id, struct pt_regs *regs) unexpected_intr(irq, hwgroup); } cli(); + hwif = hwgroup->hwif; + do { + if (hwif->irq != irq) enable_irq(hwif->irq); + } while ((hwif = hwif->next) != hwgroup->hwif); } /* @@ -1627,6 +1639,7 @@ void ide_unregister (unsigned int index) */ unregister_blkdev(hwif->major, hwif->name); kfree(blksize_size[hwif->major]); + kfree(max_sectors[hwif->major]); blk_dev[hwif->major].request_fn = NULL; blk_dev[hwif->major].data = NULL; blk_dev[hwif->major].queue = NULL; @@ -1686,13 +1699,15 @@ found: static int ide_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int err; + int err, major, minor; ide_drive_t *drive; unsigned long flags; struct request rq; + kdev_t dev; - if (!inode || !(inode->i_rdev)) + if (!inode || !(dev = inode->i_rdev)) return -EINVAL; + major = MAJOR(dev); minor = MINOR(dev); if ((drive = get_info_ptr(inode->i_rdev)) == NULL) return -ENODEV; ide_init_drive_cmd (&rq); @@ -1726,6 +1741,23 @@ static int ide_ioctl (struct inode *inode, struct file *file, case BLKGETSIZE: /* Return device size */ return put_user(drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects, (long *) arg); + case BLKFRASET: + if (!suser()) return -EACCES; + max_readahead[major][minor] = arg; + return 0; + + case BLKFRAGET: + return put_user(max_readahead[major][minor], (long *) arg); + + case BLKSECTSET: + if (!suser()) return -EACCES; + if (!arg || arg > 0xff) return -EINVAL; + max_sectors[major][minor] = arg; + return 0; + + case BLKSECTGET: + return put_user(max_sectors[major][minor], (long *) arg); + case BLKRRPART: /* Re-read partition tables */ if (!suser()) return -EACCES; return ide_revalidate_disk(inode->i_rdev); @@ -2074,6 +2106,9 @@ __initfunc(static int match_parm (char *s, const char *keywords[], int vals[], i * This is the default for most chipsets, * except the cmd640. * "idex=serialize" : do not overlap operations on idex and ide(x^1) + * "idex=four" : four drives on idex and ide(x^1) share same ports + * "idex=reset" : reset interface before first use + * "idex=nodma" : do not enable DMA by default on either drive * * The following are valid ONLY on ide0, * and the defaults for the base,ctl ports must not be altered. @@ -2175,8 +2210,8 @@ __initfunc(void ide_setup (char *s)) /* * Be VERY CAREFUL changing this: note hardcoded indexes below */ - const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune", "qd6580", - "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", "four", "reset", NULL}; + const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune", "reset", "nodma", "four", + "qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL}; hw = s[3] - '0'; hwif = &ide_hwifs[hw]; i = match_parm(&s[4], ide_words, vals, 3); @@ -2184,42 +2219,19 @@ __initfunc(void ide_setup (char *s)) /* * Cryptic check to ensure chipset not already set for hwif: */ - if (i > 0 || (i <= -5 && i != -13)) { + if (i > 0 || i <= -7) { /* is parameter a chipset name? */ if (hwif->chipset != ide_unknown) - goto bad_option; - if (i <= -5) { - if (ide_hwifs[1].chipset != ide_unknown) - goto bad_option; - /* - * Interface keywords work only for ide0: - */ - if (hw != 0) - goto bad_hwif; - printk("\n"); - } + goto bad_option; /* chipset already specified */ + if (i != -7 && hw != 0) + goto bad_hwif; /* chipset drivers are for "ide0=" only */ + if (ide_hwifs[hw^1].chipset != ide_unknown) + goto bad_option; /* chipset for 2nd port already specified */ + printk("\n"); } switch (i) { - case -13: /* "reset" */ - hwif->reset = 1; - goto done; -#ifdef CONFIG_BLK_DEV_4DRIVES - case -12: /* "four" drives on one set of ports */ - { - ide_hwif_t *mate = &ide_hwifs[hw^1]; - mate->mate = hwif; - hwif->mate = mate; - hwif->chipset = mate->chipset = ide_4drives; - hwif->serialized = mate->serialized = 1; - mate->drives[0].select.all ^= 0x20; - mate->drives[1].select.all ^= 0x20; - memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports)); - mate->irq = hwif->irq; - goto done; - } -#endif /* CONFIG_BLK_DEV_4DRIVES */ #ifdef CONFIG_BLK_DEV_PDC4030 - case -11: /* "dc4030" */ + case -14: /* "dc4030" */ { extern void setup_pdc4030(ide_hwif_t *); setup_pdc4030(hwif); @@ -2227,7 +2239,7 @@ __initfunc(void ide_setup (char *s)) } #endif /* CONFIG_BLK_DEV_PDC4030 */ #ifdef CONFIG_BLK_DEV_ALI14XX - case -10: /* "ali14xx" */ + case -13: /* "ali14xx" */ { extern void init_ali14xx (void); init_ali14xx(); @@ -2235,7 +2247,7 @@ __initfunc(void ide_setup (char *s)) } #endif /* CONFIG_BLK_DEV_ALI14XX */ #ifdef CONFIG_BLK_DEV_UMC8672 - case -9: /* "umc8672" */ + case -12: /* "umc8672" */ { extern void init_umc8672 (void); init_umc8672(); @@ -2243,7 +2255,7 @@ __initfunc(void ide_setup (char *s)) } #endif /* CONFIG_BLK_DEV_UMC8672 */ #ifdef CONFIG_BLK_DEV_DTC2278 - case -8: /* "dtc2278" */ + case -11: /* "dtc2278" */ { extern void init_dtc2278 (void); init_dtc2278(); @@ -2251,7 +2263,7 @@ __initfunc(void ide_setup (char *s)) } #endif /* CONFIG_BLK_DEV_DTC2278 */ #ifdef CONFIG_BLK_DEV_CMD640 - case -7: /* "cmd640_vlb" */ + case -10: /* "cmd640_vlb" */ { extern int cmd640_vlb; /* flag for cmd640.c */ cmd640_vlb = 1; @@ -2259,7 +2271,7 @@ __initfunc(void ide_setup (char *s)) } #endif /* CONFIG_BLK_DEV_CMD640 */ #ifdef CONFIG_BLK_DEV_HT6560B - case -6: /* "ht6560b" */ + case -9: /* "ht6560b" */ { extern void init_ht6560b (void); init_ht6560b(); @@ -2267,13 +2279,31 @@ __initfunc(void ide_setup (char *s)) } #endif /* CONFIG_BLK_DEV_HT6560B */ #if CONFIG_BLK_DEV_QD6580 - case -5: /* "qd6580" (has secondary i/f) */ + case -8: /* "qd6580" */ { extern void init_qd6580 (void); init_qd6580(); goto done; } #endif /* CONFIG_BLK_DEV_QD6580 */ +#ifdef CONFIG_BLK_DEV_4DRIVES + case -7: /* "four" drives on one set of ports */ + { + ide_hwif_t *mate = &ide_hwifs[hw^1]; + mate->drives[0].select.all ^= 0x20; + mate->drives[1].select.all ^= 0x20; + hwif->chipset = mate->chipset = ide_4drives; + mate->irq = hwif->irq; + memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports)); + goto do_serialize; + } +#endif /* CONFIG_BLK_DEV_4DRIVES */ + case -6: /* nodma */ + hwif->no_autodma = 1; + goto done; + case -5: /* "reset" */ + hwif->reset = 1; + goto done; case -4: /* "noautotune" */ hwif->drives[0].autotune = 2; hwif->drives[1].autotune = 2; @@ -2284,8 +2314,9 @@ __initfunc(void ide_setup (char *s)) goto done; case -2: /* "serialize" */ do_serialize: - ide_hwifs[hw].serialized = 1; /* serialize */ - ide_hwifs[hw^1].serialized = 1; /* with mate */ + hwif->mate = &ide_hwifs[hw^1]; + hwif->mate->mate = hwif; + hwif->serialized = hwif->mate->serialized = 1; goto done; case -1: /* "noprobe" */ @@ -2395,44 +2426,37 @@ int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg) /* * probe_for_hwifs() finds/initializes "known" IDE interfaces - * - * This routine should ideally be using pcibios_find_class() to find all - * PCI IDE interfaces, but that function causes some systems to "go weird". */ __initfunc(static void probe_for_hwifs (void)) { #ifdef CONFIG_PCI - /* - * Find/initialize PCI IDE interfaces - */ if (pcibios_present()) { -#ifdef CONFIG_BLK_DEV_IDEDMA - { - extern void ide_scan_pcibus(void); - ide_scan_pcibus(); - } -#endif +#ifdef CONFIG_BLK_DEV_IDEPCI + ide_scan_pcibus(); +#else #ifdef CONFIG_BLK_DEV_RZ1000 { extern void ide_probe_for_rz100x(void); ide_probe_for_rz100x(); } -#endif +#endif /* CONFIG_BLK_DEV_RZ1000 */ +#endif /* CONFIG_BLK_DEV_IDEPCI */ } -#endif /* CONFIG_PCI */ +#endif /* CONFIG_PCI */ + #ifdef CONFIG_BLK_DEV_CMD640 { extern void ide_probe_for_cmd640x(void); ide_probe_for_cmd640x(); } -#endif +#endif /* CONFIG_BLK_DEV_CMD640 */ #ifdef CONFIG_BLK_DEV_PDC4030 { extern int init_pdc4030(void); (void) init_pdc4030(); } -#endif +#endif /* CONFIG_BLK_DEV_PDC4030 */ } __initfunc(void ide_init_builtin_drivers (void)) @@ -2460,6 +2484,10 @@ __initfunc(void ide_init_builtin_drivers (void)) #endif /* __mc68000__ */ #endif /* CONFIG_BLK_DEV_IDE */ +#ifdef CONFIG_PROC_FS + proc_ide_init(); +#endif + /* * Attempt to match drivers for the available drives */ @@ -2552,18 +2580,19 @@ static void setup_driver_defaults (ide_drive_t *drive) ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n) { unsigned int unit, index, i; - ide_drive_t *drive; for (index = 0; index < MAX_HWIFS; ++index) if (ide_hwifs[index].present) goto search; ide_init_module(IDE_PROBE_MODULE); search: for (index = 0, i = 0; index < MAX_HWIFS; ++index) { - for (unit = 0; unit < MAX_DRIVES; ++unit) { - drive = &ide_hwifs[index].drives[unit]; - if (drive->present && drive->media == media && - drive->driver == driver && ++i > n) - return drive; + ide_hwif_t *hwif = &ide_hwifs[index]; + if (hwif->present) { + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + if (drive->present && drive->media == media && drive->driver == driver && ++i > n) + return drive; + } } } return NULL; @@ -2590,6 +2619,7 @@ int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int versio drive->nice1 = 1; } drive->revalidate = 1; + ide_add_proc_entries(drive, driver->proc); return 0; } @@ -2603,6 +2633,7 @@ int ide_unregister_subdriver (ide_drive_t *drive) restore_flags(flags); return 1; } + ide_remove_proc_entries(drive, DRIVER(drive)->proc); drive->driver = NULL; restore_flags(flags); return 0; @@ -2694,6 +2725,9 @@ EXPORT_SYMBOL(ide_end_request); EXPORT_SYMBOL(ide_revalidate_disk); EXPORT_SYMBOL(ide_cmd); EXPORT_SYMBOL(ide_stall_queue); +EXPORT_SYMBOL(ide_add_proc_entries); +EXPORT_SYMBOL(ide_remove_proc_entries); +EXPORT_SYMBOL(proc_ide_read_geometry); EXPORT_SYMBOL(ide_register); EXPORT_SYMBOL(ide_unregister); |