patch-2.1.115 linux/drivers/block/cmd646.c
Next file: linux/drivers/block/genhd.c
Previous file: linux/drivers/block/Config.in
Back to the patch index
Back to the overall index
- Lines: 253
- Date:
Tue Aug 4 16:56:37 1998
- Orig file:
v2.1.114/linux/drivers/block/cmd646.c
- Orig date:
Mon Apr 6 17:40:59 1998
diff -u --recursive --new-file v2.1.114/linux/drivers/block/cmd646.c linux/drivers/block/cmd646.c
@@ -1,20 +1,248 @@
-/* $Id: cmd646.c,v 1.1 1998/03/15 13:29:10 ecd Exp $
- * cmd646.c: Enable interrupts at initialization time on Ultra/PCI machines
+/* $Id: cmd646.c,v 1.10 1998/08/03 15:28:42 davem Exp $
+ * cmd646.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)
*/
+#include <linux/types.h>
#include <linux/pci.h>
+#include <linux/delay.h>
+#include <asm/io.h>
#include "ide.h"
+static int cmd646_config_drive_for_dma(ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+
+ /* Even if the drive is not _currently_ in a DMA
+ * mode, we succeed, and we'll enable it manually
+ * below in cmd646_dma_onoff.
+ *
+ * This is done for disks only, CDROMs and other
+ * IDE devices are just too quirky.
+ */
+ if((id != NULL) &&
+ ((id->capability & 1) != 0) &&
+ hwif->autodma &&
+ (drive->media == ide_disk)) {
+ if(id->field_valid & 0x0004) {
+ if(id->dma_ultra & 0x0007)
+ return hwif->dmaproc(ide_dma_on, drive);
+ }
+ if(id->field_valid & 0x0002)
+ if((id->dma_mword & 0x0004) || (id->dma_1word & 0x0004))
+ return hwif->dmaproc(ide_dma_on, drive);
+ }
+ return hwif->dmaproc(ide_dma_off_quietly, drive);
+}
+
+/* This is fun. -DaveM */
+#define IDE_SETXFER 0x03
+#define IDE_SETFEATURE 0xef
+#define IDE_DMA2_ENABLE 0x22
+#define IDE_DMA1_ENABLE 0x21
+#define IDE_DMA0_ENABLE 0x20
+#define IDE_UDMA2_ENABLE 0x42
+#define IDE_UDMA1_ENABLE 0x41
+#define IDE_UDMA0_ENABLE 0x40
+
+static __inline__ unsigned char dma2_bits_to_command(unsigned char bits)
+{
+ if(bits & 0x04)
+ return IDE_DMA2_ENABLE;
+ if(bits & 0x02)
+ return IDE_DMA1_ENABLE;
+ return IDE_DMA0_ENABLE;
+}
+
+static __inline__ unsigned char udma2_bits_to_command(unsigned char bits)
+{
+ if(bits & 0x04)
+ return IDE_UDMA2_ENABLE;
+ if(bits & 0x02)
+ return IDE_UDMA1_ENABLE;
+ return IDE_UDMA0_ENABLE;
+}
+
+static __inline__ int wait_for_ready(ide_drive_t *drive)
+{
+ int timeout = 100;
+ byte stat;
+
+ while(--timeout) {
+ stat = GET_STAT();
+
+ printk("STAT(%2x) ", stat);
+ if(!(stat & BUSY_STAT)) {
+ if((stat & READY_STAT) || (stat & ERR_STAT))
+ break;
+ }
+ udelay(100);
+ }
+ if((stat & ERR_STAT) || timeout <= 0)
+ return 1;
+ return 0;
+}
+
+static void cmd646_do_setfeature(ide_drive_t *drive, byte command)
+{
+ unsigned long flags;
+ byte old_select;
+
+ save_flags(flags);
+ cli();
+ printk("SELECT ");
+ old_select = IN_BYTE(IDE_SELECT_REG);
+ OUT_BYTE(drive->select.all, IDE_SELECT_REG);
+ printk("SETXFER ");
+ OUT_BYTE(IDE_SETXFER, IDE_FEATURE_REG);
+ printk("CMND ");
+ OUT_BYTE(command, IDE_NSECTOR_REG);
+ printk("wait ");
+ if(wait_for_ready(drive))
+ goto out;
+ printk("SETFEATURE ");
+ OUT_BYTE(IDE_SETFEATURE, IDE_COMMAND_REG);
+ printk("wait ");
+ (void) wait_for_ready(drive);
+out:
+ OUT_BYTE(old_select, IDE_SELECT_REG);
+ restore_flags(flags);
+}
+
+static void cmd646_dma2_enable(ide_drive_t *drive, unsigned long dma_base)
+{
+ byte unit = (drive->select.b.unit & 0x01);
+ byte bits = (drive->id->dma_mword | drive->id->dma_1word) & 0x07;
+
+ printk("CMD646: MDMA enable [");
+ if((((drive->id->dma_mword & 0x0007) << 8) !=
+ (drive->id->dma_mword & 0x0700)))
+ cmd646_do_setfeature(drive, dma2_bits_to_command(bits));
+ printk("DMA_CAP ");
+ outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+ printk("DONE]\n");
+}
+
+static void cmd646_udma_enable(ide_drive_t *drive, unsigned long dma_base)
+{
+ byte unit = (drive->select.b.unit & 0x01);
+ byte udma_ctrl, bits = drive->id->dma_ultra & 0x07;
+ byte udma_timing_bits;
+
+ printk("CMD646: UDMA enable [");
+ if(((drive->id->dma_ultra & 0x0007) << 8) !=
+ (drive->id->dma_ultra & 0x0700))
+ cmd646_do_setfeature(drive, udma2_bits_to_command(bits));
+
+ /* Enable DMA and UltraDMA */
+ printk("DMA_CAP ");
+ outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+
+ udma_ctrl = inb(dma_base + 3);
+
+ /* Put this channel into UDMA mode. */
+ printk("UDMA_CTRL ");
+ udma_ctrl |= (1 << unit);
+
+ /* Set UDMA2 usable timings. */
+ if(bits & 0x04)
+ udma_timing_bits = 0x10;
+ else if(bits & 0x02)
+ udma_timing_bits = 0x20;
+ else
+ udma_timing_bits = 0x30;
+ udma_ctrl &= ~(0x30 << (unit * 2));
+ udma_ctrl |= (udma_timing_bits << (unit * 2));
+
+ outb(udma_ctrl, dma_base+3);
+ printk("DONE]\n");
+}
+
+static int cmd646_dma_onoff(ide_drive_t *drive, int enable)
+{
+ if(enable) {
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned long dma_base = hwif->dma_base;
+ struct hd_driveid *id = drive->id;
+ unsigned int class_rev;
+
+ /* UltraDMA only supported on PCI646U and PCI646U2,
+ * which correspond to revisions 0x03 and 0x05 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 chipsets.
+ */
+ pci_read_config_dword(hwif->pci_dev,
+ PCI_CLASS_REVISION,
+ &class_rev);
+ class_rev &= 0xff;
+ if((class_rev == 0x05) &&
+ (id->field_valid & 0x0004) &&
+ (id->dma_ultra & 0x07)) {
+ /* UltraDMA modes. */
+ cmd646_udma_enable(drive, dma_base);
+ } else {
+ /* Normal MultiWord DMA modes. */
+ cmd646_dma2_enable(drive, dma_base);
+ }
+ }
+ drive->using_dma = enable;
+ return 0;
+}
+
+static int cmd646_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+ if(func == ide_dma_check)
+ return cmd646_config_drive_for_dma(drive);
+ else if(func == ide_dma_on || func == ide_dma_off || func == ide_dma_off_quietly)
+ return cmd646_dma_onoff(drive, (func == ide_dma_on));
+
+ /* Other cases are done by generic IDE-DMA code. */
+ return ide_dmaproc(func, drive);
+}
+
__initfunc(void ide_init_cmd646 (ide_hwif_t *hwif))
{
-#ifdef __sparc_v9__
struct pci_dev *dev = hwif->pci_dev;
unsigned char mrdmode;
+ hwif->chipset = ide_cmd646;
+
+ /* Set a good latency timer value. */
+ (void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 240);
+
+ /* Setup interrupts. */
(void) pci_read_config_byte(dev, 0x71, &mrdmode);
mrdmode &= ~(0x30);
(void) pci_write_config_byte(dev, 0x71, mrdmode);
-#endif
+
+ /* 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);
+
+ hwif->dmaproc = &cmd646_dmaproc;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov