fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / ide / pci / hpt366.c
index 77bc208..b486442 100644 (file)
@@ -1,14 +1,23 @@
 /*
- * linux/drivers/ide/pci/hpt366.c              Version 0.34    Sept 17, 2002
+ * linux/drivers/ide/pci/hpt366.c              Version 0.36    April 25, 2003
  *
  * Copyright (C) 1999-2003             Andre Hedrick <andre@linux-ide.org>
  * Portions Copyright (C) 2001         Sun Microsystems, Inc.
+ * Portions Copyright (C) 2003         Red Hat Inc
+ * Portions Copyright (C) 2005-2006    MontaVista Software, Inc.
  *
  * Thanks to HighPoint Technologies for their assistance, and hardware.
  * Special Thanks to Jon Burchmore in SanDiego for the deep pockets, his
  * donation of an ABit BP6 mainboard, processor, and memory acellerated
  * development and support.
  *
+ *
+ * HighPoint has its own drivers (open source except for the RAID part)
+ * available from http://www.highpoint-tech.com/BIOS%20+%20Driver/.
+ * This may be useful to anyone wanting to work on this driver, however  do not
+ * trust  them too much since the code tends to become less and less meaningful
+ * as the time passes... :-/
+ *
  * Note that final HPT370 support was done by force extraction of GPL.
  *
  * - add function for getting/setting power status of drive
  * Reset the hpt366 on error, reset on dma
  * Fix disabling Fast Interrupt hpt366.
  *     Mike Waychison <crlf@sun.com>
+ *
+ * Added support for 372N clocking and clock switching. The 372N needs
+ * different clocks on read/write. This requires overloading rw_disk and
+ * other deeply crazy things. Thanks to <http://www.hoerstreich.de> for
+ * keeping me sane. 
+ *             Alan Cox <alan@redhat.com>
+ *
+ * - fix the clock turnaround code: it was writing to the wrong ports when
+ *   called for the secondary channel, caching the current clock mode per-
+ *   channel caused the cached register value to get out of sync with the
+ *   actual one, the channels weren't serialized, the turnaround shouldn't
+ *   be done on 66 MHz PCI bus
+ * - avoid calibrating PLL twice as the second time results in a wrong PCI
+ *   frequency and thus in the wrong timings for the secondary channel
+ * - disable UltraATA/133 for HPT372 by default (50 MHz DPLL clock do not
+ *   allow for this speed anyway)
+ * - add support for HPT302N and HPT371N clocking (the same as for HPT372N)
+ * - HPT371/N are single channel chips, so avoid touching the primary channel
+ *   which exists only virtually (there's no pins for it)
+ * - fix/remove bad/unused timing tables and use one set of tables for the whole
+ *   HPT37x chip family; save space by introducing the separate transfer mode
+ *   table in which the mode lookup is done
+ * - use f_CNT value saved by  the HighPoint BIOS as reading it directly gives
+ *   the wrong PCI frequency since DPLL has already been calibrated by BIOS
+ * - fix the hotswap code:  it caused RESET- to glitch when tristating the bus,
+ *   and for HPT36x the obsolete HDIO_TRISTATE_HWIF handler was called instead
+ * - pass to init_chipset() handlers a copy of the IDE PCI device structure as
+ *   they tamper with its fields
+ *             <source@mvista.com>
+ *
  */
 
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 
-#include "hpt366.h"
+/* various tuning parameters */
+#define HPT_RESET_STATE_ENGINE
+#undef HPT_DELAY_INTERRUPT
+#define HPT_SERIALIZE_IO       0
+
+static const char *quirk_drives[] = {
+       "QUANTUM FIREBALLlct08 08",
+       "QUANTUM FIREBALLP KA6.4",
+       "QUANTUM FIREBALLP LM20.4",
+       "QUANTUM FIREBALLP LM20.5",
+       NULL
+};
+
+static const char *bad_ata100_5[] = {
+       "IBM-DTLA-307075",
+       "IBM-DTLA-307060",
+       "IBM-DTLA-307045",
+       "IBM-DTLA-307030",
+       "IBM-DTLA-307020",
+       "IBM-DTLA-307015",
+       "IBM-DTLA-305040",
+       "IBM-DTLA-305030",
+       "IBM-DTLA-305020",
+       "IC35L010AVER07-0",
+       "IC35L020AVER07-0",
+       "IC35L030AVER07-0",
+       "IC35L040AVER07-0",
+       "IC35L060AVER07-0",
+       "WDC AC310200R",
+       NULL
+};
+
+static const char *bad_ata66_4[] = {
+       "IBM-DTLA-307075",
+       "IBM-DTLA-307060",
+       "IBM-DTLA-307045",
+       "IBM-DTLA-307030",
+       "IBM-DTLA-307020",
+       "IBM-DTLA-307015",
+       "IBM-DTLA-305040",
+       "IBM-DTLA-305030",
+       "IBM-DTLA-305020",
+       "IC35L010AVER07-0",
+       "IC35L020AVER07-0",
+       "IC35L030AVER07-0",
+       "IC35L040AVER07-0",
+       "IC35L060AVER07-0",
+       "WDC AC310200R",
+       NULL
+};
+
+static const char *bad_ata66_3[] = {
+       "WDC AC310200R",
+       NULL
+};
 
-#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-#endif  /* defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) */
+static const char *bad_ata33[] = {
+       "Maxtor 92720U8", "Maxtor 92040U6", "Maxtor 91360U4", "Maxtor 91020U3", "Maxtor 90845U3", "Maxtor 90650U2",
+       "Maxtor 91360D8", "Maxtor 91190D7", "Maxtor 91020D6", "Maxtor 90845D5", "Maxtor 90680D4", "Maxtor 90510D3", "Maxtor 90340D2",
+       "Maxtor 91152D8", "Maxtor 91008D7", "Maxtor 90845D6", "Maxtor 90840D6", "Maxtor 90720D5", "Maxtor 90648D5", "Maxtor 90576D4",
+       "Maxtor 90510D4",
+       "Maxtor 90432D3", "Maxtor 90288D2", "Maxtor 90256D2",
+       "Maxtor 91000D8", "Maxtor 90910D8", "Maxtor 90875D7", "Maxtor 90840D7", "Maxtor 90750D6", "Maxtor 90625D5", "Maxtor 90500D4",
+       "Maxtor 91728D8", "Maxtor 91512D7", "Maxtor 91303D6", "Maxtor 91080D5", "Maxtor 90845D4", "Maxtor 90680D4", "Maxtor 90648D3", "Maxtor 90432D2",
+       NULL
+};
 
-static unsigned int hpt_revision(struct pci_dev *dev);
-static unsigned int hpt_minimum_revision(struct pci_dev *dev, int revision);
+static u8 xfer_speeds[] = {
+       XFER_UDMA_6,
+       XFER_UDMA_5,
+       XFER_UDMA_4,
+       XFER_UDMA_3,
+       XFER_UDMA_2,
+       XFER_UDMA_1,
+       XFER_UDMA_0,
+
+       XFER_MW_DMA_2,
+       XFER_MW_DMA_1,
+       XFER_MW_DMA_0,
+
+       XFER_PIO_4,
+       XFER_PIO_3,
+       XFER_PIO_2,
+       XFER_PIO_1,
+       XFER_PIO_0
+};
 
-#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
+/* Key for bus clock timings
+ * 36x   37x
+ * bits  bits
+ * 0:3  0:3    data_high_time. Inactive time of DIOW_/DIOR_ for PIO and MW DMA.
+ *             cycles = value + 1
+ * 4:7  4:8    data_low_time. Active time of DIOW_/DIOR_ for PIO and MW DMA.
+ *             cycles = value + 1
+ * 8:11  9:12  cmd_high_time. Inactive time of DIOW_/DIOR_ during task file
+ *             register access.
+ * 12:15 13:17 cmd_low_time. Active time of DIOW_/DIOR_ during task file
+ *             register access.
+ * 16:18 18:20 udma_cycle_time. Clock cycles for UDMA xfer.
+ * -    21     CLK frequency: 0=ATA clock, 1=dual ATA clock.
+ * 19:21 22:24 pre_high_time. Time to initialize the 1st cycle for PIO and
+ *             MW DMA xfer.
+ * 22:24 25:27 cmd_pre_high_time. Time to initialize the 1st PIO cycle for
+ *             task file register access.
+ * 28   28     UDMA enable.
+ * 29   29     DMA  enable.
+ * 30   30     PIO MST enable. If set, the chip is in bus master mode during
+ *             PIO xfer.
+ * 31   31     FIFO enable.
+ */
 
-static u8 hpt366_proc = 0;
-static struct pci_dev *hpt_devs[HPT366_MAX_DEVS];
-static int n_hpt_devs;
+static u32 forty_base_hpt36x[] = {
+       /* XFER_UDMA_6 */       0x900fd943,
+       /* XFER_UDMA_5 */       0x900fd943,
+       /* XFER_UDMA_4 */       0x900fd943,
+       /* XFER_UDMA_3 */       0x900ad943,
+       /* XFER_UDMA_2 */       0x900bd943,
+       /* XFER_UDMA_1 */       0x9008d943,
+       /* XFER_UDMA_0 */       0x9008d943,
+
+       /* XFER_MW_DMA_2 */     0xa008d943,
+       /* XFER_MW_DMA_1 */     0xa010d955,
+       /* XFER_MW_DMA_0 */     0xa010d9fc,
+
+       /* XFER_PIO_4 */        0xc008d963,
+       /* XFER_PIO_3 */        0xc010d974,
+       /* XFER_PIO_2 */        0xc010d997,
+       /* XFER_PIO_1 */        0xc010d9c7,
+       /* XFER_PIO_0 */        0xc018d9d9
+};
 
