Commit f03a6bbb authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] PATCH: Fix HPT366 crash and support HPT372N

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: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent ed158dc1
/*
* 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
......@@ -39,6 +40,13 @@
* 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,11 +899,52 @@ 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) {
/*
* 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
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
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))
......@@ -824,10 +954,9 @@ static int __devinit init_hpt37x(struct pci_dev *dev)
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;
} 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))
......@@ -838,11 +967,9 @@ static int __devinit init_hpt37x(struct pci_dev *dev)
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);
}
else if (hpt_minimum_revision(dev,5))
pci_set_drvdata(dev, (void *) sixty_six_base_hpt372);
......@@ -852,6 +979,7 @@ static int __devinit init_hpt37x(struct pci_dev *dev)
pci_set_drvdata(dev, (void *) sixty_six_base_hpt370);
printk("HPT37X: using 66MHz PCI clock\n");
}
}
/*
* only try the pll if we don't have a table for the clock
......@@ -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,6 +1133,18 @@ 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;
......@@ -1007,6 +1152,9 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
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
* address lines to access an external eeprom. To read valid
......@@ -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);
......
......@@ -1184,6 +1184,7 @@
#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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment