summaryrefslogtreecommitdiffstats
path: root/drivers/block/cmd64x.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/block/cmd64x.c')
-rw-r--r--drivers/block/cmd64x.c278
1 files changed, 262 insertions, 16 deletions
diff --git a/drivers/block/cmd64x.c b/drivers/block/cmd64x.c
index 8f5f04aea..4e98893ec 100644
--- a/drivers/block/cmd64x.c
+++ b/drivers/block/cmd64x.c
@@ -1,4 +1,4 @@
-/* $Id: cmd64x.c,v 1.20 1999/12/30 03:48:37
+/* $Id: cmd64x.c,v 1.21 2000/01/30 23:23:16
*
* cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines.
* Note, this driver is not used at all on other systems because
@@ -8,7 +8,7 @@
*
* Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
* Copyright (C) 1998 David S. Miller (davem@redhat.com)
- * Copyright (C) 1999 Andre Hedrick (andre@suse.com)
+ * Copyright (C) 1999-2000 Andre Hedrick (andre@suse.com)
*/
#include <linux/types.h>
@@ -18,8 +18,10 @@
#include <linux/ide.h>
#include <asm/io.h>
+#include "ide_modes.h"
#define CMD_DEBUG 0
+#undef NO_WRITE
#if CMD_DEBUG
#define cmdprintk(x...) printk(##x)
@@ -27,6 +29,235 @@
#define cmdprintk(x...)
#endif
+/*
+ * CMD64x specific registers definition.
+ */
+
+#define CNTRL 0x51
+#define CNTRL_DIS_RA0 0x40
+#define CNTRL_DIS_RA1 0x80
+#define CNTRL_ENA_2ND 0x08
+
+#define CMDTIM 0x52
+#define ARTTIM0 0x53
+#define DRWTIM0 0x54
+#define ARTTIM1 0x55
+#define DRWTIM1 0x56
+#define ARTTIM23 0x57
+#define ARTTIM23_DIS_RA2 0x04
+#define ARTTIM23_DIS_RA3 0x08
+#define DRWTIM23 0x58
+#define DRWTIM2 0x58
+#define BRST 0x59
+#define DRWTIM3 0x5b
+
+#define MRDMODE 0x71
+
+#define DISPLAY_CMD64X_TIMINGS
+
+#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int cmd64x_get_info(char *, char **, off_t, int);
+extern int (*cmd64x_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int cmd64x_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+ char *p = buffer;
+ u32 bibma = bmide_dev->resource[4].start;
+ u8 c0 = 0, c1 = 0;
+
+ switch(bmide_dev->device) {
+ case PCI_DEVICE_ID_CMD_648:
+ p += sprintf(p, "\n CMD648 Chipset.\n");
+ break;
+ case PCI_DEVICE_ID_CMD_646:
+ p += sprintf(p, "\n CMD646 Chipset.\n");
+ break;
+ case PCI_DEVICE_ID_CMD_643:
+ p += sprintf(p, "\n CMD643 Chipset.\n");
+ break;
+ default:
+ p += sprintf(p, "\n CMD64? Chipse.\n");
+ break;
+ }
+
+ /*
+ * at that point bibma+0x2 et bibma+0xa are byte registers
+ * to investigate:
+ */
+ c0 = inb_p((unsigned short)bibma + 0x02);
+ c1 = inb_p((unsigned short)bibma + 0x0a);
+ p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+ p += sprintf(p, " %sabled %sabled\n",
+ (c0&0x80) ? "dis" : " en",
+ (c1&0x80) ? "dis" : " en");
+ p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+ p += sprintf(p, "DMA enabled: %s %s %s %s\n",
+ (c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
+ (c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
+
+ p += sprintf(p, "UDMA\n");
+ p += sprintf(p, "DMA\n");
+ p += sprintf(p, "PIO\n");
+
+ return p-buffer; /* => must be less than 4k! */
+}
+#endif /* defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte cmd64x_proc = 0;
+
+/*
+ * Registers and masks for easy access by drive index:
+ */
+#if 0
+static byte prefetch_regs[4] = {CNTRL, CNTRL, ARTTIM23, ARTTIM23};
+static byte prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3};
+#endif
+
+/*
+ * This routine writes the prepared setup/active/recovery counts
+ * for a drive into the cmd646 chipset registers to active them.
+ */
+static void program_drive_counts (ide_drive_t *drive, int setup_count, int active_count, int recovery_count)
+{
+ unsigned long flags;
+ ide_drive_t *drives = HWIF(drive)->drives;
+ byte temp_b;
+ static const byte setup_counts[] = {0x40, 0x40, 0x40, 0x80, 0, 0xc0};
+ static const byte recovery_counts[] =
+ {15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0};
+ static const byte arttim_regs[2][2] = {
+ { ARTTIM0, ARTTIM1 },
+ { ARTTIM23, ARTTIM23 }
+ };
+ static const byte drwtim_regs[2][2] = {
+ { DRWTIM0, DRWTIM1 },
+ { DRWTIM2, DRWTIM3 }
+ };
+ int channel = (int) HWIF(drive)->channel;
+ int slave = (drives != drive); /* Is this really the best way to determine this?? */
+
+ cmdprintk("program_drive_count parameters = s(%d),a(%d),r(%d),p(%d)\n", setup_count,
+ active_count, recovery_count, drive->present);
+ /*
+ * Set up address setup count registers.
+ * Primary interface has individual count/timing registers for
+ * each drive. Secondary interface has one common set of registers,
+ * for address setup so we merge these timings, using the slowest
+ * value.
+ */
+ if (channel) {
+ drive->drive_data = setup_count;
+ setup_count = IDE_MAX(drives[0].drive_data, drives[1].drive_data);
+ cmdprintk("Secondary interface, setup_count = %d\n", setup_count);
+ }
+
+ /*
+ * Convert values to internal chipset representation
+ */
+ setup_count = (setup_count > 5) ? 0xc0 : (int) setup_counts[setup_count];
+ active_count &= 0xf; /* Remember, max value is 16 */
+ recovery_count = (int) recovery_counts[recovery_count];
+
+ cmdprintk("Final values = %d,%d,%d\n", setup_count, active_count, recovery_count);
+
+ /*
+ * Now that everything is ready, program the new timings
+ */
+ __save_flags (flags);
+ __cli();
+ /*
+ * Program the address_setup clocks into ARTTIM reg,
+ * and then the active/recovery counts into the DRWTIM reg
+ */
+ (void) pci_read_config_byte(HWIF(drive)->pci_dev, arttim_regs[channel][slave], &temp_b);
+#ifndef NO_WRITE
+ (void) pci_write_config_byte(HWIF(drive)->pci_dev, arttim_regs[channel][slave],
+ ((byte) setup_count) | (temp_b & 0x3f));
+ (void) pci_write_config_byte(HWIF(drive)->pci_dev, drwtim_regs[channel][slave],
+ (byte) ((active_count << 4) | recovery_count));
+#endif
+ cmdprintk ("Write %x to %x\n", ((byte) setup_count) | (temp_b & 0x3f), arttim_regs[channel][slave]);
+ cmdprintk ("Write %x to %x\n", (byte) ((active_count << 4) | recovery_count), drwtim_regs[channel][slave]);
+ __restore_flags(flags);
+}
+
+/*
+ * Attempts to set the interface PIO mode.
+ * The preferred method of selecting PIO modes (e.g. mode 4) is
+ * "echo 'piomode:4' > /proc/ide/hdx/settings". Special cases are
+ * 8: prefetch off, 9: prefetch on, 255: auto-select best mode.
+ * Called with 255 at boot time.
+ */
+static void cmd64x_tuneproc (ide_drive_t *drive, byte mode_wanted)
+{
+ int setup_time, active_time, recovery_time, clock_time, pio_mode, cycle_time;
+ byte recovery_count2, cycle_count;
+ int setup_count, active_count, recovery_count;
+ int bus_speed = ide_system_bus_speed();
+ /*byte b;*/
+ ide_pio_data_t d;
+
+ switch (mode_wanted) {
+ case 8: /* set prefetch off */
+ case 9: /* set prefetch on */
+ mode_wanted &= 1;
+ /*set_prefetch_mode(index, mode_wanted);*/
+ cmdprintk("%s: %sabled cmd640 prefetch\n", drive->name, mode_wanted ? "en" : "dis");
+ return;
+ }
+
+ (void) ide_get_best_pio_mode (drive, mode_wanted, 5, &d);
+ pio_mode = d.pio_mode;
+ cycle_time = d.cycle_time;
+
+ /*
+ * I copied all this complicated stuff from cmd640.c and made a few minor changes.
+ * For now I am just going to pray that it is correct.
+ */
+ if (pio_mode > 5)
+ pio_mode = 5;
+ setup_time = ide_pio_timings[pio_mode].setup_time;
+ active_time = ide_pio_timings[pio_mode].active_time;
+ recovery_time = cycle_time - (setup_time + active_time);
+ clock_time = 1000 / bus_speed;
+ cycle_count = (cycle_time + clock_time - 1) / clock_time;
+
+ setup_count = (setup_time + clock_time - 1) / clock_time;
+
+ active_count = (active_time + clock_time - 1) / clock_time;
+
+ recovery_count = (recovery_time + clock_time - 1) / clock_time;
+ recovery_count2 = cycle_count - (setup_count + active_count);
+ if (recovery_count2 > recovery_count)
+ recovery_count = recovery_count2;
+ if (recovery_count > 16) {
+ active_count += recovery_count - 16;
+ recovery_count = 16;
+ }
+ if (active_count > 16)
+ active_count = 16; /* maximum allowed by cmd646 */
+
+ /*
+ * In a perfect world, we might set the drive pio mode here
+ * (using WIN_SETFEATURE) before continuing.
+ *
+ * But we do not, because:
+ * 1) this is the wrong place to do it (proper is do_special() in ide.c)
+ * 2) in practice this is rarely, if ever, necessary
+ */
+ program_drive_counts (drive, setup_count, active_count, recovery_count);
+
+ printk ("%s: selected cmd646 PIO mode%d (%dns)%s, clocks=%d/%d/%d\n",
+ drive->name, pio_mode, cycle_time,
+ d.overridden ? " (overriding vendor mode)" : "",
+ setup_count, active_count, recovery_count);
+}
+
static int config_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ultra_66)
{
struct hd_driveid *id = drive->id;
@@ -123,7 +354,7 @@ static int config_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ul
static void config_chipset_for_pio (ide_drive_t *drive, unsigned int rev)
{
- /* FIXME!! figure out some PIOing junk.... */
+ cmd64x_tuneproc(drive, 5);
}
static int cmd64x_config_drive_for_dma (ide_drive_t *drive)
@@ -198,7 +429,7 @@ fast_ata_pio:
no_dma_set:
config_chipset_for_pio(drive, class_rev);
}
- return hwif->dmaproc(dma_func, drive);
+ return HWIF(drive)->dmaproc(dma_func, drive);
}
static int cmd64x_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
@@ -277,9 +508,9 @@ unsigned int __init pci_init_cmd64x (struct pci_dev *dev, const char *name)
#endif
/* Setup interrupts. */
- (void) pci_read_config_byte(dev, 0x71, &mrdmode);
+ (void) pci_read_config_byte(dev, MRDMODE, &mrdmode);
mrdmode &= ~(0x30);
- (void) pci_write_config_byte(dev, 0x71, mrdmode);
+ (void) pci_write_config_byte(dev, MRDMODE, mrdmode);
/* Use MEMORY READ LINE for reads.
* NOTE: Although not mentioned in the PCI0646U specs,
@@ -287,16 +518,26 @@ unsigned int __init pci_init_cmd64x (struct pci_dev *dev, const char *name)
* back as set or not. The PCI0646U2 specs clarify
* this point.
*/
- (void) pci_write_config_byte(dev, 0x71, mrdmode | 0x02);
-
+ (void) pci_write_config_byte(dev, MRDMODE, mrdmode | 0x02);
+#if 0
/* 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);
+ (void) pci_write_config_byte(dev, ARTTIM0, 0x40);
+ (void) pci_write_config_byte(dev, DRWTIM0, 0x3f);
+ (void) pci_write_config_byte(dev, ARTTIM1, 0x40);
+ (void) pci_write_config_byte(dev, DRWTIM1, 0x3f);
+ (void) pci_write_config_byte(dev, ARTTIM23, 0x5c);
+ (void) pci_write_config_byte(dev, DRWTIM23, 0x3f);
+ (void) pci_write_config_byte(dev, DRWTIM3, 0x3f);
+#else
+ (void) pci_write_config_byte(dev, ARTTIM23, 0x1c);
+#endif
+
+#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
+ cmd64x_proc = 1;
+ bmide_dev = dev;
+ cmd64x_display_info = &cmd64x_get_info;
+#endif /* DISPLAY_CMD64X_TIMINGS && CONFIG_PROC_FS */
+
return 0;
}
@@ -317,8 +558,13 @@ void __init ide_init_cmd64x (ide_hwif_t *hwif)
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
class_rev &= 0xff;
- if (!hwif->dma_base)
+ hwif->tuneproc = &cmd64x_tuneproc;
+
+ if (!hwif->dma_base) {
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
return;
+ }
switch(dev->device) {
case PCI_DEVICE_ID_CMD_643: