VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / drivers / ide / pci / hpt366.c
index 77bc208..5f5bc3b 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;
@@ -292,7 +308,7 @@ static unsigned int pci_bus_clock_list (u8 speed, struct chipset_bus_clock_list_
        return chipset_table->chipset_settings;
 }
 
-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;
        u8 speed                = hpt3xx_ratefilter(drive, xferspeed);
@@ -329,14 +345,11 @@ 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;
        u8 speed        = hpt3xx_ratefilter(drive, xferspeed);
@@ -378,9 +391,11 @@ static void hpt370_tune_chipset (ide_drive_t *drive, u8 xferspeed)
        }
 
        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;
        u8 speed        = hpt3xx_ratefilter(drive, xferspeed);
@@ -406,11 +421,8 @@ static void hpt372_tune_chipset (ide_drive_t *drive, u8 xferspeed)
        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)
@@ -418,7 +430,7 @@ 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);
+               return hpt372_tune_chipset(drive, speed); /* not a typo */
 #if 0
        else if (hpt_minimum_revision(dev, 7))
                hpt371_tune_chipset(drive, speed);
@@ -426,15 +438,11 @@ static int hpt3xx_tune_chipset (ide_drive_t *drive, u8 speed)
                hpt302_tune_chipset(drive, speed);
 #endif
        else if (hpt_minimum_revision(dev, 5))
-               hpt372_tune_chipset(drive, speed);
+               return 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));
+               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)
@@ -680,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
@@ -801,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
@@ -818,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");
        }
        
        /*
@@ -871,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.
@@ -1008,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
@@ -1187,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;
@@ -1195,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);
@@ -1251,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);