This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / ide / ide.c
index 467b734..46855a9 100644 (file)
 #include <linux/cdrom.h>
 #include <linux/seq_file.h>
 #include <linux/device.h>
-#include <linux/bitops.h>
 
 #include <asm/byteorder.h>
 #include <asm/irq.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
+#include <asm/bitops.h>
 
 
 /* default maximum number of failures */
@@ -175,6 +175,7 @@ static int system_bus_speed;        /* holds what we think is VESA/PCI bus speed */
 static int initializing;       /* set while initializing built-in drivers */
 
 DECLARE_MUTEX(ide_cfg_sem);
+EXPORT_SYMBOL_GPL(ide_cfg_sem);
 spinlock_t ide_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 
 #ifdef CONFIG_BLK_DEV_IDEPCI
@@ -205,11 +206,13 @@ static void setup_driver_defaults(ide_driver_t *driver);
 static void init_hwif_data(ide_hwif_t *hwif, unsigned int index)
 {
        unsigned int unit;
+       unsigned int key = hwif->key;
 
        /* bulk initialize hwif & drive info with zeros */
        memset(hwif, 0, sizeof(ide_hwif_t));
 
        /* fill in any non-zero initial values */
+       hwif->key       = key + 1;      /* Protected by ide_cfg_sem */
        hwif->index     = index;
        hwif->major     = ide_hwif_to_major[index];
 
@@ -320,6 +323,92 @@ static void __init init_ide_data (void)
 #endif
 }
 
+/*
+ *     ide_drive_from_key      -       turn key into drive
+ *     @kval: persistent key
+ *
+ *     Convert a key into a drive. Currently the key is packed as
+ *     [keyval] << 16 | hwif << 8 | drive_num. Caller must hold 
+ *     ide_settings_sem for the duration of the returned reference
+ */
+
+ide_drive_t *ide_drive_from_key(void *kval)
+{
+       unsigned long key = (unsigned long) kval;
+       int idx = (key >> 8) & 0xFF;
+       int drive = key & 3;
+       ide_hwif_t *hwif = &ide_hwifs[idx];
+       ide_drive_t *ret;
+
+       key >>= 16;
+
+       if(hwif->configured == 0 || hwif->present == 0 || hwif->drives[drive].dead || hwif->key != key)
+               ret = NULL;
+       else
+               ret = &ide_hwifs[idx].drives[drive];
+
+       return ret;
+}
+
+EXPORT_SYMBOL_GPL(ide_drive_from_key);
+
+/*
+ *     ide_drive_to_key        -       turn drive to persistent key
+ *     @drive: drive to use
+ *
+ *     Convert drive into a key. Currently the key is packed as
+ *     [keyval] << 16 | hwif << 8 | drive_num. Caller must hold 
+ *     ide_settings_sem for the duration of the returned reference
+ */
+
+void *ide_drive_to_key(ide_drive_t *drive)
+{
+       ide_hwif_t *hwif = HWIF(drive);
+       unsigned long val;
+
+       val = (hwif->index << 8) | (hwif->key << 16) | drive->select.b.unit;
+       return (void *)val;
+}
+
+/*
+ *     ide_hwif_from_key       -       turn key into hwif
+ *     @kval: persistent key
+ *
+ *     Convert a key into a drive. Currently the key is packed as
+ *     [keyval] << 16 | hwif << 8 | drive_num. Caller must hold 
+ *     ide_settings_sem for the duration of the returned reference
+ */
+
+ide_hwif_t *ide_hwif_from_key(void *kval)
+{
+       unsigned long key = (unsigned long) kval;
+       int idx = (key >> 8) & 0xFF;
+       ide_hwif_t *hwif = &ide_hwifs[idx];
+
+       key >>= 16;
+
+       if(hwif->configured == 0 || hwif->present == 0 || hwif->key != key)
+               return NULL;
+       return hwif;
+}
+
+/*
+ *     ide_hwif_to_key -       turn drive to persistent key
+ *     @hwif: hwif to use
+ *
+ *     Convert drive into a key. Currently the key is packed as
+ *     [keyval] << 16 | hwif << 8 | drive_num. Caller must hold 
+ *     ide_settings_sem for the duration of the returned reference
+ */
+
+void *ide_hwif_to_key(ide_hwif_t *hwif)
+{
+       unsigned long val;
+
+       val = (hwif->index << 8) | (hwif->key << 16);
+       return (void *)val;
+}
+
 /**
  *     ide_system_bus_speed    -       guess bus speed
  *
@@ -335,16 +424,11 @@ static void __init init_ide_data (void)
 
 int ide_system_bus_speed (void)
 {
-       static struct pci_device_id pci_default[] = {
-               { PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID) },
-               { }
-       };
-
        if (!system_bus_speed) {
                if (idebus_parameter) {
                        /* user supplied value */
                        system_bus_speed = idebus_parameter;
