]> git.hungrycats.org Git - linux/commitdiff
[PATCH] PATCH: Fix HPT366 crash and support HPT372N
authorAlan Cox <alan@redhat.com>
Sat, 31 Jul 2004 11:34:32 +0000 (04:34 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Sat, 31 Jul 2004 11:34:32 +0000 (04:34 -0700)
On a board containing the HPT372N IDE controller the 2.6.x series kernels will
misbehave. If the HPT372N is set up with the newer PCI identifier it is
ignored. If it is set up with the HPT372 identifier then the kernel crashes
on boot.

This patch is a forward port of my 2.4 driver fixes that have been in 2.4
for a year but somehow escaped 2.6. Ronny Buchmann caught a couple
of merge details I missed and those are fixed in this diff too.

As well as adding 372N support this also fixes the unknown revision case
to avoid crashes should any future 37x variants with weird class_rev's appear

Signed-off-by: Alan Cox <alan@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
drivers/ide/pci/hpt366.c
include/linux/pci_ids.h

index 6c25d34b89390bd4b648cc18bd9f6115a3840a1a..5f5bc3b6e8bc5907495531dddb0599f75e3c60d4 100644 (file)
@@ -1,8 +1,9 @@
 /*
- * 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
  *
  * Thanks to HighPoint Technologies for their assistance, and hardware.
  * Special Thanks to Jon Burchmore in SanDiego for the deep pockets, his
  * 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>
+ *
  */
 
 
@@ -168,6 +176,9 @@ static u32 hpt_revision (struct pci_dev *dev)
        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:
@@ -217,6 +228,11 @@ static u8 hpt3xx_ratemask (ide_drive_t *drive)
        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;
@@ -672,6 +688,69 @@ static int hpt374_ide_dma_end (ide_drive_t *drive)
        return __ide_dma_end(drive);
 }
 
+/**
+ *     hpt372n_set_clock       -       perform clock switching dance
+ *     @drive: Drive to switch
+ *     @mode: Switching mode (0x21 for write, 0x23 otherwise)
+ *
+ *     Switch the DPLL clock on the HPT372N devices. This is a
+ *     right mess.
+ */
+static void hpt372n_set_clock(ide_drive_t *drive, int mode)
+{
+       ide_hwif_t *hwif        = HWIF(drive);
+       
+       /* FIXME: should we check for DMA active and BUG() */
+       /* Tristate the bus */
+       outb(0x80, hwif->dma_base+0x73);
+       outb(0x80, hwif->dma_base+0x77);
+       
+       /* Switch clock and reset channels */
+       outb(mode, hwif->dma_base+0x7B);
+       outb(0xC0, hwif->dma_base+0x79);
+       
+       /* Reset state machines */
+       outb(0x37, hwif->dma_base+0x70);
+       outb(0x37, hwif->dma_base+0x74);
+       
+       /* Complete reset */
+       outb(0x00, hwif->dma_base+0x79);
+       
+       /* Reconnect channels to bus */
+       outb(0x00, hwif->dma_base+0x73);
+       outb(0x00, hwif->dma_base+0x77);
+}
+
+/**
+ *     hpt372n_rw_disk         -       wrapper for I/O
+ *     @drive: drive for command
+ *     @rq: block request structure
+ *     @block: block number
+ *
+ *     This is called when a disk I/O is issued to the 372N instead
+ *     of the default functionality. We need it because of the clock
+ *     switching
+ *
+ */
+static ide_startstop_t hpt372n_rw_disk(ide_drive_t *drive, struct request *rq, sector_t block)
+{
+       int wantclock;
+       
+       if(rq_data_dir(rq) == READ)
+               wantclock = 0x21;
+       else
+               wantclock = 0x23;
+               
+       if(HWIF(drive)->config_data != wantclock)
+       {
+               hpt372n_set_clock(drive, wantclock);
+               HWIF(drive)->config_data = wantclock;
+       }
+       return __ide_do_rw_disk(drive, rq, block);
+}
+
 /*
  * 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
@@ -793,13 +872,23 @@ static int __devinit init_hpt37x(struct pci_dev *dev)
        u16 freq;
        u32 pll;
        u8 reg5bh;
-
-#if 1
        u8 reg5ah = 0;
+       unsigned long dmabase = pci_resource_start(dev, 4);
+       u8 did, rid;    
+       int is_372n = 0;
+       
        pci_read_config_byte(dev, 0x5a, &reg5ah);
        /* interrupt force enable */
        pci_write_config_byte(dev, 0x5a, (reg5ah & ~0x10));