-static int hpt366_get_info (char *buffer, char **addr, off_t offset, int count)
+static u32 thirty_three_base_hpt36x[] = {
+       /* XFER_UDMA_6 */       0x90c9a731,
+       /* XFER_UDMA_5 */       0x90c9a731,
+       /* XFER_UDMA_4 */       0x90c9a731,
+       /* XFER_UDMA_3 */       0x90cfa731,
+       /* XFER_UDMA_2 */       0x90caa731,
+       /* XFER_UDMA_1 */       0x90cba731,
+       /* XFER_UDMA_0 */       0x90c8a731,
+
+       /* XFER_MW_DMA_2 */     0xa0c8a731,
+       /* XFER_MW_DMA_1 */     0xa0c8a732,     /* 0xa0c8a733 */
+       /* XFER_MW_DMA_0 */     0xa0c8a797,
+
+       /* XFER_PIO_4 */        0xc0c8a731,
+       /* XFER_PIO_3 */        0xc0c8a742,
+       /* XFER_PIO_2 */        0xc0d0a753,
+       /* XFER_PIO_1 */        0xc0d0a7a3,     /* 0xc0d0a793 */
+       /* XFER_PIO_0 */        0xc0d0a7aa      /* 0xc0d0a7a7 */
+};
+
+static u32 twenty_five_base_hpt36x[] = {
+       /* XFER_UDMA_6 */       0x90c98521,
+       /* XFER_UDMA_5 */       0x90c98521,
+       /* XFER_UDMA_4 */       0x90c98521,
+       /* XFER_UDMA_3 */       0x90cf8521,
+       /* XFER_UDMA_2 */       0x90cf8521,
+       /* XFER_UDMA_1 */       0x90cb8521,
+       /* XFER_UDMA_0 */       0x90cb8521,
+
+       /* XFER_MW_DMA_2 */     0xa0ca8521,
+       /* XFER_MW_DMA_1 */     0xa0ca8532,
+       /* XFER_MW_DMA_0 */     0xa0ca8575,
+
+       /* XFER_PIO_4 */        0xc0ca8521,
+       /* XFER_PIO_3 */        0xc0ca8532,
+       /* XFER_PIO_2 */        0xc0ca8542,
+       /* XFER_PIO_1 */        0xc0d08572,
+       /* XFER_PIO_0 */        0xc0d08585
+};
+
+static u32 thirty_three_base_hpt37x[] = {
+       /* XFER_UDMA_6 */       0x12446231,     /* 0x12646231 ?? */
+       /* XFER_UDMA_5 */       0x12446231,
+       /* XFER_UDMA_4 */       0x12446231,
+       /* XFER_UDMA_3 */       0x126c6231,
+       /* XFER_UDMA_2 */       0x12486231,
+       /* XFER_UDMA_1 */       0x124c6233,
+       /* XFER_UDMA_0 */       0x12506297,
+
+       /* XFER_MW_DMA_2 */     0x22406c31,
+       /* XFER_MW_DMA_1 */     0x22406c33,
+       /* XFER_MW_DMA_0 */     0x22406c97,
+
+       /* XFER_PIO_4 */        0x06414e31,
+       /* XFER_PIO_3 */        0x06414e42,
+       /* XFER_PIO_2 */        0x06414e53,
+       /* XFER_PIO_1 */        0x06814e93,
+       /* XFER_PIO_0 */        0x06814ea7
+};
+
+static u32 fifty_base_hpt37x[] = {
+       /* XFER_UDMA_6 */       0x12848242,
+       /* XFER_UDMA_5 */       0x12848242,
+       /* XFER_UDMA_4 */       0x12ac8242,
+       /* XFER_UDMA_3 */       0x128c8242,
+       /* XFER_UDMA_2 */       0x120c8242,
+       /* XFER_UDMA_1 */       0x12148254,
+       /* XFER_UDMA_0 */       0x121882ea,
+
+       /* XFER_MW_DMA_2 */     0x22808242,
+       /* XFER_MW_DMA_1 */     0x22808254,
+       /* XFER_MW_DMA_0 */     0x228082ea,
+
+       /* XFER_PIO_4 */        0x0a81f442,
+       /* XFER_PIO_3 */        0x0a81f443,
+       /* XFER_PIO_2 */        0x0a81f454,
+       /* XFER_PIO_1 */        0x0ac1f465,
+       /* XFER_PIO_0 */        0x0ac1f48a
+};
+
+static u32 sixty_six_base_hpt37x[] = {
+       /* XFER_UDMA_6 */       0x1c869c62,
+       /* XFER_UDMA_5 */       0x1cae9c62,     /* 0x1c8a9c62 */
+       /* XFER_UDMA_4 */       0x1c8a9c62,
+       /* XFER_UDMA_3 */       0x1c8e9c62,
+       /* XFER_UDMA_2 */       0x1c929c62,
+       /* XFER_UDMA_1 */       0x1c9a9c62,
+       /* XFER_UDMA_0 */       0x1c829c62,
+
+       /* XFER_MW_DMA_2 */     0x2c829c62,
+       /* XFER_MW_DMA_1 */     0x2c829c66,
+       /* XFER_MW_DMA_0 */     0x2c829d2e,
+
+       /* XFER_PIO_4 */        0x0c829c62,
+       /* XFER_PIO_3 */        0x0c829c84,
+       /* XFER_PIO_2 */        0x0c829ca6,
+       /* XFER_PIO_1 */        0x0d029d26,
+       /* XFER_PIO_0 */        0x0d029d5e
+};
+
+#define HPT366_DEBUG_DRIVE_INFO                0
+#define HPT374_ALLOW_ATA133_6          0
+#define HPT371_ALLOW_ATA133_6          0
+#define HPT302_ALLOW_ATA133_6          0
+#define HPT372_ALLOW_ATA133_6          0
+#define HPT370_ALLOW_ATA100_5          1
+#define HPT366_ALLOW_ATA66_4           1
+#define HPT366_ALLOW_ATA66_3           1
+#define HPT366_MAX_DEVS                        8
+
+#define F_LOW_PCI_33   0x23
+#define F_LOW_PCI_40   0x29
+#define F_LOW_PCI_50   0x2d
+#define F_LOW_PCI_66   0x42
+
+/*
+ *     Hold all the highpoint quirks and revision information in one
+ *     place.
+ */
+
+struct hpt_info
 {
-       char *p = buffer;
-       char *chipset_nums[] = {"366", "366",  "368",
-                               "370", "370A", "372",
-                               "302", "371",  "374" };
-       int i, len;
-
-       p += sprintf(p, "\n                             "
-               "HighPoint HPT366/368/370/372/374\n");
-       for (i = 0; i < n_hpt_devs; i++) {
-               struct pci_dev *dev = hpt_devs[i];
-               unsigned long iobase = dev->resource[4].start;
-               u32 class_rev = hpt_revision(dev);
-               u8 c0, c1;
-
-               p += sprintf(p, "\nController: %d\n", i);
-               p += sprintf(p, "Chipset: HPT%s\n", chipset_nums[class_rev]);
-               p += sprintf(p, "--------------- Primary Channel "
-                               "--------------- Secondary Channel "
-                               "--------------\n");
-
-               /* get the bus master status registers */
-               c0 = inb(iobase + 0x2);
-               c1 = inb(iobase + 0xa);
-               p += sprintf(p, "Enabled:        %s"
-                               "                             %s\n",
-                       (c0 & 0x80) ? "no" : "yes",
-                       (c1 & 0x80) ? "no" : "yes");
-#if 0
-               if (hpt_minimum_revision(dev, 3)) {
-                       u8 cbl;
-                       cbl = inb(iobase + 0x7b);
-                       outb(cbl | 1, iobase + 0x7b);
-                       outb(cbl & ~1, iobase + 0x7b);
-                       cbl = inb(iobase + 0x7a);
-                       p += sprintf(p, "Cable:          ATA-%d"
-                                       "                          ATA-%d\n",
-                               (cbl & 0x02) ? 33 : 66,
-                               (cbl & 0x01) ? 33 : 66);
-                       p += sprintf(p, "\n");
-               }
-#endif
-               p += sprintf(p, "--------------- drive0 --------- drive1 "
-                               "------- drive0 ---------- drive1 -------\n");
-               p += sprintf(p, "DMA capable:    %s              %s" 
-                               "            %s               %s\n",
-                       (c0 & 0x20) ? "yes" : "no ", 
-                       (c0 & 0x40) ? "yes" : "no ",
-                       (c1 & 0x20) ? "yes" : "no ", 
-                       (c1 & 0x40) ? "yes" : "no ");
-
-               {
-                       u8 c2, c3;
-                       /* older revs don't have these registers mapped 
-                        * into io space */
-                       pci_read_config_byte(dev, 0x43, &c0);
-                       pci_read_config_byte(dev, 0x47, &c1);
-                       pci_read_config_byte(dev, 0x4b, &c2);
-                       pci_read_config_byte(dev, 0x4f, &c3);
-
-                       p += sprintf(p, "Mode:           %s             %s"
-                                       "           %s              %s\n",
-                               (c0 & 0x10) ? "UDMA" : (c0 & 0x20) ? "DMA " : 
-                                       (c0 & 0x80) ? "PIO " : "off ",
-                               (c1 & 0x10) ? "UDMA" : (c1 & 0x20) ? "DMA " :
-                                       (c1 & 0x80) ? "PIO " : "off ",
-                               (c2 & 0x10) ? "UDMA" : (c2 & 0x20) ? "DMA " :
-                                       (c2 & 0x80) ? "PIO " : "off ",
-                               (c3 & 0x10) ? "UDMA" : (c3 & 0x20) ? "DMA " :
-                                       (c3 & 0x80) ? "PIO " : "off ");
-               }
-       }
-       p += sprintf(p, "\n");
+       u8 max_mode;            /* Speeds allowed */
+       int revision;           /* Chipset revision */
+       int flags;              /* Chipset properties */
+#define PLL_MODE       1
+#define IS_3xxN        2
+#define PCI_66MHZ      4
+                               /* Speed table */
+       u32 *speed;
+};
 
-       /* p - buffer must be less than 4k! */
-       len = (p - buffer) - offset;
-       *addr = buffer + offset;
-       
-       return len > count ? count : len;
-}
-#endif  /* defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) */
+/*
+ *     This wants fixing so that we do everything not by classrev
+ *     (which breaks on the newest chips) but by creating an
+ *     enumeration of chip variants and using that
+ */
 
-static u32 hpt_revision (struct pci_dev *dev)
+static __devinit u32 hpt_revision (struct pci_dev *dev)
 {
        u32 class_rev;
        pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
        class_rev &= 0xff;
 
        switch(dev->device) {
+               /* Remap new 372N onto 372 */
+               case PCI_DEVICE_ID_TTI_HPT372N:
+                       class_rev = PCI_DEVICE_ID_TTI_HPT372; break;
                case PCI_DEVICE_ID_TTI_HPT374:
                        class_rev = PCI_DEVICE_ID_TTI_HPT374; break;
                case PCI_DEVICE_ID_TTI_HPT371:
@@ -182,44 +393,46 @@ static u32 hpt_revision (struct pci_dev *dev)
        return class_rev;
 }
 
-static u32 hpt_minimum_revision (struct pci_dev *dev, int revision)
-{
-       unsigned int class_rev = hpt_revision(dev);
-       revision--;
-       return ((int) (class_rev > revision) ? 1 : 0);
-}
-
 static int check_in_drive_lists(ide_drive_t *drive, const char **list);
 
 static u8 hpt3xx_ratemask (ide_drive_t *drive)
 {
-       struct pci_dev *dev     = HWIF(drive)->pci_dev;
+       ide_hwif_t *hwif        = drive->hwif;
+       struct hpt_info *info   = ide_get_hwifdata(hwif);
        u8 mode                 = 0;
 
-       if (hpt_minimum_revision(dev, 8)) {             /* HPT374 */
+       /* FIXME: TODO - move this to set info->mode once at boot */
+
+       if (info->revision >= 8) {              /* HPT374 */
                mode = (HPT374_ALLOW_ATA133_6) ? 4 : 3;
-       } else if (hpt_minimum_revision(dev, 7)) {      /* HPT371 */
+       } else if (info->revision >= 7) {       /* HPT371 */
                mode = (HPT371_ALLOW_ATA133_6) ? 4 : 3;
-       } else if (hpt_minimum_revision(dev, 6)) {      /* HPT302 */
+       } else if (info->revision >= 6) {       /* HPT302 */
                mode = (HPT302_ALLOW_ATA133_6) ? 4 : 3;
-       } else if (hpt_minimum_revision(dev, 5)) {      /* HPT372 */
+       } else if (info->revision >= 5) {       /* HPT372 */
                mode = (HPT372_ALLOW_ATA133_6) ? 4 : 3;
-       } else if (hpt_minimum_revision(dev, 4)) {      /* HPT370A */
+       } else if (info->revision >= 4) {       /* HPT370A */
                mode = (HPT370_ALLOW_ATA100_5) ? 3 : 2;
-       } else if (hpt_minimum_revision(dev, 3)) {      /* HPT370 */
+       } else if (info->revision >= 3) {       /* HPT370 */
                mode = (HPT370_ALLOW_ATA100_5) ? 3 : 2;
                mode = (check_in_drive_lists(drive, bad_ata33)) ? 0 : mode;
        } else {                                /* HPT366 and HPT368 */
                mode = (check_in_drive_lists(drive, bad_ata33)) ? 0 : 2;
        }
-       if (!eighty_ninty_three(drive) && (mode))
+       if (!eighty_ninty_three(drive) && mode)
                mode = min(mode, (u8)1);
        return mode;
 }
 
+/*
+ *     Note for the future; the SATA hpt37x we must set
+ *     either PIO or UDMA modes 0,4,5
+ */
 static u8 hpt3xx_ratefilter (ide_drive_t *drive, u8 speed)
 {
-       struct pci_dev *dev     = HWIF(drive)->pci_dev;
+       ide_hwif_t *hwif        = drive->hwif;
+       struct hpt_info *info   = ide_get_hwifdata(hwif);
        u8 mode                 = hpt3xx_ratemask(drive);
 
        if (drive->media != ide_disk)
@@ -231,7 +444,7 @@ static u8 hpt3xx_ratefilter (ide_drive_t *drive, u8 speed)
                        break;
                case 0x03:
                        speed = min(speed, (u8)XFER_UDMA_5);
-                       if (hpt_minimum_revision(dev, 5))
+                       if (info->revision >= 5)
                                break;
                        if (check_in_drive_lists(drive, bad_ata100_5))
                                speed = min(speed, (u8)XFER_UDMA_4);
@@ -241,7 +454,7 @@ static u8 hpt3xx_ratefilter (ide_drive_t *drive, u8 speed)
        /*
         * CHECK ME, Does this need to be set to 5 ??
         */
-                       if (hpt_minimum_revision(dev, 3))
+                       if (info->revision >= 3)
                                break;
                        if ((check_in_drive_lists(drive, bad_ata66_4)) ||
                            (!(HPT366_ALLOW_ATA66_4)))
@@ -255,7 +468,7 @@ static u8 hpt3xx_ratefilter (ide_drive_t *drive, u8 speed)
        /*
         * CHECK ME, Does this need to be set to 5 ??
         */
-                       if (hpt_minimum_revision(dev, 3))
+                       if (info->revision >= 3)
                                break;
                        if (check_in_drive_lists(drive, bad_ata33))
                                speed = min(speed, (u8)XFER_MW_DMA_2);
@@ -284,21 +497,30 @@ static int check_in_drive_lists (ide_drive_t *drive, const char **list)
        return 0;
 }
 
-static unsigned int pci_bus_clock_list (u8 speed, struct chipset_bus_clock_list_entry * chipset_table)
+static u32 pci_bus_clock_list(u8 speed, u32 *chipset_table)
 {
-       for ( ; chipset_table->xfer_speed ; chipset_table++)
-               if (chipset_table->xfer_speed == speed)
-                       return chipset_table->chipset_settings;
-       return chipset_table->chipset_settings;
+       int i;
+
+       /*
+        * Lookup the transfer mode table to get the index into
+        * the timing table.
+        *
+        * NOTE: For XFER_PIO_SLOW, PIO mode 0 timings will be used.
+        */
+       for (i = 0; i < ARRAY_SIZE(xfer_speeds) - 1; i++)
+               if (xfer_speeds[i] == speed)
+                       break;
+       return chipset_table[i];
 }
 
-static void hpt366_tune_chipset (ide_drive_t *drive, u8 xferspeed)
+static int hpt36x_tune_chipset(ide_drive_t *drive, u8 xferspeed)
 {
-       struct pci_dev *dev     = HWIF(drive)->pci_dev;
+       ide_hwif_t *hwif        = drive->hwif;
+       struct pci_dev *dev     = hwif->pci_dev;
+       struct hpt_info *info   = ide_get_hwifdata(hwif);
        u8 speed                = hpt3xx_ratefilter(drive, xferspeed);
-//     u8 speed                = ide_rate_filter(hpt3xx_ratemask(drive), xferspeed);
        u8 regtime              = (drive->select.b.unit & 0x01) ? 0x44 : 0x40;
-       u8 regfast              = (HWIF(drive)->channel) ? 0x55 : 0x51;
+       u8 regfast              = (hwif->channel) ? 0x55 : 0x51;
        u8 drive_fast           = 0;
        u32 reg1 = 0, reg2      = 0;
 
@@ -306,16 +528,11 @@ static void hpt366_tune_chipset (ide_drive_t *drive, u8 xferspeed)
         * Disable the "fast interrupt" prediction.
         */
        pci_read_config_byte(dev, regfast, &drive_fast);
-#if 0
-       if (drive_fast & 0x02)
-               pci_write_config_byte(dev, regfast, drive_fast & ~0x20);
-#else
        if (drive_fast & 0x80)
                pci_write_config_byte(dev, regfast, drive_fast & ~0x80);
-#endif
 
-       reg2 = pci_bus_clock_list(speed,
-               (struct chipset_bus_clock_list_entry *) pci_get_drvdata(dev));
+       reg2 = pci_bus_clock_list(speed, info->speed);
+
        /*
         * Disable on-chip PIO FIFO/buffer
         *  (to avoid problems handling I/O errors later)
@@ -329,19 +546,17 @@ static void hpt366_tune_chipset (ide_drive_t *drive, u8 xferspeed)
        reg2 &= ~0x80000000;
 
        pci_write_config_dword(dev, regtime, reg2);
-}
 
-static void hpt368_tune_chipset (ide_drive_t *drive, u8 speed)
-{
-       hpt366_tune_chipset(drive, speed);
+       return ide_config_drive_speed(drive, speed);
 }
 
-static void hpt370_tune_chipset (ide_drive_t *drive, u8 xferspeed)
+static int hpt370_tune_chipset(ide_drive_t *drive, u8 xferspeed)
 {
-       struct pci_dev *dev = HWIF(drive)->pci_dev;
+       ide_hwif_t *hwif        = drive->hwif;
+       struct pci_dev *dev = hwif->pci_dev;
+       struct hpt_info *info   = ide_get_hwifdata(hwif);
        u8 speed        = hpt3xx_ratefilter(drive, xferspeed);
-//     u8 speed        = ide_rate_filter(hpt3xx_ratemask(drive), xferspeed);
-       u8 regfast      = (HWIF(drive)->channel) ? 0x55 : 0x51;
+       u8 regfast      = (drive->hwif->channel) ? 0x55 : 0x51;
        u8 drive_pci    = 0x40 + (drive->dn * 4);
        u8 new_fast     = 0, drive_fast = 0;
        u32 list_conf   = 0, drive_conf = 0;
@@ -366,26 +581,25 @@ static void hpt370_tune_chipset (ide_drive_t *drive, u8 xferspeed)
        if (new_fast != drive_fast)
                pci_write_config_byte(dev, regfast, new_fast);
 
-       list_conf = pci_bus_clock_list(speed, 
-                                      (struct chipset_bus_clock_list_entry *)
-                                      pci_get_drvdata(dev));
+       list_conf = pci_bus_clock_list(speed, info->speed);
 
        pci_read_config_dword(dev, drive_pci, &drive_conf);
        list_conf = (list_conf & ~conf_mask) | (drive_conf & conf_mask);
        
-       if (speed < XFER_MW_DMA_0) {
+       if (speed < XFER_MW_DMA_0)
                list_conf &= ~0x80000000; /* Disable on-chip PIO FIFO/buffer */
-       }
-
        pci_write_config_dword(dev, drive_pci, list_conf);
+
+       return ide_config_drive_speed(drive, speed);
 }
 
-static void hpt372_tune_chipset (ide_drive_t *drive, u8 xferspeed)
+static int hpt372_tune_chipset(ide_drive_t *drive, u8 xferspeed)
 {
-       struct pci_dev *dev     = HWIF(drive)->pci_dev;
+       ide_hwif_t *hwif        = drive->hwif;
+       struct pci_dev *dev     = hwif->pci_dev;
+       struct hpt_info *info   = ide_get_hwifdata(hwif);
        u8 speed        = hpt3xx_ratefilter(drive, xferspeed);
-//     u8 speed        = ide_rate_filter(hpt3xx_ratemask(drive), xferspeed);
-       u8 regfast      = (HWIF(drive)->channel) ? 0x55 : 0x51;
+       u8 regfast      = (drive->hwif->channel) ? 0x55 : 0x51;
        u8 drive_fast   = 0, drive_pci = 0x40 + (drive->dn * 4);
        u32 list_conf   = 0, drive_conf = 0;
        u32 conf_mask   = (speed >= XFER_MW_DMA_0) ? 0xc0000000 : 0x30070000;
@@ -397,44 +611,30 @@ static void hpt372_tune_chipset (ide_drive_t *drive, u8 xferspeed)
        pci_read_config_byte(dev, regfast, &drive_fast);
        drive_fast &= ~0x07;
        pci_write_config_byte(dev, regfast, drive_fast);
-                                       
-       list_conf = pci_bus_clock_list(speed,
-                       (struct chipset_bus_clock_list_entry *)
-                                       pci_get_drvdata(dev));
+
+       list_conf = pci_bus_clock_list(speed, info->speed);
        pci_read_config_dword(dev, drive_pci, &drive_conf);
        list_conf = (list_conf & ~conf_mask) | (drive_conf & conf_mask);
        if (speed < XFER_MW_DMA_0)
                list_conf &= ~0x80000000; /* Disable on-chip PIO FIFO/buffer */
        pci_write_config_dword(dev, drive_pci, list_conf);
-}
 
-static void hpt374_tune_chipset (ide_drive_t *drive, u8 speed)
-{
-       hpt372_tune_chipset(drive, speed);
+       return ide_config_drive_speed(drive, speed);
 }
 
 static int hpt3xx_tune_chipset (ide_drive_t *drive, u8 speed)
 {
-       struct pci_dev *dev     = HWIF(drive)->pci_dev;
-
-       if (hpt_minimum_revision(dev, 8))
-               hpt374_tune_chipset(drive, speed);
-#if 0
-       else if (hpt_minimum_revision(dev, 7))
-               hpt371_tune_chipset(drive, speed);
-       else if (hpt_minimum_revision(dev, 6))
-               hpt302_tune_chipset(drive, speed);
-#endif
-       else if (hpt_minimum_revision(dev, 5))
-               hpt372_tune_chipset(drive, speed);
-       else if (hpt_minimum_revision(dev, 3))
-               hpt370_tune_chipset(drive, speed);
-       else if (hpt_minimum_revision(dev, 2))
-               hpt368_tune_chipset(drive, speed);
-       else
-                hpt366_tune_chipset(drive, speed);
-
-       return ((int) ide_config_drive_speed(drive, speed));
+       ide_hwif_t *hwif        = drive->hwif;
+       struct hpt_info *info   = ide_get_hwifdata(hwif);
+
+       if (info->revision >= 8)
+               return hpt372_tune_chipset(drive, speed); /* not a typo */
+       else if (info->revision >= 5)
+               return hpt372_tune_chipset(drive, speed);
+       else if (info->revision >= 3)
+               return hpt370_tune_chipset(drive, speed);
+       else    /* hpt368: hpt_minimum_revision(dev, 2) */
+               return hpt36x_tune_chipset(drive, speed);
 }
 
 static void hpt3xx_tune_drive (ide_drive_t *drive, u8 pio)
@@ -457,8 +657,14 @@ static void hpt3xx_tune_drive (ide_drive_t *drive, u8 pio)
 static int config_chipset_for_dma (ide_drive_t *drive)
 {
        u8 speed = ide_dma_speed(drive, hpt3xx_ratemask(drive));
+       ide_hwif_t *hwif = drive->hwif;
+       struct hpt_info *info   = ide_get_hwifdata(hwif);
 
-       if (!(speed))
+       if (!speed)
+               return 0;
+
+       /* If we don't have any timings we can't do a lot */
+       if (info->speed == NULL)
                return 0;
 
        (void) hpt3xx_tune_chipset(drive, speed);
@@ -472,7 +678,7 @@ static int hpt3xx_quirkproc (ide_drive_t *drive)
 
 static void hpt3xx_intrproc (ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = HWIF(drive);
+       ide_hwif_t *hwif = drive->hwif;
 
        if (drive->quirk_list)
                return;
@@ -482,24 +688,26 @@ static void hpt3xx_intrproc (ide_drive_t *drive)
 
 static void hpt3xx_maskproc (ide_drive_t *drive, int mask)
 {
-       struct pci_dev *dev = HWIF(drive)->pci_dev;
+       ide_hwif_t *hwif = drive->hwif;
+       struct hpt_info *info = ide_get_hwifdata(hwif);
+       struct pci_dev *dev = hwif->pci_dev;
 
        if (drive->quirk_list) {
-               if (hpt_minimum_revision(dev,3)) {
+               if (info->revision >= 3) {
                        u8 reg5a = 0;
                        pci_read_config_byte(dev, 0x5a, &reg5a);
                        if (((reg5a & 0x10) >> 4) != mask)
                                pci_write_config_byte(dev, 0x5a, mask ? (reg5a | 0x10) : (reg5a & ~0x10));
                } else {
                        if (mask) {
-                               disable_irq(HWIF(drive)->irq);
+                               disable_irq(hwif->irq);
                        } else {
-                               enable_irq(HWIF(drive)->irq);
+                               enable_irq(hwif->irq);
                        }
                }
        } else {
                if (IDE_CONTROL_REG)
-                       HWIF(drive)->OUTB(mask ? (drive->ctl | 2) :
+                       hwif->OUTB(mask ? (drive->ctl | 2) :
                                                 (drive->ctl & ~2),
                                                 IDE_CONTROL_REG);
        }
@@ -507,41 +715,22 @@ static void hpt3xx_maskproc (ide_drive_t *drive, int mask)
 
 static int hpt366_config_drive_xfer_rate (ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = HWIF(drive);
+       ide_hwif_t *hwif        = drive->hwif;
        struct hd_driveid *id   = drive->id;
 
        drive->init_speed = 0;
 
-       if (id && (id->capability & 1) && drive->autodma) {
-               /* Consult the list of known "bad" drives */
-               if (__ide_dma_bad_drive(drive))
-                       goto fast_ata_pio;
-               if (id->field_valid & 4) {
-                       if (id->dma_ultra & hwif->ultra_mask) {
-                               /* Force if Capable UltraDMA */
-                               int dma = config_chipset_for_dma(drive);
-                               if ((id->field_valid & 2) && !dma)
-                                       goto try_dma_modes;
-                       }
-               } else if (id->field_valid & 2) {
-try_dma_modes:
-                       if (id->dma_mword & hwif->mwdma_mask) {
-                               /* Force if Capable regular DMA modes */
-                               if (!config_chipset_for_dma(drive))
-                                       goto no_dma_set;
-                       }
-               } else if (__ide_dma_good_drive(drive) &&
-                          (id->eide_dma_time < 150)) {
-                       /* Consult the list of known "good" drives */
-                       if (!config_chipset_for_dma(drive))
-                               goto no_dma_set;
-               } else {
-                       goto fast_ata_pio;
+       if ((id->capability & 1) && drive->autodma) {
+
+               if (ide_use_dma(drive)) {
+                       if (config_chipset_for_dma(drive))
+                               return hwif->ide_dma_on(drive);
                }
-               return hwif->ide_dma_on(drive);
+
+               goto fast_ata_pio;
+
        } else if ((id->capability & 8) || (id->field_valid & 2)) {
 fast_ata_pio:
-no_dma_set:
                hpt3xx_tune_drive(drive, 5);
                return hwif->ide_dma_off_quietly(drive);
        }
@@ -565,15 +754,6 @@ static int hpt366_ide_dma_lostirq (ide_drive_t *drive)
                drive->name, __FUNCTION__, reg50h, reg52h, reg5ah);
        if (reg5ah & 0x10)
                pci_write_config_byte(dev, 0x5a, reg5ah & ~0x10);
-#if 0
-       /* how about we flush and reset, mmmkay? */
-       pci_write_config_byte(dev, 0x51, 0x1F);
-       /* fall through to a reset */
-       case ide_dma_begin:
-       case ide_dma_end:
-       /* reset the chips state over and over.. */
-       pci_write_config_byte(dev, 0x51, 0x13);
-#endif
        return __ide_dma_lostirq(drive);
 }
 
@@ -584,12 +764,12 @@ static void hpt370_clear_engine (ide_drive_t *drive)
        udelay(10);
 }
 
-static int hpt370_ide_dma_begin (ide_drive_t *drive)
+static void hpt370_ide_dma_start(ide_drive_t *drive)
 {
 #ifdef HPT_RESET_STATE_ENGINE
        hpt370_clear_engine(drive);
 #endif
-       return __ide_dma_begin(drive);
+       ide_dma_start(drive);
 }
 
 static int hpt370_ide_dma_end (ide_drive_t *drive)
@@ -616,7 +796,7 @@ static void hpt370_lostirq_timeout (ide_drive_t *drive)
        u8 dma_stat = 0, dma_cmd = 0;
 
        pci_read_config_byte(HWIF(drive)->pci_dev, reginfo, &bfifo);
-       printk("%s: %d bytes in FIFO\n", drive->name, bfifo);
+       printk(KERN_DEBUG "%s: %d bytes in FIFO\n", drive->name, bfifo);
        hpt370_clear_engine(drive);
        /* get dma command mode */
        dma_cmd = hwif->INB(hwif->dma_command);
@@ -680,200 +860,264 @@ static int hpt374_ide_dma_end (ide_drive_t *drive)
        return __ide_dma_end(drive);
 }
 
-/*
- * Since SUN Cobalt is attempting to do this operation, I should disclose
- * this has been a long time ago Thu Jul 27 16:40:57 2000 was the patch date
- * HOTSWAP ATA Infrastructure.
+/**
+ *     hpt3xxn_set_clock       -       perform clock switching dance
+ *     @hwif: hwif to switch
+ *     @mode: clocking mode (0x21 for write, 0x23 otherwise)
+ *
+ *     Switch the DPLL clock on the HPT3xxN devices. This is a right mess.
+ *     NOTE: avoid touching the disabled primary channel on HPT371N -- it
+ *     doesn't physically exist anyway...
  */
 
-static void hpt3xx_reset (ide_drive_t *drive)
+static void hpt3xxn_set_clock(ide_hwif_t *hwif, u8 mode)
 {
-#if 0
-       unsigned long high_16   = pci_resource_start(HWIF(drive)->pci_dev, 4);
-       u8 reset        = (HWIF(drive)->channel) ? 0x80 : 0x40;
-       u8 reg59h       = 0;
-
-       pci_read_config_byte(HWIF(drive)->pci_dev, 0x59, &reg59h);
-       pci_write_config_byte(HWIF(drive)->pci_dev, 0x59, reg59h|reset);
-       pci_write_config_byte(HWIF(drive)->pci_dev, 0x59, reg59h);
-#endif
-}
+       u8 mcr1, scr2 = hwif->INB(hwif->dma_master + 0x7b);
 
-static int hpt3xx_tristate (ide_drive_t * drive, int state)
-{
-       ide_hwif_t *hwif        = HWIF(drive);
-       struct pci_dev *dev     = hwif->pci_dev;
-       u8 reg59h = 0, reset    = (hwif->channel) ? 0x80 : 0x40;
-       u8 regXXh = 0, state_reg= (hwif->channel) ? 0x57 : 0x53;
+       if ((scr2 & 0x7f) == mode)
+               return;
 
-       if (!hwif)
-               return -EINVAL;
+       /* MISC. control register 1 has the channel enable bit... */
+       mcr1 = hwif->INB(hwif->dma_master + 0x70);
 
-//     hwif->bus_state = state;
+       /* Tristate the bus */
+       if (mcr1 & 0x04)
+               hwif->OUTB(0x80, hwif->dma_master + 0x73);
+       hwif->OUTB(0x80, hwif->dma_master + 0x77);
 
-       pci_read_config_byte(dev, 0x59, &reg59h);
-       pci_read_config_byte(dev, state_reg, &regXXh);
+       /* Switch clock and reset channels */
+       hwif->OUTB(mode, hwif->dma_master + 0x7b);
+       hwif->OUTB(0xc0, hwif->dma_master + 0x79);
 
-       if (state) {
-               (void) ide_do_reset(drive);
-               pci_write_config_byte(dev, state_reg, regXXh|0x80);
-               pci_write_config_byte(dev, 0x59, reg59h|reset);
-       } else {
-               pci_write_config_byte(dev, 0x59, reg59h & ~(reset));
-               pci_write_config_byte(dev, state_reg, regXXh & ~(0x80));
-               (void) ide_do_reset(drive);
-       }
-       return 0;
+       /* Reset state machines */
+       if (mcr1 & 0x04)
+               hwif->OUTB(0x37, hwif->dma_master + 0x70);
+       hwif->OUTB(0x37, hwif->dma_master + 0x74);
+
+       /* Complete reset */
+       hwif->OUTB(0x00, hwif->dma_master + 0x79);
+
+       /* Reconnect channels to bus */
+       if (mcr1 & 0x04)
+               hwif->OUTB(0x00, hwif->dma_master + 0x73);
+       hwif->OUTB(0x00, hwif->dma_master + 0x77);
+}
+
+/**
+ *     hpt3xxn_rw_disk         -       prepare for I/O
+ *     @drive: drive for command
+ *     @rq: block request structure
+ *
+ *     This is called when a disk I/O is issued to HPT3xxN.
+ *     We need it because of the clock switching.
+ */
+
+static void hpt3xxn_rw_disk(ide_drive_t *drive, struct request *rq)
+{
+       ide_hwif_t *hwif        = HWIF(drive);
+       u8 wantclock            = rq_data_dir(rq) ? 0x23 : 0x21;
+
+       hpt3xxn_set_clock(hwif, wantclock);
 }
 
 /* 
- * set/get power state for a drive.
- * turning the power off does the following things:
- *   1) soft-reset the drive
- *   2) tri-states the ide bus
+ * Set/get power state for a drive.
  *
- * when we turn things back on, we need to re-initialize things.
+ * When we turn the power back on, we need to re-initialize things.
  */
 #define TRISTATE_BIT  0x8000
-static int hpt370_busproc(ide_drive_t * drive, int state)
+
+static int hpt3xx_busproc(ide_drive_t *drive, int state)
 {
-       ide_hwif_t *hwif        = HWIF(drive);
+       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = hwif->pci_dev;
-       u8 tristate = 0, resetmask = 0, bus_reg = 0;
-       u16 tri_reg;
-
-       if (!hwif)
-               return -EINVAL;
+       u8  tristate, resetmask, bus_reg = 0;
+       u16 tri_reg = 0;
 
        hwif->bus_state = state;
 
        if (hwif->channel) { 
                /* secondary channel */
-               tristate = 0x56;
-               resetmask = 0x80; 
+               tristate  = 0x56;
+               resetmask = 0x80;
        } else { 
                /* primary channel */
-               tristate = 0x52;
+               tristate  = 0x52;
                resetmask = 0x40;
        }
 
-       /* grab status */
+       /* Grab the status. */
        pci_read_config_word(dev, tristate, &tri_reg);
        pci_read_config_byte(dev, 0x59, &bus_reg);
 
-       /* set the state. we don't set it if we don't need to do so.
-        * make sure that the drive knows that it has failed if it's off */
+       /*
+        * Set the state. We don't set it if we don't need to do so.
+        * Make sure that the drive knows that it has failed if it's off.
+        */
        switch (state) {
        case BUSSTATE_ON:
-               hwif->drives[0].failures = 0;
-               hwif->drives[1].failures = 0;
-               if ((bus_reg & resetmask) == 0)
+               if (!(bus_reg & resetmask))
                        return 0;
-               tri_reg &= ~TRISTATE_BIT;
-               bus_reg &= ~resetmask;
-               break;
+               hwif->drives[0].failures = hwif->drives[1].failures = 0;
+
+               pci_write_config_byte(dev, 0x59, bus_reg & ~resetmask);
+               pci_write_config_word(dev, tristate, tri_reg & ~TRISTATE_BIT);
+               return 0;
        case BUSSTATE_OFF:
-               hwif->drives[0].failures = hwif->drives[0].max_failures + 1;
-               hwif->drives[1].failures = hwif->drives[1].max_failures + 1;
-               if ((tri_reg & TRISTATE_BIT) == 0 && (bus_reg & resetmask))
+               if ((bus_reg & resetmask) && !(tri_reg & TRISTATE_BIT))
                        return 0;
                tri_reg &= ~TRISTATE_BIT;
-               bus_reg |= resetmask;
                break;
        case BUSSTATE_TRISTATE:
-               hwif->drives[0].failures = hwif->drives[0].max_failures + 1;
-               hwif->drives[1].failures = hwif->drives[1].max_failures + 1;
-               if ((tri_reg & TRISTATE_BIT) && (bus_reg & resetmask))
+               if ((bus_reg & resetmask) &&  (tri_reg & TRISTATE_BIT))
                        return 0;
                tri_reg |= TRISTATE_BIT;
-               bus_reg |= resetmask;
                break;
+       default:
+               return -EINVAL;
        }
-       pci_write_config_byte(dev, 0x59, bus_reg);
-       pci_write_config_word(dev, tristate, tri_reg);
 
+       hwif->drives[0].failures = hwif->drives[0].max_failures + 1;
+       hwif->drives[1].failures = hwif->drives[1].max_failures + 1;
+
+       pci_write_config_word(dev, tristate, tri_reg);
+       pci_write_config_byte(dev, 0x59, bus_reg | resetmask);
        return 0;
 }
 
-static int __devinit init_hpt37x(struct pci_dev *dev)
+static void __devinit hpt366_clocking(ide_hwif_t *hwif)
 {
-       int adjust, i;
-       u16 freq;
-       u32 pll;
-       u8 reg5bh;
+       u32 reg1        = 0;
+       struct hpt_info *info = ide_get_hwifdata(hwif);
 
-#if 1
-       u8 reg5ah = 0;
-       pci_read_config_byte(dev, 0x5a, &reg5ah);
-       /* interrupt force enable */
-       pci_write_config_byte(dev, 0x5a, (reg5ah & ~0x10));
-#endif
+       pci_read_config_dword(hwif->pci_dev, 0x40, &reg1);
 
+       /* detect bus speed by looking at control reg timing: */
+       switch((reg1 >> 8) & 7) {
+               case 5:
+                       info->speed = forty_base_hpt36x;
+                       break;
+               case 9:
+                       info->speed = twenty_five_base_hpt36x;
+                       break;
+               case 7:
+               default:
+                       info->speed = thirty_three_base_hpt36x;
+                       break;
+       }
+}
+
+static void __devinit hpt37x_clocking(ide_hwif_t *hwif)
+{
+       struct hpt_info *info = ide_get_hwifdata(hwif);
+       struct pci_dev *dev = hwif->pci_dev;
+       int adjust, i;
+       u16 freq = 0;
+       u32 pll, temp = 0;
+       u8 reg5bh = 0, mcr1 = 0;
+       
        /*
         * default to pci clock. make sure MA15/16 are set to output
-        * to prevent drives having problems with 40-pin cables.
+        * to prevent drives having problems with 40-pin cables. Needed
+        * for some drives such as IBM-DTLA which will not enter ready
+        * state on reset when PDIAG is a input.
+        *
+        * ToDo: should we set 0x21 when using PLL mode ?
         */
        pci_write_config_byte(dev, 0x5b, 0x23);
 
        /*
-        * set up the PLL. we need to adjust it so that it's stable. 
-        * freq = Tpll * 192 / Tpci
+        * We'll have to read f_CNT value in order to determine
+        * the PCI clock frequency according to the following ratio:
+        *
+        * f_CNT = Fpci * 192 / Fdpll
+        *
+        * First try reading the register in which the HighPoint BIOS
+        * saves f_CNT value before  reprogramming the DPLL from its
+        * default setting (which differs for the various chips).
+        * NOTE: This register is only accessible via I/O space.
+        *
+        * In case the signature check fails, we'll have to resort to
+        * reading the f_CNT register itself in hopes that nobody has
+        * touched the DPLL yet...
         */
-       pci_read_config_word(dev, 0x78, &freq);
-       freq &= 0x1FF;
-       if (freq < 0xa0) {
-               pll = F_LOW_PCI_33;
-               if (hpt_minimum_revision(dev,8))
-                       pci_set_drvdata(dev, (void *) thirty_three_base_hpt374);
-               else if (hpt_minimum_revision(dev,5))
-                       pci_set_drvdata(dev, (void *) thirty_three_base_hpt372);
-               else if (hpt_minimum_revision(dev,4))
-                       pci_set_drvdata(dev, (void *) thirty_three_base_hpt370a);
-               else
-                       pci_set_drvdata(dev, (void *) thirty_three_base_hpt370);
-               printk("HPT37X: using 33MHz PCI clock\n");
-       } else if (freq < 0xb0) {
-               pll = F_LOW_PCI_40;
-       } else if (freq < 0xc8) {
-               pll = F_LOW_PCI_50;
-               if (hpt_minimum_revision(dev,8))
-                       pci_set_drvdata(dev, NULL);
-               else if (hpt_minimum_revision(dev,5))
-                       pci_set_drvdata(dev, (void *) fifty_base_hpt372);
-               else if (hpt_minimum_revision(dev,4))
-                       pci_set_drvdata(dev, (void *) fifty_base_hpt370a);
-               else
-                       pci_set_drvdata(dev, (void *) fifty_base_hpt370a);
-               printk("HPT37X: using 50MHz PCI clock\n");
-       } else {
-               pll = F_LOW_PCI_66;
-               if (hpt_minimum_revision(dev,8))
-               {
-                       printk(KERN_ERR "HPT37x: 66MHz timings are not supported.\n");
-                       pci_set_drvdata(dev, NULL);
+       temp = inl(pci_resource_start(dev, 4) + 0x90);
+       if ((temp & 0xFFFFF000) != 0xABCDE000) {
+               printk(KERN_WARNING "HPT37X: no clock data saved by BIOS\n");
+
+               /* Calculate the average value of f_CNT */
+               for (temp = i = 0; i < 128; i++) {
+                       pci_read_config_word(dev, 0x78, &freq);
+                       temp += freq & 0x1ff;
+                       mdelay(1);
                }
-               else if (hpt_minimum_revision(dev,5))
-                       pci_set_drvdata(dev, (void *) sixty_six_base_hpt372);
-               else if (hpt_minimum_revision(dev,4))
-                       pci_set_drvdata(dev, (void *) sixty_six_base_hpt370a);
+               freq = temp / 128;
+       } else
+               freq = temp & 0x1ff;
+
+       /*
+        * HPT3xxN chips use different PCI clock information.
+        * Currently we always set up the PLL for them.
+        */
+
+       if (info->flags & IS_3xxN) {
+               if(freq < 0x55)
+                       pll = F_LOW_PCI_33;
+               else if(freq < 0x70)
+                       pll = F_LOW_PCI_40;
+               else if(freq < 0x7F)
+                       pll = F_LOW_PCI_50;
                else
-                       pci_set_drvdata(dev, (void *) sixty_six_base_hpt370);
-               printk("HPT37X: using 66MHz PCI clock\n");
+                       pll = F_LOW_PCI_66;
+
+               printk(KERN_INFO "HPT3xxN detected, FREQ: %d, PLL: %d\n", freq, pll);
        }
+       else
+       {
+               if(freq < 0x9C)
+                       pll = F_LOW_PCI_33;
+               else if(freq < 0xb0)
+                       pll = F_LOW_PCI_40;
+               else if(freq <0xc8)
+                       pll = F_LOW_PCI_50;
+               else
+                       pll = F_LOW_PCI_66;
        
+               if (pll == F_LOW_PCI_33) {
+                       info->speed = thirty_three_base_hpt37x;
+                       printk(KERN_DEBUG "HPT37X: using 33MHz PCI clock\n");
+               } else if (pll == F_LOW_PCI_40) {
+                       /* Unsupported */
+               } else if (pll == F_LOW_PCI_50) {
+                       info->speed = fifty_base_hpt37x;
+                       printk(KERN_DEBUG "HPT37X: using 50MHz PCI clock\n");
+               } else {
+                       info->speed = sixty_six_base_hpt37x;
+                       printk(KERN_DEBUG "HPT37X: using 66MHz PCI clock\n");
+               }
+       }
+
+       if (pll == F_LOW_PCI_66)
+               info->flags |= PCI_66MHZ;
+
        /*
         * only try the pll if we don't have a table for the clock
         * speed that we're running at. NOTE: the internal PLL will
         * result in slow reads when using a 33MHz PCI clock. we also
         * don't like to use the PLL because it will cause glitches
         * on PRST/SRST when the HPT state engine gets reset.
+        *
+        * ToDo: Use 66MHz PLL when ATA133 devices are present on a
+        * 372 device so we can get ATA133 support
         */
-       if (pci_get_drvdata(dev)) 
+       if (info->speed)
                goto init_hpt37X_done;
+
+       info->flags |= PLL_MODE;
        
        /*
-        * adjust PLL based upon PCI clock, enable it, and wait for
-        * stabilization.
+        * Adjust the PLL based upon the PCI clock, enable it, and
+        * wait for stabilization...
         */
        adjust = 0;
        freq = (pll < F_LOW_PCI_50) ? 2 : 4;
@@ -896,14 +1140,8 @@ static int __devinit init_hpt37x(struct pci_dev *dev)
                                pci_write_config_dword(dev, 0x5c, 
                                                       pll & ~0x100);
                                pci_write_config_byte(dev, 0x5b, 0x21);
-                               if (hpt_minimum_revision(dev,8))
-                                       pci_set_drvdata(dev, (void *) fifty_base_hpt370a);
-                               else if (hpt_minimum_revision(dev,5))
-                                       pci_set_drvdata(dev, (void *) fifty_base_hpt372);
-                               else if (hpt_minimum_revision(dev,4))
-                                       pci_set_drvdata(dev, (void *) fifty_base_hpt370a);
-                               else
-                                       pci_set_drvdata(dev, (void *) fifty_base_hpt370a);
+
+                               info->speed = fifty_base_hpt37x;
                                printk("HPT37X: using 50MHz internal PLL\n");
                                goto init_hpt37X_done;
                        }
@@ -916,10 +1154,27 @@ pll_recal:
        } 
 
 init_hpt37X_done:
-       /* reset state engine */
-       pci_write_config_byte(dev, 0x50, 0x37); 
-       pci_write_config_byte(dev, 0x54, 0x37); 
+       if (!info->speed)
+               printk(KERN_ERR "HPT37x%s: unknown bus timing [%d %d].\n",
+                      (info->flags & IS_3xxN) ? "N" : "", pll, freq);
+       /*
+        * Reset the state engines.
+        * NOTE: avoid accidentally enabling the primary channel on HPT371N.
+        */
+       pci_read_config_byte(dev, 0x50, &mcr1);
+       if (mcr1 & 0x04)
+               pci_write_config_byte(dev, 0x50, 0x37);
+       pci_write_config_byte(dev, 0x54, 0x37);
        udelay(100);
+}
+
+static int __devinit init_hpt37x(struct pci_dev *dev)
+{
+       u8 reg5ah;
+
+       pci_read_config_byte(dev, 0x5a, &reg5ah);
+       /* interrupt force enable */
+       pci_write_config_byte(dev, 0x5a, (reg5ah & ~0x10));
        return 0;
 }
 
@@ -936,70 +1191,33 @@ static int __devinit init_hpt366(struct pci_dev *dev)
                pci_write_config_byte(dev, 0x51, drive_fast & ~0x80);
        pci_read_config_dword(dev, 0x40, &reg1);
                                                                        
-       /* detect bus speed by looking at control reg timing: */
-       switch((reg1 >> 8) & 7) {
-               case 5:
-                       pci_set_drvdata(dev, (void *) forty_base_hpt366);
-                       break;
-               case 9:
-                       pci_set_drvdata(dev, (void *) twenty_five_base_hpt366);
-                       break;
-               case 7:
-               default:
-                       pci_set_drvdata(dev, (void *) thirty_three_base_hpt366);
-                       break;
-       }
-
-       if (!pci_get_drvdata(dev))
-       {
-               printk(KERN_ERR "hpt366: unknown bus timing.\n");
-               pci_set_drvdata(dev, NULL);
-       }
        return 0;
 }
 
 static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const char *name)
 {
        int ret = 0;
-       u8 test = 0;
 
+       /*
+        * FIXME: Not portable. Also, why do we enable the ROM in the first place?
+        * We don't seem to be using it.
+        */
        if (dev->resource[PCI_ROM_RESOURCE].start)
-               pci_write_config_byte(dev, PCI_ROM_ADDRESS,
+               pci_write_config_dword(dev, PCI_ROM_ADDRESS,
                        dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
 
-       pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &test);
-       if (test != (L1_CACHE_BYTES / 4))
-               pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE,
-                       (L1_CACHE_BYTES / 4));
-
-       pci_read_config_byte(dev, PCI_LATENCY_TIMER, &test);
-       if (test != 0x78)
-               pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x78);
-
-       pci_read_config_byte(dev, PCI_MIN_GNT, &test);
-       if (test != 0x08)
-               pci_write_config_byte(dev, PCI_MIN_GNT, 0x08);
-
-       pci_read_config_byte(dev, PCI_MAX_LAT, &test);
-       if (test != 0x08)
-               pci_write_config_byte(dev, PCI_MAX_LAT, 0x08);
+       pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, (L1_CACHE_BYTES / 4));
+       pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x78);
+       pci_write_config_byte(dev, PCI_MIN_GNT, 0x08);
+       pci_write_config_byte(dev, PCI_MAX_LAT, 0x08);
 
-       if (hpt_minimum_revision(dev, 3)) {
+       if (hpt_revision(dev) >= 3)
                ret = init_hpt37x(dev);
-       } else {
-               ret =init_hpt366(dev);
-       }
+       else
+               ret = init_hpt366(dev);
+
        if (ret)
                return ret;
-       
-#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
-       hpt_devs[n_hpt_devs++] = dev;
-
-       if (!hpt366_proc) {
-               hpt366_proc = 1;
-               ide_pci_create_host_proc("hpt366", hpt366_get_info);
-       }
-#endif /* DISPLAY_HPT366_TIMINGS && CONFIG_PROC_FS */
 
        return dev->irq;
 }