-               } else if (pci_dev_present(pci_default)) {
+               } else if (pci_find_device(PCI_ANY_ID, PCI_ANY_ID, NULL) != NULL) {
                        /* safe default value for PCI */
                        system_bus_speed = 33;
                } else {
@@ -495,11 +579,16 @@ static int ide_open (struct inode * inode, struct file * filp)
 }
 
 /*
- *     drives_lock protects the list of drives, drivers_lock the
- *     list of drivers.  Currently nobody takes both at once.
+ *     drives_lock protects the list of drives, drivers lock the
+ *     list of drivers. Currently nobody takes both at once.
+ *     drivers_sem guards the drivers_list for readers that may
+ *     sleep. It must be taken before drivers_lock. Take drivers_sem
+ *     before ide_setting_sem and idecfg_sem before either of the
+ *     others.
  */
 
 static spinlock_t drives_lock = SPIN_LOCK_UNLOCKED;
+static DECLARE_MUTEX(drivers_sem);
 static spinlock_t drivers_lock = SPIN_LOCK_UNLOCKED;
 static LIST_HEAD(drivers);
 
@@ -509,7 +598,7 @@ static void *m_start(struct seq_file *m, loff_t *pos)
 {
        struct list_head *p;
        loff_t l = *pos;
-       spin_lock(&drivers_lock);
+       down(&drivers_sem);
        list_for_each(p, &drivers)
                if (!l--)
                        return list_entry(p, ide_driver_t, drivers);
@@ -525,7 +614,7 @@ static void *m_next(struct seq_file *m, void *v, loff_t *pos)
 
 static void m_stop(struct seq_file *m, void *v)
 {
-       spin_unlock(&drivers_lock);
+       up(&drivers_sem);
 }
 
 static int show_driver(struct seq_file *m, void *v)
@@ -673,6 +762,7 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif)
        hwif->cds                       = tmp_hwif->cds;
 #endif
 
+       hwif->identify                  = tmp_hwif->identify;
        hwif->tuneproc                  = tmp_hwif->tuneproc;
        hwif->speedproc                 = tmp_hwif->speedproc;
        hwif->selectproc                = tmp_hwif->selectproc;
@@ -683,15 +773,16 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif)
        hwif->maskproc                  = tmp_hwif->maskproc;
        hwif->quirkproc                 = tmp_hwif->quirkproc;
        hwif->busproc                   = tmp_hwif->busproc;
+       hwif->raw_taskfile              = tmp_hwif->raw_taskfile;
 
        hwif->ata_input_data            = tmp_hwif->ata_input_data;
        hwif->ata_output_data           = tmp_hwif->ata_output_data;
        hwif->atapi_input_bytes         = tmp_hwif->atapi_input_bytes;
        hwif->atapi_output_bytes        = tmp_hwif->atapi_output_bytes;
 
-       hwif->dma_setup                 = tmp_hwif->dma_setup;
-       hwif->dma_exec_cmd              = tmp_hwif->dma_exec_cmd;
-       hwif->dma_start                 = tmp_hwif->dma_start;
+       hwif->ide_dma_read              = tmp_hwif->ide_dma_read;
+       hwif->ide_dma_write             = tmp_hwif->ide_dma_write;
+       hwif->ide_dma_begin             = tmp_hwif->ide_dma_begin;
        hwif->ide_dma_end               = tmp_hwif->ide_dma_end;
        hwif->ide_dma_check             = tmp_hwif->ide_dma_check;
        hwif->ide_dma_on                = tmp_hwif->ide_dma_on;
@@ -699,6 +790,7 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif)
        hwif->ide_dma_test_irq          = tmp_hwif->ide_dma_test_irq;
        hwif->ide_dma_host_on           = tmp_hwif->ide_dma_host_on;
        hwif->ide_dma_host_off          = tmp_hwif->ide_dma_host_off;
+       hwif->ide_dma_verbose           = tmp_hwif->ide_dma_verbose;
        hwif->ide_dma_lostirq           = tmp_hwif->ide_dma_lostirq;
        hwif->ide_dma_timeout           = tmp_hwif->ide_dma_timeout;
 
@@ -715,8 +807,6 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif)
        hwif->INSW                      = tmp_hwif->INSW;
        hwif->INSL                      = tmp_hwif->INSL;
 
-       hwif->sg_max_nents              = tmp_hwif->sg_max_nents;
-
        hwif->mmio                      = tmp_hwif->mmio;
        hwif->rqsize                    = tmp_hwif->rqsize;
        hwif->no_lba48                  = tmp_hwif->no_lba48;
@@ -744,14 +834,16 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif)
 }
 
 /**
- *     ide_unregister          -       free an ide interface
- *     @index: index of interface (will change soon to a pointer)
+ *     __ide_unregister_hwif   -       free an ide interface
+ *     @hwif: interface to unregister
  *
  *     Perform the final unregister of an IDE interface. At the moment
  *     we don't refcount interfaces so this will also get split up.
  *
  *     Locking:
- *     The caller must not hold the IDE locks
+ *     The caller must not hold the IDE locks except for ide_cfg_sem
+ *     which must be held.
+ *
  *     The drive present/vanishing is not yet properly locked
  *     Take care with the callbacks. These have been split to avoid
  *     deadlocking the IDE layer. The shutdown callback is called
@@ -761,26 +853,39 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif)
  *     isnt yet done btw). After we commit to the final kill we
  *     call the cleanup callback with the ide locks held.
  *
+ *     An interface can be in four states we care about
+ *     - It can be busy (drive or driver thinks its active). No unload
+ *     - It can be unconfigured - which means its already gone
+ *     - It can be configured and present - a full interface
+ *     - It can be configured and not present - pci configured but no drives
+ *                                              so logically absent.
+ *
  *     Unregister restores the hwif structures to the default state.
- *     This is raving bonkers.
  */
 
-void ide_unregister(unsigned int index)
+int __ide_unregister_hwif(ide_hwif_t *hwif)
 {
-       ide_drive_t *drive;
-       ide_hwif_t *hwif, *g;
+       ide_drive_t *drive = NULL; /* keep compiler happy */
+       ide_hwif_t *g;
        static ide_hwif_t tmp_hwif; /* protected by ide_cfg_sem */
        ide_hwgroup_t *hwgroup;
        int irq_count = 0, unit, i;
-
-       BUG_ON(index >= MAX_HWIFS);
+       int was_present;
+       int ret = 0;
+       int index = hwif->index;
 
        BUG_ON(in_interrupt());
        BUG_ON(irqs_disabled());
-       down(&ide_cfg_sem);
+
+       /* Make sure nobody sneaks in via the proc interface */ 
+       down(&ide_setting_sem);
+       
+       /* Now ensure nobody gets in for I/O while we clean up and
+          do the busy check. If busy is set then the device is still
+          open and we must stop */
        spin_lock_irq(&ide_lock);
-       hwif = &ide_hwifs[index];
-       if (!hwif->present)
+       
+       if (!hwif->configured)
                goto abort;
        for (unit = 0; unit < MAX_DRIVES; ++unit) {
                drive = &hwif->drives[unit];
@@ -790,9 +895,17 @@ void ide_unregister(unsigned int index)
                        goto abort;
                drive->dead = 1;
        }
+       /* 
+        * Protect against new users. From this point the hwif
+        * is not present so cannot be opened by a new I/O source.
+        * This also invalidates key driven access from procfs
+        */
+
+       was_present = hwif->present;     
        hwif->present = 0;
 
        spin_unlock_irq(&ide_lock);
+       up(&ide_setting_sem);
 
        for (unit = 0; unit < MAX_DRIVES; ++unit) {
                drive = &hwif->drives[unit];
@@ -801,28 +914,40 @@ void ide_unregister(unsigned int index)
                DRIVER(drive)->cleanup(drive);
        }
 
+#ifdef CONFIG_PROC_FS
+       destroy_proc_ide_drives(hwif);
        destroy_proc_ide_interface(hwif);
+#endif
 
+       spin_lock_irq(&ide_lock);
        hwgroup = hwif->hwgroup;
-       /*
-        * free the irq if we were the only hwif using it
-        */
-       g = hwgroup->hwif;
-       do {
-               if (g->irq == hwif->irq)
-                       ++irq_count;
-               g = g->next;
-       } while (g != hwgroup->hwif);
-       if (irq_count == 1)
+       if(hwgroup)
+       {
+               /*
+                * free the irq if we were the only hwif using it
+                */
+               g = hwgroup->hwif;
+               do {
+                       if (g->irq == hwif->irq)
+                               ++irq_count;
+                       g = g->next;
+               } while (g != hwgroup->hwif);
+       }
+       spin_unlock_irq(&ide_lock);
+       
+       if (irq_count == 1 && hwgroup)
                free_irq(hwif->irq, hwgroup);
 
-       spin_lock_irq(&ide_lock);
        /*
         * Note that we only release the standard ports,
         * and do not even try to handle any extra ports
         * allocated for weird IDE interface chipsets.
+        *
+        * FIXME: should defer this I think
         */
-       ide_hwif_release_regions(hwif);
+
+       if(was_present)
+               ide_hwif_release_regions(hwif);
 
        /*
         * Remove us from the hwgroup, and free
@@ -836,6 +961,14 @@ void ide_unregister(unsigned int index)
                }
                if (!drive->present)
                        continue;
+                       
+               /*
+                * The hwgroup chain is IRQ touched. We must protect
+                * walking this from an IDE event for another device
+                * in the chain 
+                */
+                  
+               spin_lock_irq(&ide_lock);
                if (drive == drive->next) {
                        /* special case: last drive from hwgroup. */
                        BUG_ON(hwgroup->drive != drive);
@@ -852,47 +985,67 @@ void ide_unregister(unsigned int index)
                                hwgroup->hwif = HWIF(hwgroup->drive);
                        }
                }
+               spin_unlock_irq(&ide_lock);
+
+               /*
+                * The rest of the cleanup is private
+                */
+
                BUG_ON(hwgroup->drive == drive);
                if (drive->id != NULL) {
                        kfree(drive->id);
                        drive->id = NULL;
                }
                drive->present = 0;
-               /* Messed up locking ... */
-               spin_unlock_irq(&ide_lock);
                blk_cleanup_queue(drive->queue);
                device_unregister(&drive->gendev);
                down(&drive->gendev_rel_sem);
-               spin_lock_irq(&ide_lock);
                drive->queue = NULL;
        }
-       if (hwif->next == hwif) {
-               BUG_ON(hwgroup->hwif != hwif);
-               kfree(hwgroup);
-       } else {
-               /* There is another interface in hwgroup.
-                * Unlink us, and set hwgroup->drive and ->hwif to
-                * something sane.
-                */
-               g = hwgroup->hwif;
-               while (g->next != hwif)
-                       g = g->next;
-               g->next = hwif->next;
-               if (hwgroup->hwif == hwif) {
-                       /* Chose a random hwif for hwgroup->hwif.
-                        * It's guaranteed that there are no drives
-                        * left in the hwgroup.
+       /*
+        * Lock against hwgroup walkers including interrupts off other
+        * IDE devices wile we unhook ourselves.
+        */
+        
+       spin_lock_irq(&ide_lock);
+       
+       if (hwgroup)
+       {
+               if (hwif->next == hwif) {
+                       BUG_ON(hwgroup->hwif != hwif);
+                       kfree(hwgroup);
+               } else {
+                       /* There is another interface in hwgroup.
+                        * Unlink us, and set hwgroup->drive and ->hwif to
+                        * something sane.
                         */
-                       BUG_ON(hwgroup->drive != NULL);
-                       hwgroup->hwif = g;
+                       g = hwgroup->hwif;
+                       while (g->next != hwif)
+                               g = g->next;
+                       g->next = hwif->next;
+                       if (hwgroup->hwif == hwif) {
+                               /* Chose a random hwif for hwgroup->hwif.
+                                * It's guaranteed that there are no drives
+                                * left in the hwgroup.
+                                */
+                               BUG_ON(hwgroup->drive != NULL);
+                               hwgroup->hwif = g;
+                       }
+                       BUG_ON(hwgroup->hwif == hwif);
                }
-               BUG_ON(hwgroup->hwif == hwif);
        }
-
-       /* More messed up locking ... */
        spin_unlock_irq(&ide_lock);
-       device_unregister(&hwif->gendev);
-       down(&hwif->gendev_rel_sem);
+
+       /*
+        * PCI interfaces with no devices don't exist in the device
+        * tree so don't unregister them.
+        */
+        
+       if(was_present)
+       {
+               device_unregister(&hwif->gendev);
+               down(&hwif->gendev_rel_sem);
+       }
 
        /*
         * Remove us from the kernel's knowledge
@@ -903,10 +1056,16 @@ void ide_unregister(unsigned int index)
                hwif->drives[i].disk = NULL;
                put_disk(disk);
        }
-       kfree(hwif->sg_table);
        unregister_blkdev(hwif->major, hwif->name);
        spin_lock_irq(&ide_lock);
 
+       /*
+        * Let the driver free up private objects
+        */
+
+       if(hwif->remove)
+               hwif->remove(hwif);
+
        if (hwif->dma_base) {
                (void) ide_release_dma(hwif);
 
@@ -918,6 +1077,7 @@ void ide_unregister(unsigned int index)
                hwif->dma_vendor3 = 0;
                hwif->dma_prdtable = 0;
        }
+       hwif->chipset = ide_unknown;
 
        /* copy original settings */
        tmp_hwif = *hwif;
@@ -926,15 +1086,58 @@ void ide_unregister(unsigned int index)
        init_hwif_data(hwif, index);
        init_hwif_default(hwif, index);
 
+       hwif->configured = 0;
+
        ide_hwif_restore(hwif, &tmp_hwif);
+       
+       spin_unlock_irq(&ide_lock);
+       return 0;
 
 abort:
+       if(hwif->configured)
+       {
+               printk("Unregister %d fail %d %d\n", index, drive->usage, DRIVER(drive)->busy);
+               ret = -EBUSY;
+       }
+       else
+       {
+               printk("No such hwif!\n");
+               ret = -ENOENT;
+       }
        spin_unlock_irq(&ide_lock);
-       up(&ide_cfg_sem);
+       up(&ide_setting_sem);
+       return ret;
 }
 
-EXPORT_SYMBOL(ide_unregister);
+EXPORT_SYMBOL_GPL(__ide_unregister_hwif);
 
+/**
+ *     ide_unregister_hwif     -       free an ide interface
+ *     @hwif: interface to unregister
+ *
+ *     Perform the final unregister of an IDE interface. At the moment
+ *     we don't refcount interfaces so this will also get split up.
+ *     Unregister restores the hwif structures to the default state.
+ *
+ *     No locks should be held on entry. When an unregister must
+ *     be done atomically with a register see __ide_unregister_hwif
+ *     and hold the ide_cfg_sem yourself.
+ */
+
+int ide_unregister_hwif(ide_hwif_t *hwif)
+{
+       int ret;
+       
+       /* This protects two things. Firstly it serializes the 
+          shutdown sequence, secondly it protects us from
+          races while we are killing off a device */
+       down(&ide_cfg_sem);
+       ret = __ide_unregister_hwif(hwif);
+       up(&ide_cfg_sem);
+       return ret;
+}
+
+EXPORT_SYMBOL_GPL(ide_unregister_hwif);
 
 /**
  *     ide_setup_ports         -       set up IDE interface ports
@@ -993,19 +1196,26 @@ void ide_setup_ports (   hw_regs_t *hw,
  *     ide_register_hw_with_fixup      -       register IDE interface
  *     @hw: hardware registers
  *     @hwifp: pointer to returned hwif
- *     @fixup: fixup function
+ *     @fixup function to call
  *
  *     Register an IDE interface, specifying exactly the registers etc.
- *     Set init=1 iff calling before probes have taken place.
+ *     Set init=1 iff calling before probes have taken place. The
+ *     ide_cfg_sem protects this against races.
+ *
+ *     Invokes a fixup function after the probe and before device attachment
+ *     that can be used by the driver to amend settings or to work around
+ *     hardware funnies.
  *
  *     Returns -1 on error.
  */
 
-int ide_register_hw_with_fixup(hw_regs_t *hw, ide_hwif_t **hwifp, void(*fixup)(ide_hwif_t *hwif))
+int ide_register_hw_with_fixup(hw_regs_t *hw, ide_hwif_t **hwifp, void (*fixup)(ide_hwif_t *hwif))
 {
        int index, retry = 1;
        ide_hwif_t *hwif;
 
+       down(&ide_cfg_sem);
+
        do {
                for (index = 0; index < MAX_HWIFS; ++index) {
                        hwif = &ide_hwifs[index];
@@ -1016,28 +1226,37 @@ int ide_register_hw_with_fixup(hw_regs_t *hw, ide_hwif_t **hwifp, void(*fixup)(i
                        hwif = &ide_hwifs[index];
                        if (hwif->hold)
                                continue;
-                       if ((!hwif->present && !hwif->mate && !initializing) ||
+                       if ((!hwif->configured && !hwif->mate && !initializing) ||
                            (!hwif->hw.io_ports[IDE_DATA_OFFSET] && initializing))
                                goto found;
                }
+               /* FIXME- this check should die as should the retry loop */
                for (index = 0; index < MAX_HWIFS; index++)
-                       ide_unregister(index);
+               {
+                       hwif = &ide_hwifs[index];
+                       __ide_unregister_hwif(hwif);
+               }
        } while (retry--);
+
+       up(&ide_cfg_sem);
        return -1;
 found:
-       if (hwif->present)
-               ide_unregister(index);
+       /* FIXME: do we really need this case */
+       if (hwif->configured)
+               __ide_unregister_hwif(hwif);
        else if (!hwif->hold) {
                init_hwif_data(hwif, index);
                init_hwif_default(hwif, index);
        }
-       if (hwif->present)
+       if (hwif->configured)
                return -1;
+       hwif->configured = 1;
        memcpy(&hwif->hw, hw, sizeof(*hw));
        memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->hw.io_ports));
        hwif->irq = hw->irq;
        hwif->noprobe = 0;
        hwif->chipset = hw->chipset;
+       up(&ide_cfg_sem);
 
        if (!initializing) {
                probe_hwif_init_with_fixup(hwif, fixup);
@@ -1052,9 +1271,21 @@ found:
 
 EXPORT_SYMBOL(ide_register_hw_with_fixup);
 
-int ide_register_hw(hw_regs_t *hw, ide_hwif_t **hwifp)
+/**
+ *     ide_register_hw         -       register new IDE hardware
+ *     @hw: hardware registers
+ *     @hwifp: pointer to returned hwif
+ *
+ *     Register an IDE interface, specifying exactly the registers etc.
+ *     Set init=1 iff calling before probes have taken place. The
+ *     ide_cfg_sem protects this against races.
+ *
+ *     Returns -1 on error.
+ */
+int ide_register_hw(hw_regs_t *hw, ide_hwif_t **hwif)
 {
-       return ide_register_hw_with_fixup(hw, hwifp, NULL);
+       return ide_register_hw_with_fixup(hw, hwif, NULL);
 }
 
 EXPORT_SYMBOL(ide_register_hw);
@@ -1081,21 +1312,21 @@ DECLARE_MUTEX(ide_setting_sem);
  *     @set: setting
  *
  *     Removes the setting named from the device if it is present.
- *     The function takes the settings_lock to protect against 
- *     parallel changes. This function must not be called from IRQ
- *     context. Returns 0 on success or -1 on failure.
+ *     This function must not be called from IRQ context. Returns 0
+ *     on success or -1 on failure.
  *
  *     BUGS: This code is seriously over-engineered. There is also
  *     magic about how the driver specific features are setup. If
  *     a driver is attached we assume the driver settings are auto
  *     remove.
+ *
+ *     The caller must hold settings_lock
  */
  
 int ide_add_setting (ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
 {
        ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL;
 
-       down(&ide_setting_sem);
        while ((*p) && strcmp((*p)->name, name) < 0)
                p = &((*p)->next);
        if ((setting = kmalloc(sizeof(*setting), GFP_KERNEL)) == NULL)
@@ -1119,10 +1350,8 @@ int ide_add_setting (ide_drive_t *drive, const char *name, int rw, int read_ioct
        if (drive->driver != &idedefault_driver)
                setting->auto_remove = 1;
        *p = setting;
-       up(&ide_setting_sem);
        return 0;
 abort:
-       up(&ide_setting_sem);
        if (setting)
                kfree(setting);
        return -1;
@@ -1131,7 +1360,7 @@ abort:
 EXPORT_SYMBOL(ide_add_setting);
 
 /**
- *     __ide_remove_setting    -       remove an ide setting option
+ *     ide_remove_setting      -       remove an ide setting option
  *     @drive: drive to use
  *     @name: setting name
  *
@@ -1139,7 +1368,7 @@ EXPORT_SYMBOL(ide_add_setting);
  *     The caller must hold the setting semaphore.
  */
  
-static void __ide_remove_setting (ide_drive_t *drive, char *name)
+static void ide_remove_setting (ide_drive_t *drive, char *name)
 {
        ide_settings_t **p, *setting;
 
@@ -1217,7 +1446,7 @@ repeat:
        setting = drive->settings;
        while (setting) {
                if (setting->auto_remove) {
-                       __ide_remove_setting(drive, setting->name);
+                       ide_remove_setting(drive, setting->name);
                        goto repeat;
                }
                setting = setting->next;
@@ -1416,6 +1645,7 @@ static int set_xfer_rate (ide_drive_t *drive, int arg)
 
 void ide_add_generic_settings (ide_drive_t *drive)
 {
+       down(&ide_setting_sem);
 /*
  *                     drive   setting name            read/write access                               read ioctl              write ioctl             data type       min     max                             mul_factor      div_factor      data pointer                    set function
  */
@@ -1428,6 +1658,8 @@ void ide_add_generic_settings (ide_drive_t *drive)
        ide_add_setting(drive,  "init_speed",           SETTING_RW,                                     -1,                     -1,                     TYPE_BYTE,      0,      70,                             1,              1,              &drive->init_speed,             NULL);
        ide_add_setting(drive,  "current_speed",        SETTING_RW,                                     -1,                     -1,                     TYPE_BYTE,      0,      70,                             1,              1,              &drive->current_speed,          set_xfer_rate);
        ide_add_setting(drive,  "number",               SETTING_RW,                                     -1,                     -1,                     TYPE_BYTE,      0,      3,                              1,              1,              &drive->dn,                     NULL);
+
+       up(&ide_setting_sem);
 }
 
 /**
@@ -1484,28 +1716,30 @@ abort:
  *     A return of zero indicates attachment to a driver, of one
  *     attachment to the default driver.
  *
- *     Takes drivers_lock.
+ *     Takes the driver list lock and the ide_settings semaphore.
  */
 
 int ata_attach(ide_drive_t *drive)
 {
        struct list_head *p;
-       spin_lock(&drivers_lock);
+       down(&drivers_sem);
+       down(&ide_setting_sem);
        list_for_each(p, &drivers) {
                ide_driver_t *driver = list_entry(p, ide_driver_t, drivers);
                if (!try_module_get(driver->owner))
                        continue;
-               spin_unlock(&drivers_lock);
                if (driver->attach(drive) == 0) {
                        module_put(driver->owner);
                        drive->gendev.driver = &driver->gen_driver;
+                       up(&ide_setting_sem);
+                       up(&drivers_sem);
                        return 0;
                }
-               spin_lock(&drivers_lock);
                module_put(driver->owner);
        }
        drive->gendev.driver = &idedefault_driver.gen_driver;
-       spin_unlock(&drivers_lock);
+       up(&ide_setting_sem);
+       up(&drivers_sem);
        if(idedefault_driver.attach(drive) != 0)
                panic("ide: default attach failed");
        return 1;
@@ -1631,6 +1865,7 @@ int generic_ide_ioctl(struct file *file, struct block_device *bdev,
                case HDIO_SCAN_HWIF:
                {
                        hw_regs_t hw;
+                       ide_hwif_t *hwif;
                        int args[3];
                        if (!capable(CAP_SYS_RAWIO)) return -EACCES;
                        if (copy_from_user(args, p, 3 * sizeof(int)))
@@ -1639,14 +1874,18 @@ int generic_ide_ioctl(struct file *file, struct block_device *bdev,
                        ide_init_hwif_ports(&hw, (unsigned long) args[0],
                                            (unsigned long) args[1], NULL);
                        hw.irq = args[2];
-                       if (ide_register_hw(&hw, NULL) == -1)
+                       if (ide_register_hw(&hw, &hwif) == -1)
                                return -EIO;
+                       hwif->user_dev = 1;
                        return 0;
                }
                case HDIO_UNREGISTER_HWIF:
                        if (!capable(CAP_SYS_RAWIO)) return -EACCES;
-                       /* (arg > MAX_HWIFS) checked in function */
-                       ide_unregister(arg);
+                       if(arg > MAX_HWIFS || arg < 0)
+                               return -EINVAL;
+                       if(!ide_hwifs[arg].user_dev)
+                               return -EINVAL;
+                       return ide_unregister_hwif(&ide_hwifs[arg]);
                        return 0;
                case HDIO_SET_NICE:
                        if (!capable(CAP_SYS_ADMIN)) return -EACCES;
@@ -1787,6 +2026,9 @@ static int __init match_parm (char *s, const char *keywords[], int vals[], int m
        return 0;       /* zero = nothing matched */
 }
 
+#ifdef CONFIG_BLK_DEV_PDC4030
+static int __initdata probe_pdc4030;
+#endif
 #ifdef CONFIG_BLK_DEV_ALI14XX
 static int __initdata probe_ali14xx;
 extern int ali14xx_init(void);
@@ -1846,7 +2088,7 @@ int __init ide_setup (char *s)
 #endif /* CONFIG_BLK_DEV_IDEDOUBLER */
 
        if (!strcmp(s, "ide=nodma")) {
-               printk(" : Prevented DMA\n");
+               printk("IDE: Prevented DMA\n");
                noautodma = 1;
                return 1;
        }
@@ -1865,7 +2107,7 @@ int __init ide_setup (char *s)
        if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
                const char *hd_words[] = {
                        "none", "noprobe", "nowerr", "cdrom", "serialize",
-                       "autotune", "noautotune", "minus8", "swapdata", "bswap",
+                       "autotune", "noautotune", "stroke", "swapdata", "bswap",
                        "minus11", "remap", "remap63", "scsi", NULL };
                unit = s[2] - 'a';
                hw   = unit / MAX_DRIVES;
@@ -1895,10 +2137,13 @@ int __init ide_setup (char *s)
                                goto do_serialize;
                        case -6: /* "autotune" */
                                drive->autotune = IDE_TUNE_AUTO;
-                               goto obsolete_option;
+                               goto done;
                        case -7: /* "noautotune" */
                                drive->autotune = IDE_TUNE_NOAUTO;
-                               goto obsolete_option;
+                               goto done;
+                       case -8: /* stroke */
+                               drive->stroke = 1;
+                               goto done;
                        case -9: /* "swapdata" */
                        case -10: /* "bswap" */
                                drive->bswap = 1;
@@ -1952,7 +2197,7 @@ int __init ide_setup (char *s)
                        "noprobe", "serialize", "autotune", "noautotune", 
                        "reset", "dma", "ata66", "minus8", "minus9",
                        "minus10", "four", "qd65xx", "ht6560b", "cmd640_vlb",
-                       "dtc2278", "umc8672", "ali14xx", NULL };
+                       "dtc2278", "umc8672", "ali14xx", "dc4030", NULL };
                hw = s[3] - '0';
                hwif = &ide_hwifs[hw];
                i = match_parm(&s[4], ide_words, vals, 3);
@@ -1978,6 +2223,11 @@ int __init ide_setup (char *s)
                }
 
                switch (i) {
+#ifdef CONFIG_BLK_DEV_PDC4030
+                       case -18: /* "dc4030" */
+                               probe_pdc4030 = 1;
+                               goto done;
+#endif
 #ifdef CONFIG_BLK_DEV_ALI14XX
                        case -17: /* "ali14xx" */
                                probe_ali14xx = 1;
@@ -2030,30 +2280,30 @@ int __init ide_setup (char *s)
                        case -7: /* ata66 */
 #ifdef CONFIG_BLK_DEV_IDEPCI
                                hwif->udma_four = 1;
-                               goto obsolete_option;
+                               goto done;
 #else
                                goto bad_hwif;
 #endif
                        case -6: /* dma */
                                hwif->autodma = 1;
-                               goto obsolete_option;
+                               goto done;
                        case -5: /* "reset" */
                                hwif->reset = 1;
-                               goto obsolete_option;
+                               goto done;
                        case -4: /* "noautotune" */
                                hwif->drives[0].autotune = IDE_TUNE_NOAUTO;
                                hwif->drives[1].autotune = IDE_TUNE_NOAUTO;
-                               goto obsolete_option;
+                               goto done;
                        case -3: /* "autotune" */
                                hwif->drives[0].autotune = IDE_TUNE_AUTO;
                                hwif->drives[1].autotune = IDE_TUNE_AUTO;
-                               goto obsolete_option;
+                               goto done;
                        case -2: /* "serialize" */
                        do_serialize:
                                hwif->mate = &ide_hwifs[hw^1];
                                hwif->mate->mate = hwif;
                                hwif->serialized = hwif->mate->serialized = 1;
-                               goto obsolete_option;
+                               goto done;
 
                        case -1: /* "noprobe" */
                                hwif->noprobe = 1;
@@ -2070,7 +2320,7 @@ int __init ide_setup (char *s)
                                hwif->irq      = vals[2];
                                hwif->noprobe  = 0;
                                hwif->chipset  = ide_forced;
-                               goto obsolete_option;
+                               goto done;
 
                        case 0: goto bad_option;
                        default:
@@ -2081,9 +2331,6 @@ int __init ide_setup (char *s)
 bad_option:
        printk(" -- BAD OPTION\n");
        return 1;
-obsolete_option:
-       printk(" -- OBSOLETE OPTION, WILL BE REMOVED SOON!\n");
-       return 1;
 bad_hwif:
        printk("-- NOT SUPPORTED ON ide%d", hw);
 done:
@@ -2115,6 +2362,13 @@ static void __init probe_for_hwifs (void)
                ide_probe_for_cmd640x();
        }
 #endif /* CONFIG_BLK_DEV_CMD640 */
+#ifdef CONFIG_BLK_DEV_PDC4030
+       {
+               extern int pdc4030_init(void);
+               if (probe_pdc4030)
+                       (void)pdc4030_init();
+       }
+#endif /* CONFIG_BLK_DEV_PDC4030 */
 #ifdef CONFIG_BLK_DEV_IDE_PMAC
        {
                extern void pmac_ide_probe(void);
@@ -2271,8 +2525,8 @@ int ide_register_subdriver(ide_drive_t *drive, ide_driver_t *driver)
        drive->suspend_reset = 0;
 #ifdef CONFIG_PROC_FS
        if (drive->driver != &idedefault_driver) {
-               ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive);
-               ide_add_proc_entries(drive->proc, driver->proc, drive);
+               ide_add_proc_entries(drive->proc, generic_subdriver_entries, ide_drive_to_key(drive));
+               ide_add_proc_entries(drive->proc, driver->proc, ide_drive_to_key(drive));
        }
 #endif
        return 0;
@@ -2297,6 +2551,7 @@ EXPORT_SYMBOL(ide_register_subdriver);
 int ide_unregister_subdriver (ide_drive_t *drive)
 {
        unsigned long flags;
+       ide_proc_entry_t *dir;
        
        down(&ide_setting_sem);
        spin_lock_irqsave(&ide_lock, flags);
@@ -2305,13 +2560,14 @@ int ide_unregister_subdriver (ide_drive_t *drive)
                up(&ide_setting_sem);
                return 1;
        }
+       dir = DRIVER(drive)->proc;
+       drive->driver = &idedefault_driver;
+       spin_unlock_irqrestore(&ide_lock, flags);
 #ifdef CONFIG_PROC_FS
-       ide_remove_proc_entries(drive->proc, DRIVER(drive)->proc);
+       ide_remove_proc_entries(drive->proc, dir);
        ide_remove_proc_entries(drive->proc, generic_subdriver_entries);
 #endif
        auto_remove_settings(drive);
-       drive->driver = &idedefault_driver;
-       spin_unlock_irqrestore(&ide_lock, flags);
        up(&ide_setting_sem);
        spin_lock(&drives_lock);
        list_del_init(&drive->list);
@@ -2337,7 +2593,8 @@ static int ide_drive_remove(struct device * dev)
  *     on the IDE bus in case any should be attached to the
  *     driver we have just registered.  If so attach them.
  *
- *     Takes drivers_lock and drives_lock.
+ *     Takes the drivers and drives lock. Should take the
+ *     ide_sem but doesn't - FIXME ??
  */
 
 int ide_register_driver(ide_driver_t *driver)
@@ -2348,9 +2605,11 @@ int ide_register_driver(ide_driver_t *driver)
 
        setup_driver_defaults(driver);
 
+       down(&drivers_sem);
        spin_lock(&drivers_lock);
        list_add(&driver->drivers, &drivers);
        spin_unlock(&drivers_lock);
+       up(&drivers_sem);
 
        INIT_LIST_HEAD(&list);
        spin_lock(&drives_lock);
@@ -2386,9 +2645,11 @@ void ide_unregister_driver(ide_driver_t *driver)
 {
        ide_drive_t *drive;
 
+       down(&drivers_sem);
        spin_lock(&drivers_lock);
        list_del(&driver->drivers);
        spin_unlock(&drivers_lock);
+       up(&drivers_sem);
 
        driver_unregister(&driver->gen_driver);
 
@@ -2474,7 +2735,7 @@ int __init ide_init (void)
 
 #ifdef MODULE
 char *options = NULL;
-module_param(options, charp, 0);
+MODULE_PARM(options,"s");
 MODULE_LICENSE("GPL");
 
 static void __init parse_options (char *line)
@@ -2502,7 +2763,8 @@ void cleanup_module (void)
        int index;
 
        for (index = 0; index < MAX_HWIFS; ++index) {
-               ide_unregister(index);
+               if(ide_unregister_hwif(&ide_hwifs[index]))
+                       printk(KERN_ERR "ide: unload yet busy!\n");
                if (ide_hwifs[index].dma_base)
                        (void) ide_release_dma(&ide_hwifs[index]);
        }