patch-2.3.40 linux/drivers/block/cmd64x.c
Next file: linux/drivers/block/cpqarray.c
Previous file: linux/drivers/block/cmd646.c
Back to the patch index
Back to the overall index
- Lines: 341
- Date:
Fri Jan 14 00:50:53 2000
- Orig file:
v2.3.39/linux/drivers/block/cmd64x.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.39/linux/drivers/block/cmd64x.c linux/drivers/block/cmd64x.c
@@ -0,0 +1,340 @@
+/* $Id: cmd64x.c,v 1.20 1999/12/30 03:48:37
+ *
+ * cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines.
+ * Note, this driver is not used at all on other systems because
+ * there the "BIOS" has done all of the following already.
+ * Due to massive hardware bugs, UltraDMA is only supported
+ * on the 646U2 and not on the 646U.
+ *
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com)
+ * Copyright (C) 1999 Andre Hedrick (andre@suse.com)
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#define CMD_DEBUG 0
+
+#if CMD_DEBUG
+#define cmdprintk(x...) printk(##x)
+#else
+#define cmdprintk(x...)
+#endif
+
+static int config_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ultra_66)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned long dma_base = hwif->dma_base;
+
+ byte unit = (drive->select.b.unit & 0x01);
+ byte speed = 0x00;
+ byte udma_timing_bits = 0x00;
+ byte udma_33 = ((rev >= 0x05) || (ultra_66)) ? 1 : 0;
+ byte udma_66 = ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
+ /* int drive_number = ((hwif->channel ? 2 : 0) + unit); */
+ int rval;
+
+ switch(dev->device) {
+ case PCI_DEVICE_ID_CMD_643:
+ case PCI_DEVICE_ID_CMD_646:
+ case PCI_DEVICE_ID_CMD_648:
+ default:
+ break;
+ }
+
+ if (drive->media != ide_disk) {
+ cmdprintk("CMD64X: drive->media != ide_disk at double check, inital check failed!!\n");
+ return ((int) ide_dma_off);
+ }
+
+ /* UltraDMA only supported on PCI646U and PCI646U2,
+ * which correspond to revisions 0x03, 0x05 and 0x07 respectively.
+ * Actually, although the CMD tech support people won't
+ * tell me the details, the 0x03 revision cannot support
+ * UDMA correctly without hardware modifications, and even
+ * then it only works with Quantum disks due to some
+ * hold time assumptions in the 646U part which are fixed
+ * in the 646U2.
+ * So we only do UltraDMA on revision 0x05 and 0x07 chipsets.
+ */
+
+ if ((id->dma_ultra & 0x0010) && (udma_66) && (udma_33)) {
+ speed = XFER_UDMA_4;
+ udma_timing_bits = 0x10; /* 2 clock */
+ } else if ((id->dma_ultra & 0x0008) && (udma_66) && (udma_33)) {
+ speed = XFER_UDMA_3;
+ udma_timing_bits = 0x20; /* 3 clock */
+ } else if ((id->dma_ultra & 0x0004) && (udma_33)) {
+ speed = XFER_UDMA_2;
+ udma_timing_bits = 0x10; /* 2 clock */
+ } else if ((id->dma_ultra & 0x0002) && (udma_33)) {
+ speed = XFER_UDMA_1;
+ udma_timing_bits = 0x20; /* 3 clock */
+ } else if ((id->dma_ultra & 0x0001) && (udma_33)) {
+ speed = XFER_UDMA_0;
+ udma_timing_bits = 0x30; /* 4 clock */
+ } else if (id->dma_mword & 0x0004) {
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_mword & 0x0001) {
+ speed = XFER_MW_DMA_0;
+ } else if (id->dma_1word & 0x0004) {
+ speed = XFER_SW_DMA_2;
+ } else if (id->dma_1word & 0x0002) {
+ speed = XFER_SW_DMA_1;
+ } else if (id->dma_1word & 0x0001) {
+ speed = XFER_SW_DMA_0;
+ } else {
+ return ((int) ide_dma_off_quietly);
+ }
+
+ (void) ide_config_drive_speed(drive, speed);
+ outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+
+ if (speed >= XFER_UDMA_0) {
+ byte udma_ctrl = inb(dma_base + 3);
+ /* Put this channel into UDMA mode. */
+ udma_ctrl |= (1 << unit);
+ udma_ctrl &= ~(0x04 << unit);
+ if (udma_66)
+ udma_ctrl |= (0x04 << unit);
+ udma_ctrl &= ~(0x30 << (unit * 2));
+ udma_ctrl |= (udma_timing_bits << (unit * 2));
+ outb(udma_ctrl, dma_base+3);
+ }
+
+ rval = (int)( ((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+ ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+
+ return rval;
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive, unsigned int rev)
+{
+ /* FIXME!! figure out some PIOing junk.... */
+}
+
+static int cmd64x_config_drive_for_dma (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned int class_rev = 0;
+ byte can_ultra_33 = 0;
+ byte can_ultra_66 = 0;
+ ide_dma_action_t dma_func = ide_dma_on;
+
+ pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
+ class_rev &= 0xff;
+
+ switch(dev->device) {
+ case PCI_DEVICE_ID_CMD_643:
+ can_ultra_33 = 1;
+ can_ultra_66 = 0;
+ break;
+ case PCI_DEVICE_ID_CMD_646:
+ can_ultra_33 = (class_rev >= 0x05) ? 1 : 0;
+ can_ultra_66 = 0;
+ break;
+ case PCI_DEVICE_ID_CMD_648:
+ can_ultra_33 = 1;
+ can_ultra_66 = 1;
+ break;
+ default:
+ return hwif->dmaproc(ide_dma_off, drive);
+ }
+
+ if ((id != NULL) && ((id->capability & 1) != 0) &&
+ hwif->autodma && (drive->media == ide_disk)) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ dma_func = ide_dma_off;
+ goto fast_ata_pio;
+ }
+ dma_func = ide_dma_off_quietly;
+ if ((id->field_valid & 4) && (can_ultra_33)) {
+ if (id->dma_ultra & 0x001F) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive, class_rev, can_ultra_66);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if ((id->dma_mword & 0x0007) ||
+ (id->dma_1word & 0x0007)) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive, class_rev, 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;
+ }
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive, class_rev, 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:
+ config_chipset_for_pio(drive, class_rev);
+ }
+ return hwif->dmaproc(dma_func, drive);
+}
+
+static int cmd64x_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ return cmd64x_config_drive_for_dma(drive);
+ default:
+ break;
+ }
+ /* Other cases are done by generic IDE-DMA code. */
+ return ide_dmaproc(func, drive);
+}
+
+/*
+ * ASUS P55T2P4D with CMD646 chipset revision 0x01 requires the old
+ * event order for DMA transfers.
+ */
+static int cmd646_1_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned long dma_base = hwif->dma_base;
+ byte dma_stat;
+
+ if (func == ide_dma_end) {
+ drive->waiting_for_dma = 0;
+ dma_stat = inb(dma_base+2); /* get DMA status */
+ outb(inb(dma_base)&~1, dma_base); /* stop DMA */
+ outb(dma_stat|6, dma_base+2); /* clear the INTR & ERROR bits */
+ return (dma_stat & 7) != 4; /* verify good DMA status */
+ }
+
+ /* Other cases are done by generic IDE-DMA code. */
+ return cmd64x_dmaproc(func, drive);
+}
+
+unsigned int __init pci_init_cmd64x (struct pci_dev *dev, const char *name)
+{
+ unsigned char mrdmode;
+ unsigned int class_rev;
+
+ pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
+ class_rev &= 0xff;
+
+ switch(dev->device) {
+ case PCI_DEVICE_ID_CMD_643:
+ break;
+ case PCI_DEVICE_ID_CMD_646:
+ printk("%s: chipset revision 0x%02X, ", name, class_rev);
+ switch(class_rev) {
+ case 0x07:
+ case 0x05:
+ printk("UltraDMA Capable");
+ break;
+ case 0x03:
+ printk("MultiWord DMA Force Limited");
+ break;
+ case 0x01:
+ default:
+ printk("MultiWord DMA Limited, IRQ workaround enabled");
+ break;
+ }
+ printk("\n");
+ break;
+ case PCI_DEVICE_ID_CMD_648:
+ break;
+ default:
+ break;
+ }
+
+ /* Set a good latency timer and cache line size value. */
+ (void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+#ifdef __sparc_v9__
+ (void) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x10);
+#endif
+
+ /* Setup interrupts. */
+ (void) pci_read_config_byte(dev, 0x71, &mrdmode);
+ mrdmode &= ~(0x30);
+ (void) pci_write_config_byte(dev, 0x71, mrdmode);
+
+ /* Use MEMORY READ LINE for reads.
+ * NOTE: Although not mentioned in the PCI0646U specs,
+ * these bits are write only and won't be read
+ * back as set or not. The PCI0646U2 specs clarify
+ * this point.
+ */
+ (void) pci_write_config_byte(dev, 0x71, mrdmode | 0x02);
+
+ /* Set reasonable active/recovery/address-setup values. */
+ (void) pci_write_config_byte(dev, 0x53, 0x40);
+ (void) pci_write_config_byte(dev, 0x54, 0x3f);
+ (void) pci_write_config_byte(dev, 0x55, 0x40);
+ (void) pci_write_config_byte(dev, 0x56, 0x3f);
+ (void) pci_write_config_byte(dev, 0x57, 0x5c);
+ (void) pci_write_config_byte(dev, 0x58, 0x3f);
+ (void) pci_write_config_byte(dev, 0x5b, 0x3f);
+ return 0;
+}
+
+unsigned int __init ata66_cmd64x (ide_hwif_t *hwif)
+{
+ byte ata66 = 0;
+ byte mask = (hwif->channel) ? 0x02 : 0x01;
+
+ pci_read_config_byte(hwif->pci_dev, 0x79, &ata66);
+ return (ata66 & mask) ? 1 : 0;
+}
+
+void __init ide_init_cmd64x (ide_hwif_t *hwif)
+{
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned int class_rev;
+
+ pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
+ class_rev &= 0xff;
+
+ if (!hwif->dma_base)
+ return;
+
+ switch(dev->device) {
+ case PCI_DEVICE_ID_CMD_643:
+ hwif->dmaproc = &cmd64x_dmaproc;
+ break;
+ case PCI_DEVICE_ID_CMD_646:
+ hwif->chipset = ide_cmd646;
+ if (class_rev == 0x01) {
+ hwif->dmaproc = &cmd646_1_dmaproc;
+ } else {
+ hwif->dmaproc = &cmd64x_dmaproc;
+ }
+ break;
+ case PCI_DEVICE_ID_CMD_648:
+ hwif->dmaproc = &cmd64x_dmaproc;
+ break;
+ default:
+ break;
+ }
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)