@@ -1007,20 +1225,37 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha
 static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
 {
        struct pci_dev *dev             = hwif->pci_dev;
+       struct hpt_info *info           = ide_get_hwifdata(hwif);
        u8 ata66 = 0, regmask           = (hwif->channel) ? 0x01 : 0x02;
-
+       int serialize                   = HPT_SERIALIZE_IO;
+       
        hwif->tuneproc                  = &hpt3xx_tune_drive;
        hwif->speedproc                 = &hpt3xx_tune_chipset;
        hwif->quirkproc                 = &hpt3xx_quirkproc;
        hwif->intrproc                  = &hpt3xx_intrproc;
        hwif->maskproc                  = &hpt3xx_maskproc;
+       
+       /*
+        * HPT3xxN chips have some complications:
+        *
+        * - on 33 MHz PCI we must clock switch
+        * - on 66 MHz PCI we must NOT use the PCI clock
+        */
+       if ((info->flags & (IS_3xxN | PCI_66MHZ)) == IS_3xxN) {
+               /*
+                * Clock is shared between the channels,
+                * so we'll have to serialize them... :-(
+                */
+               serialize = 1;
+               hwif->rw_disk = &hpt3xxn_rw_disk;
+       }
 
        /*
         * The HPT37x uses the CBLID pins as outputs for MA15/MA16
         * address lines to access an external eeprom.  To read valid
         * cable detect state the pins must be enabled as inputs.
         */
-       if (hpt_minimum_revision(dev, 8) && PCI_FUNC(dev->devfn) & 1) {
+       if (info->revision >= 8 && (PCI_FUNC(dev->devfn) & 1)) {
                /*
                 * HPT374 PCI function 1
                 * - set bit 15 of reg 0x52 to enable TCBLID as input
@@ -1035,7 +1270,7 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
                pci_read_config_byte(dev, 0x5a, &ata66);
                pci_write_config_word(dev, 0x52, mcr3);
                pci_write_config_word(dev, 0x56, mcr6);
-       } else if (hpt_minimum_revision(dev, 3)) {
+       } else if (info->revision >= 3) {
                /*
                 * HPT370/372 and 374 pcifn 0
                 * - clear bit 0 of 0x5b to enable P/SCBLID as inputs
@@ -1056,30 +1291,15 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
                PCI_FUNC(hwif->pci_dev->devfn));
 #endif /* DEBUG */
 
-#ifdef HPT_SERIALIZE_IO
-       /* serialize access to this device */
-       if (hwif->mate)
+       /* Serialize access to this device */
+       if (serialize && hwif->mate)
                hwif->serialized = hwif->mate->serialized = 1;
-#endif
 
-       if (hpt_minimum_revision(dev,3)) {
-               u8 reg5ah = 0;
-                       pci_write_config_byte(dev, 0x5a, reg5ah & ~0x10);
-               /*
-                * set up ioctl for power status.
-                * note: power affects both
-                * drives on each channel
-                */
-               hwif->resetproc = &hpt3xx_reset;
-               hwif->busproc   = &hpt370_busproc;
-//             hwif->drives[0].autotune = hwif->drives[1].autotune = 1;
-       } else if (hpt_minimum_revision(dev,2)) {
-               hwif->resetproc = &hpt3xx_reset;
-               hwif->busproc   = &hpt3xx_tristate;
-       } else {
-               hwif->resetproc = &hpt3xx_reset;
-               hwif->busproc   = &hpt3xx_tristate;
-       }
+       /*
+        * Set up ioctl for power status.
+        * NOTE:  power affects both drives on each channel.
+        */
+       hwif->busproc = &hpt3xx_busproc;
 
        if (!hwif->dma_base) {
                hwif->drives[0].autotune = 1;
@@ -1094,18 +1314,18 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
                hwif->udma_four = ((ata66 & regmask) ? 0 : 1);
        hwif->ide_dma_check = &hpt366_config_drive_xfer_rate;
 
-       if (hpt_minimum_revision(dev,8)) {
+       if (info->revision >= 8) {
                hwif->ide_dma_test_irq = &hpt374_ide_dma_test_irq;
                hwif->ide_dma_end = &hpt374_ide_dma_end;
-       } else if (hpt_minimum_revision(dev,5)) {
+       } else if (info->revision >= 5) {
                hwif->ide_dma_test_irq = &hpt374_ide_dma_test_irq;
                hwif->ide_dma_end = &hpt374_ide_dma_end;
-       } else if (hpt_minimum_revision(dev,3)) {
-               hwif->ide_dma_begin = &hpt370_ide_dma_begin;
+       } else if (info->revision >= 3) {
+               hwif->dma_start = &hpt370_ide_dma_start;
                hwif->ide_dma_end = &hpt370_ide_dma_end;
                hwif->ide_dma_timeout = &hpt370_ide_dma_timeout;
                hwif->ide_dma_lostirq = &hpt370_ide_dma_lostirq;
-       } else if (hpt_minimum_revision(dev,2))
+       } else if (info->revision >= 2)
                hwif->ide_dma_lostirq = &hpt366_ide_dma_lostirq;
        else
                hwif->ide_dma_lostirq = &hpt366_ide_dma_lostirq;
@@ -1118,6 +1338,7 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
 
 static void __devinit init_dma_hpt366(ide_hwif_t *hwif, unsigned long dmabase)
 {
+       struct hpt_info *info   = ide_get_hwifdata(hwif);
        u8 masterdma    = 0, slavedma = 0;
        u8 dma_new      = 0, dma_old = 0;
        u8 primary      = hwif->channel ? 0x4b : 0x43;
@@ -1127,9 +1348,8 @@ static void __devinit init_dma_hpt366(ide_hwif_t *hwif, unsigned long dmabase)
        if (!dmabase)
                return;
                
-       if(pci_get_drvdata(hwif->pci_dev) == NULL)
-       {
-               printk(KERN_WARNING "hpt: no known IDE timings, disabling DMA.\n");
+       if(info->speed == NULL) {
+               printk(KERN_WARNING "hpt366: no known IDE timings, disabling DMA.\n");
                return;
        }
 
@@ -1151,12 +1371,53 @@ static void __devinit init_dma_hpt366(ide_hwif_t *hwif, unsigned long dmabase)
        ide_setup_dma(hwif, dmabase, 8);
 }
 
-static void __devinit init_setup_hpt374(struct pci_dev *dev, ide_pci_device_t *d)
+/*
+ *     We "borrow" this hook in order to set the data structures
+ *     up early enough before dma or init_hwif calls are made.
+ */
+
+static void __devinit init_iops_hpt366(ide_hwif_t *hwif)
+{
+       struct hpt_info *info   = kzalloc(sizeof(struct hpt_info), GFP_KERNEL);
+       struct pci_dev  *dev    = hwif->pci_dev;
+       u16 did                 = dev->device;
+       u8  rid                 = 0;
+
+       if(info == NULL) {
+               printk(KERN_WARNING "hpt366: out of memory.\n");
+               return;
+       }
+       ide_set_hwifdata(hwif, info);
+
+       /* Avoid doing the same thing twice. */
+       if (hwif->channel && hwif->mate) {
+               memcpy(info, ide_get_hwifdata(hwif->mate), sizeof(struct hpt_info));
+               return;
+       }
+
+       pci_read_config_byte(dev, PCI_CLASS_REVISION, &rid);
+
+       if (( did == PCI_DEVICE_ID_TTI_HPT366  && rid == 6) ||
+           ((did == PCI_DEVICE_ID_TTI_HPT372  ||
+             did == PCI_DEVICE_ID_TTI_HPT302  ||
+             did == PCI_DEVICE_ID_TTI_HPT371) && rid > 1) ||
+             did == PCI_DEVICE_ID_TTI_HPT372N)
+               info->flags |= IS_3xxN;
+
+       info->revision = hpt_revision(dev);
+
+       if (info->revision >= 3)
+               hpt37x_clocking(hwif);
+       else
+               hpt366_clocking(hwif);
+}
+
+static int __devinit init_setup_hpt374(struct pci_dev *dev, ide_pci_device_t *d)
 {
        struct pci_dev *findev = NULL;
 
        if (PCI_FUNC(dev->devfn) & 1)
-               return;
+               return -ENODEV;
 
        while ((findev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, findev)) != NULL) {
                if ((findev->vendor == dev->vendor) &&
@@ -1169,40 +1430,63 @@ static void __devinit init_setup_hpt374(struct pci_dev *dev, ide_pci_device_t *d
                                printk(KERN_WARNING "%s: pci-config space interrupt "
                                        "fixed.\n", d->name);
                        }
-                       ide_setup_pci_devices(dev, findev, d);
-                       return;
+                       return ide_setup_pci_devices(dev, findev, d);
                }
        }
-       ide_setup_pci_device(dev, d);
+       return ide_setup_pci_device(dev, d);
+}
+
+static int __devinit init_setup_hpt37x(struct pci_dev *dev, ide_pci_device_t *d)
+{
+       return ide_setup_pci_device(dev, d);
 }
 
-static void __devinit init_setup_hpt37x(struct pci_dev *dev, ide_pci_device_t *d)
+static int __devinit init_setup_hpt371(struct pci_dev *dev, ide_pci_device_t *d)
 {
-       ide_setup_pci_device(dev, d);
+       u8 mcr1 = 0;
+
+       /*
+        * HPT371 chips physically have only one channel, the secondary one,
+        * but the primary channel registers do exist!  Go figure...
+        * So,  we manually disable the non-existing channel here
+        * (if the BIOS hasn't done this already).
+        */
+       pci_read_config_byte(dev, 0x50, &mcr1);
+       if (mcr1 & 0x04)
+               pci_write_config_byte(dev, 0x50, (mcr1 & ~0x04));
+
+       return ide_setup_pci_device(dev, d);
 }
 
-static void __devinit init_setup_hpt366(struct pci_dev *dev, ide_pci_device_t *d)
+static int __devinit init_setup_hpt366(struct pci_dev *dev, ide_pci_device_t *d)
 {
        struct pci_dev *findev = NULL;
        u8 pin1 = 0, pin2 = 0;
        unsigned int class_rev;
        char *chipset_names[] = {"HPT366", "HPT366",  "HPT368",
-                                "HPT370", "HPT370A", "HPT372"};
+                                "HPT370", "HPT370A", "HPT372",
+                                "HPT372N" };
 
        if (PCI_FUNC(dev->devfn) & 1)
-               return;
+               return -ENODEV;
 
        pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
        class_rev &= 0xff;
 
-       strcpy(d->name, chipset_names[class_rev]);
+       if(dev->device == PCI_DEVICE_ID_TTI_HPT372N)
+               class_rev = 6;
+               
+       if(class_rev <= 6)
+               d->name = chipset_names[class_rev];
 
        switch(class_rev) {
+               case 6:
                case 5:
                case 4:
-               case 3: ide_setup_pci_device(dev, d);
-                       return;
-               default:        break;
+               case 3:
+                       goto init_single;
+               default:
+                       break;
        }
 
        d->channels = 1;
@@ -1220,13 +1504,78 @@ static void __devinit init_setup_hpt366(struct pci_dev *dev, ide_pci_device_t *d
                                        "pin1=%d pin2=%d\n", d->name,
                                        pin1, pin2);
                        }
-                       ide_setup_pci_devices(dev, findev, d);
-                       return;
+                       return ide_setup_pci_devices(dev, findev, d);
                }
        }
-       ide_setup_pci_device(dev, d);
+init_single:
+       return ide_setup_pci_device(dev, d);
 }
 
