patch-2.3.23 linux/drivers/block/pdc202xx.c
Next file: linux/drivers/block/pdc4030.c
Previous file: linux/drivers/block/paride/pcd.c
Back to the patch index
Back to the overall index
- Lines: 493
- Date:
Mon Oct 18 11:14:22 1999
- Orig file:
v2.3.22/linux/drivers/block/pdc202xx.c
- Orig date:
Mon Oct 11 15:38:14 1999
diff -u --recursive --new-file v2.3.22/linux/drivers/block/pdc202xx.c linux/drivers/block/pdc202xx.c
@@ -1,8 +1,8 @@
/*
- * linux/drivers/block/pdc202xx.c Version 0.26 May 12, 1999
+ * linux/drivers/block/pdc202xx.c Version 0.27 Sept. 3, 1999
*
- * Copyright (C) 1998-99 Andre Hedrick
- * (hedrick@astro.dyer.vanderbilt.edu)
+ * Copyright (C) 1998-99 Andre Hedrick (andre@suse.com)
+ * May be copied or modified under the terms of the GNU General Public License
*
* Promise Ultra33 cards with BIOS v1.20 through 1.28 will need this
* compiled into the kernel if you have more than one card installed.
@@ -72,6 +72,12 @@
* = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
*/
+/*
+ * Portions Copyright (C) 1999 Promise Technology, Inc.
+ * Author: Frank Tiernan (frankt@promise.com)
+ * Released under terms of General Public License
+ */
+
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
@@ -88,6 +94,8 @@
#include <asm/io.h>
#include <asm/irq.h>
+#include "ide_modes.h"
+
#define PDC202XX_DEBUG_DRIVE_INFO 0
#define PDC202XX_DECODE_REGISTER_INFO 0
@@ -208,27 +216,78 @@
struct hd_driveid *id = drive->id;
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = hwif->pci_dev;
+ unsigned long high_16 = dev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK;
int err;
unsigned int drive_conf;
byte drive_pci;
- byte test1, test2, speed;
- byte AP, BP, CP, DP, EP;
+ byte test1, test2, speed = -1;
+ byte AP, BP, CP, DP, TB, TC;
+ unsigned short EP;
+ byte CLKSPD = IN_BYTE(high_16 + 0x11);
int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
byte udma_66 = ((id->word93 & 0x2000) && (hwif->udma_four)) ? 1 : 0;
- byte udma_33 = ultra ? (inb((dev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK) + 0x001f) & 1) : 0;
+ byte udma_33 = ultra ? (inb(high_16 + 0x001f) & 1) : 0;
- pci_read_config_byte(dev, 0x50, &EP);
+ /*
+ * Set the control register to use the 66Mhz system
+ * clock for UDMA 3/4 mode operation. If one drive on
+ * a channel is U66 capable but the other isn't we
+ * fall back to U33 mode. The BIOS INT 13 hooks turn
+ * the clock on then off for each read/write issued. I don't
+ * do that here because it would require modifying the
+ * kernel, seperating the fop routines from the kernel or
+ * somehow hooking the fops calls. It may also be possible to
+ * leave the 66Mhz clock on and readjust the timing
+ * parameters.
+ */
+
+ byte mask = hwif->channel ? 0x08 : 0x02;
+ unsigned short c_mask = hwif->channel ? (1<<11) : (1<<10);
+ byte ultra_66 = ((id->dma_ultra & 0x0010) || (id->dma_ultra & 0x0008)) ? 1 : 0;
+
+ pci_read_config_word(dev, 0x50, &EP);
+
+ if ((ultra_66) && (EP & c_mask)) {
+#ifdef DEBUG
+ printk("ULTRA66: %s channel of Ultra 66 requires an 80-pin cable for Ultra66 operation.\n", hwif->channel ? "Secondary", "Primary");
+ printk(" Switching to Ultra33 mode.\n");
+#endif /* DEBUG */
+ /* Primary : zero out second bit */
+ /* Secondary : zero out fourth bit */
+ OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11));
+ } else {
+ if (ultra_66) {
+ /*
+ * check to make sure drive on same channel
+ * is u66 capable
+ */
+ if (hwif->drives[!(drive_number%2)].id) {
+ if ((hwif->drives[!(drive_number%2)].id->dma_ultra & 0x0010) ||
+ (hwif->drives[!(drive_number%2)].id->dma_ultra & 0x0008)) {
+ OUT_BYTE(CLKSPD | mask, (high_16 + 0x11));
+ } else {
+ OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11));
+ }
+ } else { /* udma4 drive by itself */
+ OUT_BYTE(CLKSPD | mask, (high_16 + 0x11));
+ }
+ }
+ }
switch(drive_number) {
case 0: drive_pci = 0x60;
pci_read_config_dword(dev, drive_pci, &drive_conf);
+ if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+ goto chipset_is_set;
pci_read_config_byte(dev, (drive_pci), &test1);
if (!(test1 & SYNC_ERRDY_EN))
pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN);
break;
case 1: drive_pci = 0x64;
pci_read_config_dword(dev, drive_pci, &drive_conf);
+ if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+ goto chipset_is_set;
pci_read_config_byte(dev, 0x60, &test1);
pci_read_config_byte(dev, (drive_pci), &test2);
if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN))
@@ -236,12 +295,16 @@
break;
case 2: drive_pci = 0x68;
pci_read_config_dword(dev, drive_pci, &drive_conf);
+ if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+ goto chipset_is_set;
pci_read_config_byte(dev, (drive_pci), &test1);
if (!(test1 & SYNC_ERRDY_EN))
pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN);
break;
case 3: drive_pci = 0x6c;
pci_read_config_dword(dev, drive_pci, &drive_conf);
+ if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+ goto chipset_is_set;
pci_read_config_byte(dev, 0x68, &test1);
pci_read_config_byte(dev, (drive_pci), &test2);
if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN))
@@ -251,6 +314,8 @@
return ide_dma_off;
}
+chipset_is_set:
+
if (drive->media != ide_disk)
return ide_dma_off_quietly;
@@ -285,127 +350,46 @@
pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
if ((id->dma_ultra & 0x0010) && (udma_66) && (udma_33)) {
- if (!((id->dma_ultra >> 8) & 16)) {
- drive->id->dma_ultra &= ~0xFF00;
- drive->id->dma_ultra |= 0x1010;
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_1word &= ~0x0F00;
- }
/* speed 8 == UDMA mode 4 == speed 6 plus cable */
- pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x20);
- pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x01);
- speed = XFER_UDMA_4;
+ speed = XFER_UDMA_4; TB = 0x20; TC = 0x01;
} else if ((id->dma_ultra & 0x0008) && (udma_66) && (udma_33)) {
- if (!((id->dma_ultra >> 8) & 8)) {
- drive->id->dma_ultra &= ~0xFF00;
- drive->id->dma_ultra |= 0x0808;
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_1word &= ~0x0F00;
- }
/* speed 7 == UDMA mode 3 == speed 5 plus cable */
- pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x40);
- pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x02);
- speed = XFER_UDMA_3;
+ speed = XFER_UDMA_3; TB = 0x40; TC = 0x02;
} else if ((id->dma_ultra & 0x0004) && (udma_33)) {
- if (!((id->dma_ultra >> 8) & 4)) {
- drive->id->dma_ultra &= ~0xFF00;
- drive->id->dma_ultra |= 0x0404;
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_1word &= ~0x0F00;
- }
/* speed 6 == UDMA mode 2 */
- pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x20);
- pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x01);
- speed = XFER_UDMA_2;
+ speed = XFER_UDMA_2; TB = 0x20; TC = 0x01;
} else if ((id->dma_ultra & 0x0002) && (udma_33)) {
- if (!((id->dma_ultra >> 8) & 2)) {
- drive->id->dma_ultra &= ~0xFF00;
- drive->id->dma_ultra |= 0x0202;
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_1word &= ~0x0F00;
- }
/* speed 5 == UDMA mode 1 */
- pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x40);
- pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x02);
- speed = XFER_UDMA_1;
+ speed = XFER_UDMA_1; TB = 0x40; TC = 0x02;
} else if ((id->dma_ultra & 0x0001) && (udma_33)) {
- if (!((id->dma_ultra >> 8) & 1)) {
- drive->id->dma_ultra &= ~0xFF00;
- drive->id->dma_ultra |= 0x0101;
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_1word &= ~0x0F00;
- }
/* speed 4 == UDMA mode 0 */
- pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60);
- pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x03);
- speed = XFER_UDMA_0;
+ speed = XFER_UDMA_0; TB = 0x60; TC = 0x03;
} else if (id->dma_mword & 0x0004) {
- if (!((id->dma_mword >> 8) & 4)) {
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_mword |= 0x0404;
- drive->id->dma_1word &= ~0x0F00;
- }
/* speed 4 == DMA mode 2 multi-word */
- pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60);
- pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x03);
- speed = XFER_MW_DMA_2;
+ speed = XFER_MW_DMA_2; TB = 0x60; TC = 0x03;
} else if (id->dma_mword & 0x0002) {
- if (!((id->dma_mword >> 8) & 2)) {
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_mword |= 0x0202;
- drive->id->dma_1word &= ~0x0F00;
- }
/* speed 3 == DMA mode 1 multi-word */
- pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60);
- pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x04);
- speed = XFER_MW_DMA_1;
+ speed = XFER_MW_DMA_1; TB = 0x60; TC = 0x04;
} else if (id->dma_mword & 0x0001) {
- if (!((id->dma_mword >> 8) & 1)) {
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_mword |= 0x0101;
- drive->id->dma_1word &= ~0x0F00;
- }
/* speed 2 == DMA mode 0 multi-word */
- pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60);
- pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x05);
- speed = XFER_MW_DMA_0;
+ speed = XFER_MW_DMA_0; TB = 0x60; TC = 0x05;
} else if (id->dma_1word & 0x0004) {
- if (!((id->dma_1word >> 8) & 4)) {
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_1word &= ~0x0F00;
- drive->id->dma_1word |= 0x0404;
- }
/* speed 2 == DMA mode 2 single-word */
- pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60);
- pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x05);
- speed = XFER_SW_DMA_2;
+ speed = XFER_SW_DMA_2; TB = 0x60; TC = 0x05;
} else if (id->dma_1word & 0x0002) {
- if (!((id->dma_1word >> 8) & 2)) {
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_1word &= ~0x0F00;
- drive->id->dma_1word |= 0x0202;
- }
/* speed 1 == DMA mode 1 single-word */
- pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x80);
- pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x06);
- speed = XFER_SW_DMA_1;
+ speed = XFER_SW_DMA_1; TB = 0x80; TC = 0x06;
} else if (id->dma_1word & 0x0001) {
- if (!((id->dma_1word >> 8) & 1)) {
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_1word &= ~0x0F00;
- drive->id->dma_1word |= 0x0101;
- }
/* speed 0 == DMA mode 0 single-word */
- pci_write_config_byte(dev, (drive_pci)|0x01, BP|0xC0);
- pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x0B);
- speed = XFER_SW_DMA_0;
+ speed = XFER_SW_DMA_0; TB = 0xC0; TC = 0x0B;
} else {
/* restore original pci-config space */
pci_write_config_dword(dev, drive_pci, drive_conf);
return ide_dma_off_quietly;
}
- err = ide_config_drive_speed(drive, speed);
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB);
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP|TC);
#if PDC202XX_DECODE_REGISTER_INFO
pci_read_config_byte(dev, (drive_pci), &AP);
@@ -418,6 +402,8 @@
decode_registers(REG_D, DP);
#endif /* PDC202XX_DECODE_REGISTER_INFO */
+ err = ide_config_drive_speed(drive, speed);
+
#if PDC202XX_DEBUG_DRIVE_INFO
printk("%s: %s drive%d 0x%08x ",
drive->name, ide_xfer_verbose(speed),
@@ -441,6 +427,83 @@
* 11, 5, 4, 3, 2, 1, 0
*/
+static int config_chipset_for_pio (ide_drive_t *drive, byte pio)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ byte drive_pci, speed;
+ byte AP, BP, TA, TB;
+
+ int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ int err;
+
+ switch (drive_number) {
+ case 0: drive_pci = 0x60; break;
+ case 1: drive_pci = 0x64; break;
+ case 2: drive_pci = 0x68; break;
+ case 3: drive_pci = 0x6c; break;
+ default: return 1;
+ }
+
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+
+
+ if ((AP & 0x0F) || (BP & 0x07)) {
+ /* clear PIO modes of lower 8421 bits of A Register */
+ pci_write_config_byte(dev, (drive_pci), AP & ~0x0F);
+ pci_read_config_byte(dev, (drive_pci), &AP);
+
+ /* clear PIO modes of lower 421 bits of B Register */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0x07);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+ }
+
+ pio = (pio == 5) ? 4 : pio;
+ switch (ide_get_best_pio_mode(drive, 255, pio, NULL)) {
+ case 4: speed = XFER_PIO_4; TA=0x01; TB=0x04; break;
+ case 3: speed = XFER_PIO_3; TA=0x02; TB=0x06; break;
+ case 2: speed = XFER_PIO_2; TA=0x03; TB=0x08; break;
+ case 1: speed = XFER_PIO_1; TA=0x05; TB=0x0C; break;
+ case 0:
+ default: speed = XFER_PIO_0; TA=0x09; TB=0x13; break;
+ }
+ pci_write_config_byte(dev, (drive_pci), AP|TA);
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB);
+
+#if PDC202XX_DECODE_REGISTER_INFO
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+ pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+ pci_read_config_byte(dev, (drive_pci)|0x03, &DP);
+
+ decode_registers(REG_A, AP);
+ decode_registers(REG_B, BP);
+ decode_registers(REG_C, CP);
+ decode_registers(REG_D, DP);
+#endif /* PDC202XX_DECODE_REGISTER_INFO */
+
+ err = ide_config_drive_speed(drive, speed);
+
+#if PDC202XX_DEBUG_DRIVE_INFO
+ printk("%s: %s drive%d 0x%08x ",
+ drive->name, ide_xfer_verbose(speed),
+ drive_number, drive_conf);
+ pci_read_config_dword(dev, drive_pci, &drive_conf);
+ printk("0x%08x\n", drive_conf);
+#endif /* PDC202XX_DEBUG_DRIVE_INFO */
+
+ return err;
+}
+
+static void pdc202xx_tune_drive (ide_drive_t *drive, byte pio)
+{
+ (void) config_chipset_for_pio(drive, pio);
+}
+
static int config_drive_xfer_rate (ide_drive_t *drive)
{
struct hd_driveid *id = drive->id;
@@ -450,9 +513,10 @@
if (id && (id->capability & 1) && hwif->autodma) {
/* Consult the list of known "bad" drives */
if (ide_dmaproc(ide_dma_bad_drive, drive)) {
- return HWIF(drive)->dmaproc(ide_dma_off, drive);
+ dma_func = ide_dma_off;
+ goto fast_ata_pio;
}
-
+ dma_func = ide_dma_off_quietly;
if (id->field_valid & 4) {
if (id->dma_ultra & 0x001F) {
/* Force if Capable UltraDMA */
@@ -463,17 +527,31 @@
}
} else if (id->field_valid & 2) {
try_dma_modes:
- if ((id->dma_mword & 0x0004) ||
- (id->dma_1word & 0x0004)) {
+ if ((id->dma_mword & 0x0007) ||
+ (id->dma_1word & 0x0007)) {
/* Force if Capable regular DMA modes */
dma_func = config_chipset_for_dma(drive, 0);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ }
+ } else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+ if (id->eide_dma_time > 150) {
+ goto no_dma_set;
}
- } else if ((ide_dmaproc(ide_dma_good_drive, drive)) &&
- (id->eide_dma_time > 150)) {
/* Consult the list of known "good" drives */
dma_func = config_chipset_for_dma(drive, 0);
- }
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ } else {
+ goto fast_ata_pio;
+ }
+ } else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+ dma_func = ide_dma_off_quietly;
+no_dma_set:
+ (void) config_chipset_for_pio(drive, 5);
}
+
return HWIF(drive)->dmaproc(dma_func, drive);
}
@@ -498,6 +576,26 @@
byte primary_mode = inb(high_16 + 0x001a);
byte secondary_mode = inb(high_16 + 0x001b);
+ if (dev->device == PCI_DEVICE_ID_PROMISE_20262) {
+ int i = 0;
+ /*
+ * software reset - this is required because the bios
+ * will set UDMA timing on if the hdd supports it. The
+ * user may want to turn udma off. A bug in the pdc20262
+ * is that it cannot handle a downgrade in timing from UDMA
+ * to DMA. Disk accesses after issuing a set feature command
+ * will result in errors. A software reset leaves the timing
+ * registers intact, but resets the drives.
+ */
+
+ OUT_BYTE(udma_speed_flag | 0x10, high_16 + 0x001f);
+ ide_delay_50ms();
+ ide_delay_50ms();
+ OUT_BYTE(udma_speed_flag & ~0x10, high_16 + 0x001f);
+ for (i=0; i<40; i++)
+ ide_delay_50ms();
+ }
+
if (dev->resource[PCI_ROM_RESOURCE].start) {
pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start);
@@ -547,24 +645,23 @@
return dev->irq;
}
+unsigned int __init ata66_pdc202xx (ide_hwif_t *hwif)
+{
+ unsigned short mask = (hwif->channel) ? (1<<11) : (1<<10);
+ unsigned short CIS;
+
+ pci_read_config_word(hwif->pci_dev, 0x50, &CIS);
+ return ((CIS & mask) ? 0 : 1);
+}
+
void __init ide_init_pdc202xx (ide_hwif_t *hwif)
{
+ hwif->tuneproc = &pdc202xx_tune_drive;
+
if (hwif->dma_base) {
hwif->dmaproc = &pdc202xx_dmaproc;
-
- switch(hwif->pci_dev->device) {
- case PCI_DEVICE_ID_PROMISE_20262:
-#if 0
- {
- unsigned long high_16 = hwif->pci_dev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK;
- hwif->udma_four = 1;
- }
-#endif
- break;
- case PCI_DEVICE_ID_PROMISE_20246:
- default:
- hwif->udma_four = 0;
- break;
- }
+ } else {
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
}
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)