-#endif
+
+       if(dmabase)
+       {
+               did = inb(dmabase + 0x22);
+               rid = inb(dmabase + 0x28);
+       
+               if((did == 4 && rid == 6) || (did == 5 && rid > 1))
+                       is_372n = 1;
+       }
 
        /*
         * default to pci clock. make sure MA15/16 are set to output
@@ -810,47 +899,86 @@ static int __devinit init_hpt37x(struct pci_dev *dev)
        /*
         * set up the PLL. we need to adjust it so that it's stable. 
         * freq = Tpll * 192 / Tpci
+        *
+        * Todo. For non x86 should probably check the dword is
+        * set to 0xABCDExxx indicating the BIOS saved f_CNT
         */
        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);
+       
+       /*
+        * The 372N uses different PCI clock information and has
+        * some other complications
+        *      On PCI33 timing we must clock switch
+        *      On PCI66 timing we must NOT use the PCI clock
+        *
+        * Currently we always set up the PLL for the 372N
+        */
+        
+       pci_set_drvdata(dev, NULL);
+       
+       if(is_372n)
+       {
+               printk(KERN_INFO "hpt: HPT372N detected, using 372N timing.\n");
+               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 *) 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);
+                       pll = F_LOW_PCI_66;
+                       
+               printk(KERN_INFO "FREQ: %d PLL: %d\n", freq, pll);
+                       
+               /* We always use the pll not the PCI clock on 372N */
+       }
+       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
-                       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);
+                       pll = F_LOW_PCI_66;
+       
+               if (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 (pll == F_LOW_PCI_40) {
+                       /* Unsupported */
+               } else if (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 {
+                       if (hpt_minimum_revision(dev,8))
+                       {
+                               printk(KERN_ERR "HPT37x: 66MHz timings are not supported.\n");
+                       }
+                       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);
+                       else
+                               pci_set_drvdata(dev, (void *) sixty_six_base_hpt370);
+                       printk("HPT37X: using 66MHz PCI clock\n");
                }
-               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);
-               else
-                       pci_set_drvdata(dev, (void *) sixty_six_base_hpt370);
-               printk("HPT37X: using 66MHz PCI clock\n");
        }
        
        /*
@@ -863,6 +991,11 @@ static int __devinit init_hpt37x(struct pci_dev *dev)
        if (pci_get_drvdata(dev)) 
                goto init_hpt37X_done;
        
+       if (hpt_minimum_revision(dev,8))
+       {
+               printk(KERN_ERR "HPT374: Only 33MHz PCI timings are supported.\n");
+               return -EOPNOTSUPP;
+       }
        /*
         * adjust PLL based upon PCI clock, enable it, and wait for
         * stabilization.
@@ -1000,12 +1133,27 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
 {
        struct pci_dev *dev             = hwif->pci_dev;
        u8 ata66 = 0, regmask           = (hwif->channel) ? 0x01 : 0x02;
-
+       u8 did, rid;
+       unsigned long dmabase           = hwif->dma_base;
+       int is_372n = 0;
+       
+       if(dmabase)
+       {
+               did = inb(dmabase + 0x22);
+               rid = inb(dmabase + 0x28);
+       
+               if((did == 4 && rid == 6) || (did == 5 && rid > 1))
+                       is_372n = 1;
+       }
+               
        hwif->tuneproc                  = &hpt3xx_tune_drive;
        hwif->speedproc                 = &hpt3xx_tune_chipset;
        hwif->quirkproc                 = &hpt3xx_quirkproc;
        hwif->intrproc                  = &hpt3xx_intrproc;
        hwif->maskproc                  = &hpt3xx_maskproc;
+       
+       if(is_372n)
+               hwif->rw_disk = &hpt372n_rw_disk;
 
        /*
         * The HPT37x uses the CBLID pins as outputs for MA15/MA16
@@ -1179,7 +1327,8 @@ static void __devinit init_setup_hpt366(struct pci_dev *dev, ide_pci_device_t *d
        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;
@@ -1187,9 +1336,14 @@ static void __devinit init_setup_hpt366(struct pci_dev *dev, ide_pci_device_t *d
        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);
@@ -1243,6 +1397,7 @@ 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);
index 237a53e2224f3745ee518b6701e4cef429665487..50a747035c0515406d3e20ae04414633eec97fc4 100644 (file)
 #define PCI_DEVICE_ID_TTI_HPT302       0x0006
 #define PCI_DEVICE_ID_TTI_HPT371       0x0007
 #define PCI_DEVICE_ID_TTI_HPT374       0x0008
+#define PCI_DEVICE_ID_TTI_HPT372N      0x0009  // apparently a 372N variant?
 
 #define PCI_VENDOR_ID_VIA              0x1106
 #define PCI_DEVICE_ID_VIA_8763_0       0x0198