+static ide_pci_device_t hpt366_chipsets[] __devinitdata = {
+       {       /* 0 */
+               .name           = "HPT366",
+               .init_setup     = init_setup_hpt366,
+               .init_chipset   = init_chipset_hpt366,
+               .init_iops      = init_iops_hpt366,
+               .init_hwif      = init_hwif_hpt366,
+               .init_dma       = init_dma_hpt366,
+               .channels       = 2,
+               .autodma        = AUTODMA,
+               .bootable       = OFF_BOARD,
+               .extra          = 240
+       },{     /* 1 */
+               .name           = "HPT372A",
+               .init_setup     = init_setup_hpt37x,
+               .init_chipset   = init_chipset_hpt366,
+               .init_iops      = init_iops_hpt366,
+               .init_hwif      = init_hwif_hpt366,
+               .init_dma       = init_dma_hpt366,
+               .channels       = 2,
+               .autodma        = AUTODMA,
+               .bootable       = OFF_BOARD,
+       },{     /* 2 */
+               .name           = "HPT302",
+               .init_setup     = init_setup_hpt37x,
+               .init_chipset   = init_chipset_hpt366,
+               .init_iops      = init_iops_hpt366,
+               .init_hwif      = init_hwif_hpt366,
+               .init_dma       = init_dma_hpt366,
+               .channels       = 2,
+               .autodma        = AUTODMA,
+               .bootable       = OFF_BOARD,
+       },{     /* 3 */
+               .name           = "HPT371",
+               .init_setup     = init_setup_hpt371,
+               .init_chipset   = init_chipset_hpt366,
+               .init_iops      = init_iops_hpt366,
+               .init_hwif      = init_hwif_hpt366,
+               .init_dma       = init_dma_hpt366,
+               .channels       = 2,
+               .autodma        = AUTODMA,
+               .enablebits     = {{0x50,0x04,0x04}, {0x54,0x04,0x04}},
+               .bootable       = OFF_BOARD,
+       },{     /* 4 */
+               .name           = "HPT374",
+               .init_setup     = init_setup_hpt374,
+               .init_chipset   = init_chipset_hpt366,
+               .init_iops      = init_iops_hpt366,
+               .init_hwif      = init_hwif_hpt366,
+               .init_dma       = init_dma_hpt366,
+               .channels       = 2,    /* 4 */
+               .autodma        = AUTODMA,
+               .bootable       = OFF_BOARD,
+       },{     /* 5 */
+               .name           = "HPT372N",
+               .init_setup     = init_setup_hpt37x,
+               .init_chipset   = init_chipset_hpt366,
+               .init_iops      = init_iops_hpt366,
+               .init_hwif      = init_hwif_hpt366,
+               .init_dma       = init_dma_hpt366,
+               .channels       = 2,    /* 4 */
+               .autodma        = AUTODMA,
+               .bootable       = OFF_BOARD,
+       }
+};
 
 /**
  *     hpt366_init_one -       called when an HPT366 is found
@@ -1235,14 +1584,16 @@ static void __devinit init_setup_hpt366(struct pci_dev *dev, ide_pci_device_t *d
  *
  *     Called when the PCI registration layer (or the IDE initialization)
  *     finds a device matching our IDE device tables.
+ *
+ *     NOTE: since we'll have to modify some fields of the ide_pci_device_t
+ *     structure depending on the chip's revision, we'd better pass a local
+ *     copy down the call chain...
  */
 static int __devinit hpt366_init_one(struct pci_dev *dev, const struct pci_device_id *id)
 {
-       ide_pci_device_t *d = &hpt366_chipsets[id->driver_data];
+       ide_pci_device_t d = hpt366_chipsets[id->driver_data];
 
-       d->init_setup(dev, d);
-       return 0;
+       return d.init_setup(dev, &d);
 }
 
 static struct pci_device_id hpt366_pci_tbl[] = {
@@ -1251,17 +1602,18 @@ static struct pci_device_id hpt366_pci_tbl[] = {
        { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT302, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2},
        { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3},
        { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT374, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
+       { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT372N, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5},
        { 0, },
 };
 MODULE_DEVICE_TABLE(pci, hpt366_pci_tbl);
 
 static struct pci_driver driver = {
-       .name           = "HPT366 IDE",
+       .name           = "HPT366_IDE",
        .id_table       = hpt366_pci_tbl,
        .probe          = hpt366_init_one,
 };
 
-static int hpt366_ide_init(void)
+static int __init hpt366_ide_init(void)
 {
        return ide_pci_register_driver(&driver);
 }