patch-2.0.9 linux/drivers/block/cmd640.c
Next file: linux/drivers/block/dtc2278.c
Previous file: linux/drivers/block/ali14xx.c
Back to the patch index
Back to the overall index
- Lines: 580
- Date:
Tue Jul 23 11:33:32 1996
- Orig file:
v2.0.8/linux/drivers/block/cmd640.c
- Orig date:
Sun May 5 08:51:58 1996
diff -u --recursive --new-file v2.0.8/linux/drivers/block/cmd640.c linux/drivers/block/cmd640.c
@@ -1,15 +1,26 @@
/*
- * linux/drivers/block/cmd640.c Version 0.09 Mar 19, 1996
+ * linux/drivers/block/cmd640.c Version 0.12 Jul 22, 1996
*
* Copyright (C) 1995-1996 Linus Torvalds & authors (see below)
*/
/*
- * Principal Author/Maintainer: abramov@cecmow.enet.dec.com (Igor Abramov)
+ * Original author: abramov@cecmow.enet.dec.com (Igor Abramov)
+ *
+ * Maintained by: s0033las@sun10.vsz.bme.hu (Laszlo Peter)
+ * mlord@pobox.com (Mark Lord)
*
* This file provides support for the advanced features and bugs
* of IDE interfaces using the CMD Technologies 0640 IDE interface chip.
*
+ * These chips are basically fucked by design, and getting this driver
+ * to work on every motherboard design that uses this screwed chip seems
+ * bloody well impossible. However, we're still trying.
+ *
+ * We think version 0.12 should work for most folks.
+ * User feedback is essential.
+ *
+ *
* Version 0.01 Initial version, hacked out of ide.c,
* and #include'd rather than compiled separately.
* This will get cleaned up in a subsequent release.
@@ -23,8 +34,8 @@
* with read ahead mode. Separate function for setting
* readahead is added, possibly it will be called some
* day from ioctl processing code.
- *
- * Version 0.04 Now configs/compiles separate from ide.c -ml
+ *
+ * Version 0.04 Now configs/compiles separate from ide.c -ml
*
* Version 0.05 Major rewrite of interface timing code.
* Added new function cmd640_set_mode to set PIO mode
@@ -33,9 +44,9 @@
* Version 0.06 More code cleanup. Readahead is enabled only for
* detected hard drives, not included in readahead
* black list.
- *
+ *
* Version 0.07 Changed to more conservative drive tuning policy.
- * Unknown drives, which report PIO < 4 are set to
+ * Unknown drives, which report PIO < 4 are set to
* (reported_PIO - 1) if it is supported, or to PIO0.
* List of known drives extended by info provided by
* CMD at their ftp site.
@@ -44,7 +55,12 @@
*
* Version 0.09 Try to be smarter about 2nd port enabling. -ml
* Version 0.10 Be nice and don't reset 2nd port. -ml
- *
+ * Version 0.11 Try to handle more wierd situations. -ml
+ *
+ * Version 0.12 Lots of bug fixes from Laszlo Peter
+ * irq unmasking disabled for reliability. -lp
+ * try to be even smarter about the second port. -lp
+ * tidy up source code formatting. -ml
*/
#undef REALLY_SLOW_IO /* most systems can safely undef this */
@@ -61,6 +77,8 @@
#include "ide.h"
#include "ide_modes.h"
+#define PARANOID_ABOUT_CMD640 1 /* used to tag superstitious code */
+
int cmd640_vlb = 0;
/*
@@ -109,8 +127,8 @@
static ide_tuneproc_t cmd640_tune_drive;
/* Interface to access cmd640x registers */
-static void (*put_cmd640_reg)(int reg_no, int val);
- byte (*get_cmd640_reg)(int reg_no);
+void (*put_cmd640_reg)(int reg_no, int val);
+byte (*get_cmd640_reg)(int reg_no);
enum { none, vlb, pci1, pci2 };
static int bus_type = none;
@@ -119,8 +137,9 @@
static int bus_speed; /* MHz */
/*
- * For some unknown reasons pcibios functions which read and write registers
- * do not always work with cmd640. We use direct IO instead.
+ * The CMD640x chip does not support DWORD config write cycles, but some
+ * of the BIOSes use them to implement the config services.
+ * We use direct IO instead.
*/
/* PCI method 1 access */
@@ -302,18 +321,17 @@
* Returns 1 if an IDE interface/drive exists at 0x170,
* Returns 0 otherwise.
*/
-int secondary_port_responding (void)
+static int secondary_port_responding (void)
{
/*
* Test for hardware at 0x170 (secondary IDE port).
- * Leave the enable-bit alone if something responds.
*/
- outb_p(0x0a,0x176); /* select drive0 */
- udelay(1);
- if (inb_p(0x176) == 0xff) {
- outb_p(0x0b,0x176); /* select drive1 */
- udelay(1);
- if (inb_p(0x176) == 0xff)
+ outb_p(0xa0, 0x170 + IDE_SELECT_OFFSET); /* select drive0 */
+ udelay(100);
+ if (inb_p(0x170 + IDE_SELECT_OFFSET) != 0xa0) {
+ outb_p(0xb0, 0x170 + IDE_SELECT_OFFSET); /* select drive1 */
+ udelay(100);
+ if (inb_p(0x170 + IDE_SELECT_OFFSET) != 0xb0)
return 0; /* nothing is there */
}
return 1; /* something is there */
@@ -325,8 +343,9 @@
int ide_probe_for_cmd640x(void)
{
- int second_port_toggled = 0;
- byte b;
+ int second_port_toggled = 0;
+ int second_port_cmd640 = 0;
+ byte b;
if (probe_for_cmd640_pci1()) {
bus_type = pci1;
@@ -339,10 +358,12 @@
return 0;
}
+#ifdef PARANOID_ABOUT_CMD640
ide_hwifs[0].serialized = 1; /* ensure this *always* gets set */
ide_hwifs[1].serialized = 1; /* ensure this *always* gets set */
+#endif
-#if 0
+#if 0
/* Dump initial state of chip registers */
for (b = 0; b != 0xff; b++) {
printk(" %2x%c", get_cmd640_reg(b),
@@ -374,33 +395,55 @@
/*
* Setup the most conservative timings for all drives,
*/
+
+ put_cmd640_reg(CMDTIM, 0);
+
put_cmd640_reg(ARTTIM0, 0xc0);
put_cmd640_reg(ARTTIM1, 0xc0);
- put_cmd640_reg(ARTTIM23, 0xcc); /* 0xc0? */
+ put_cmd640_reg(ARTTIM23, 0xcc); /* disable read-ahead for drives 2&3 */
+
+ put_cmd640_reg(DRWTIM0, 0);
+ put_cmd640_reg(DRWTIM1, 0);
+ put_cmd640_reg(DRWTIM23, 0);
/*
* Set the maximum allowed bus speed (it is safest until we
* find how to detect bus speed)
* Normally PCI bus runs at 33MHz, but often works overclocked to 40
*/
- bus_speed = (bus_type == vlb) ? 50 : 40;
+ bus_speed = (bus_type == vlb) ? 50 : 40;
- /*
+ /*
* Setup Control Register
*/
- b = get_cmd640_reg(CNTRL);
-
- if (!secondary_port_responding()) {
- b ^= CNTRL_ENA_2ND; /* toggle the bit */
- second_port_toggled = 1;
- }
+ b = get_cmd640_reg(CNTRL);
/*
* Disable readahead for drives at primary interface
*/
b |= (CNTRL_DIS_RA0 | CNTRL_DIS_RA1);
+ put_cmd640_reg(CNTRL, b);
+
+ if (!ide_hwifs[1].noprobe) {
+
+ if (secondary_port_responding()) {
+
+ if ((b & CNTRL_ENA_2ND) || (bus_type == vlb))
+ second_port_cmd640 = 1;
+
+ } else if (!(b & CNTRL_ENA_2ND) && (bus_type != vlb)) {
- put_cmd640_reg(CNTRL, b);
+ second_port_toggled = 1;
+ put_cmd640_reg(CNTRL, b | CNTRL_ENA_2ND); /* Enable second interface */
+
+ if (secondary_port_responding())
+ second_port_cmd640 = 1;
+ else {
+ second_port_toggled = 0;
+ put_cmd640_reg(CNTRL, b); /* Disable second interface */
+ }
+ }
+ }
/*
* Note that we assume that the first interface is at 0x1f0,
@@ -408,6 +451,8 @@
*/
ide_hwifs[0].chipset = ide_cmd640;
ide_hwifs[0].tuneproc = &cmd640_tune_drive;
+ ide_hwifs[0].no_unmask = 1;
+
if (ide_hwifs[0].drives[0].autotune == 0)
ide_hwifs[0].drives[0].autotune = 1;
if (ide_hwifs[0].drives[1].autotune == 0)
@@ -416,27 +461,29 @@
/*
* Initialize 2nd IDE port, if required
*/
- if (secondary_port_responding()) {
+ if (second_port_cmd640) {
+
+#ifndef PARANOID_ABOUT_CMD640
+ ide_hwifs[0].serialized = 1;
+ ide_hwifs[1].serialized = 1;
+#endif
+
ide_hwifs[1].chipset = ide_cmd640;
ide_hwifs[1].tuneproc = &cmd640_tune_drive;
+ ide_hwifs[1].no_unmask = 1;
+
if (ide_hwifs[1].drives[0].autotune == 0)
ide_hwifs[1].drives[0].autotune = 1;
if (ide_hwifs[1].drives[1].autotune == 0)
ide_hwifs[1].drives[1].autotune = 1;
- /* disable read-ahead for drives 2 & 3 */
- put_cmd640_reg(ARTTIM23, (DIS_RA2 | DIS_RA3));
- if (second_port_toggled) {
- /* reset PIO timings for drives 2 & 3 */
- put_cmd640_reg(DRWTIM23, 0);
- }
#if 0
/* reset the secondary interface */
cmd640_reset_controller(1);
#endif
}
- printk("ide: buggy CMD640%c interface on ",
+ printk("ide: buggy CMD640%c interface on ",
'A' - 1 + cmd640_chip_version);
switch (bus_type) {
case vlb :
@@ -450,15 +497,12 @@
break;
}
-#if 0
- /* reset PIO timings for drives 1 & 2 */
- put_cmd640_reg(CMDTIM, 0);
-#endif /* 0 */
-
/*
* Tell everyone what we did to their system
*/
- printk("; serialized, secondary port %s\n", second_port_toggled ? "toggled" : "untouched");
+ printk(":%s serialized, second port %s\n",
+ second_port_cmd640 ? "" : " not",
+ second_port_toggled ? "toggled" : "untouched");
return 1;
}
@@ -470,7 +514,7 @@
if (bus_type == none || a == 1)
return 0;
a = 1;
- b = get_cmd640_reg(CNTRL);
+ b = get_cmd640_reg(CNTRL);
b &= ~CNTRL_ENA_2ND;
put_cmd640_reg(CNTRL, b);
return 1;
@@ -482,35 +526,48 @@
* in the future it could be called from ioctl
*/
-static void set_readahead_mode(int mode, int if_num, int dr_num)
+static void set_readahead_mode(ide_drive_t* drive, int mode)
{
- static int masks[2][2] =
- {
- {CNTRL_DIS_RA0, CNTRL_DIS_RA1},
+ static int masks[2][2] =
+ {
+ {CNTRL_DIS_RA0, CNTRL_DIS_RA1},
{DIS_RA2, DIS_RA3}
};
-
- int port = (if_num == 0) ? CNTRL : ARTTIM23;
- int mask = masks[if_num][dr_num];
byte b;
+ int interface_number = HWIF(drive)->index;
+ int drive_number = drive->select.b.unit;
+
+ int port = (interface_number == 0) ? CNTRL : ARTTIM23;
+ int mask = masks[interface_number][drive_number];
+
b = get_cmd640_reg(port);
+
+ /*
+ * I don't know why it is necessary, but without this my machine
+ * locks up, if bus_speed is not correct. And it even allows me
+ * to use 32 bit transfers on the primary port (hdparm -c1).
+ */
+ if ((interface_number == 0) && mode)
+ b|=0x27;
+
if (mode)
b &= ~mask; /* Enable readahead for specific drive */
else
b |= mask; /* Disable readahead for specific drive */
+
put_cmd640_reg(port, b);
-}
+}
-static struct readahead_black_list {
+static const struct readahead_black_list {
const char* name;
- int mode;
+ int mode;
} drives_ra[] = {
- { "QUANTUM LIGHTNING 540A", 0 },
- { "ST3655A", 0 },
- { "SAMSUNG", 0 }, /* Be conservative */
- { NULL, 0 }
-};
+ { "QUANTUM LIGHTNING 540A", 0 },
+ { "ST3655A", 0 },
+ { "SAMSUNG", 0 }, /* Be conservative */
+ { NULL, 0 }
+};
static int strmatch(const char* pattern, const char* name) {
char c1, c2;
@@ -534,7 +591,7 @@
return drives_ra[i].mode;
}
}
- return -1;
+ return -1;
}
static int arttim[4] = {2, 2, 2, 2}; /* Address setup count (in clocks) */
@@ -542,7 +599,7 @@
static int r_count[4] = {1, 1, 1, 1}; /* Recovery count (encoded) */
/*
- * Convert address setup count from number of clocks
+ * Convert address setup count from number of clocks
* to representation used by controller
*/
@@ -575,6 +632,7 @@
{
int b_reg;
int ac, rc, at;
+ byte b;
/*
* Set address setup count and drive read/write timing registers.
@@ -588,22 +646,28 @@
at = arttim[dr_num];
ac = a_count[dr_num];
rc = r_count[dr_num];
+ b = pack_arttim(at);
} else {
b_reg = ARTTIM23;
at = max(arttim[2], arttim[3]);
ac = max(a_count[2], a_count[3]);
rc = max(r_count[2], r_count[3]);
+
+ /*
+ * Protect the readahead bits
+ */
+ b = pack_arttim(at) | (get_cmd640_reg(ARTTIM23) & (DIS_RA2 | DIS_RA3));
}
- put_cmd640_reg(b_reg, pack_arttim(at));
+ put_cmd640_reg(b_reg, b);
put_cmd640_reg(b_reg + 1, pack_counts(ac, rc));
/*
- * Update CMDTIM (IDE Command Block Timing Register)
+ * Update CMDTIM (IDE Command Block Timing Register)
*/
- ac = max4(r_count);
- rc = max4(a_count);
+ ac = max4(a_count);
+ rc = max4(r_count);
put_cmd640_reg(CMDTIM, pack_counts(ac, rc));
}
@@ -611,17 +675,17 @@
* Standard timings for PIO modes
*/
-static struct pio_timing {
- int mc_time; /* Minimal cycle time (ns) */
- int av_time; /* Address valid to DIOR-/DIOW- setup (ns) */
- int ds_time; /* DIOR data setup (ns) */
+static const struct pio_timing {
+ int mc_time; /* Address setup (ns) min */
+ int av_time; /* Active pulse (ns) min */
+ int ds_time; /* Cycle time (ns) min = Active pulse + Recovery pulse */
} pio_timings[6] = {
{ 70, 165, 600 }, /* PIO Mode 0 */
{ 50, 125, 383 }, /* PIO Mode 1 */
{ 30, 100, 240 }, /* PIO Mode 2 */
- { 30, 80, 180 }, /* PIO Mode 3 */
- { 25, 70, 125 }, /* PIO Mode 4 -- should be 120, not 125 */
- { 20, 50, 100 } /* PIO Mode ? (nonstandard) */
+ { 30, 80, 180 }, /* PIO Mode 3 w/IORDY */
+ { 25, 70, 125 }, /* PIO Mode 4 w/IORDY -- should be 120, not 125 */
+ { 20, 50, 100 } /* PIO Mode 5 w/IORDY (nonstandard) */
};
static void cmd640_timings_to_clocks(int mc_time, int av_time, int ds_time,
@@ -657,11 +721,13 @@
int i;
p_base = if_num ? 0x170 : 0x1f0;
- outb_p(3, p_base + 1);
- outb_p(mode_num | 8, p_base + 2);
- outb_p((drv_num | 0xa) << 4, p_base + 6);
- outb_p(0xef, p_base + 7);
- for (i = 0; (i < 100) && (inb (p_base + 7) & 0x80); i++)
+
+ outb_p(3, p_base + IDE_FEATURE_OFFSET);
+ outb_p(mode_num | 0x08, p_base + IDE_NSECTOR_OFFSET);
+ outb_p((drv_num | 0x0a) << 4, p_base + IDE_SELECT_OFFSET);
+ outb_p(WIN_SETFEATURES, p_base + IDE_COMMAND_OFFSET);
+
+ for (i = 0; (i < 100) && (inb(p_base + IDE_STATUS_OFFSET) & BUSY_STAT); i++)
udelay(10000);
}
@@ -669,11 +735,13 @@
* Set a specific pio_mode for a drive
*/
-static void cmd640_set_mode(ide_drive_t* drive, int pio_mode) {
+static void cmd640_set_mode(ide_drive_t* drive, byte pio_mode, int ds_time) {
int interface_number;
int drive_number;
int clock_time; /* ns */
- int mc_time, av_time, ds_time;
+ int mc_time, av_time;
+
+ if (pio_mode > 5) return;
interface_number = HWIF(drive)->index;
drive_number = drive->select.b.unit;
@@ -681,7 +749,7 @@
mc_time = pio_timings[pio_mode].mc_time;
av_time = pio_timings[pio_mode].av_time;
- ds_time = pio_timings[pio_mode].ds_time;
+ ds_time = (ds_time != 0) ? ds_time : pio_timings[pio_mode].ds_time;
cmd640_timings_to_clocks(mc_time, av_time, ds_time, clock_time,
interface_number*2 + drive_number);
@@ -691,29 +759,29 @@
/*
* Drive PIO mode "autoconfiguration".
- * Ideally, this code should *always* call cmd640_set_mode(), but it doesn't.
*/
static void cmd640_tune_drive(ide_drive_t *drive, byte pio_mode) {
- int interface_number;
- int drive_number;
- int clock_time; /* ns */
int max_pio;
- int mc_time, av_time, ds_time;
- struct hd_driveid* id;
+ int ds_time;
int readahead; /* there is a global named read_ahead */
+ int overridden;
+ int iordy;
+ struct hd_driveid* id;
if (pio_mode != 255) {
- cmd640_set_mode(drive, pio_mode);
+ cmd640_set_mode(drive, pio_mode, 0);
return;
}
- interface_number = HWIF(drive)->index;
- drive_number = drive->select.b.unit;
- clock_time = 1000/bus_speed;
+ overridden = 0;
+ iordy = 0;
id = drive->id;
+
if ((max_pio = ide_scan_pio_blacklist(id->model)) != -1) {
ds_time = pio_timings[max_pio].ds_time;
+ overridden = 1;
+ iordy = (max_pio > 2);
} else {
max_pio = id->tPIO;
ds_time = pio_timings[max_pio].ds_time;
@@ -723,27 +791,28 @@
else if (id->eide_pio_modes & 2) max_pio = 4;
else max_pio = 3;
ds_time = id->eide_pio_iordy;
+ iordy = 1;
} else {
ds_time = id->eide_pio;
}
- if (ds_time == 0)
+ if (ds_time == 0) {
ds_time = pio_timings[max_pio].ds_time;
+ iordy = (max_pio > 2);
+ }
}
/*
* Conservative "downgrade"
*/
if (max_pio < 4 && max_pio != 0) {
- max_pio -= 1;
- ds_time = pio_timings[max_pio].ds_time;
+ max_pio--;
+ overridden = 1;
+ iordy = (max_pio > 2);
+ ds_time = pio_timings[max_pio].ds_time;
}
}
- mc_time = pio_timings[max_pio].mc_time;
- av_time = pio_timings[max_pio].av_time;
- cmd640_timings_to_clocks(mc_time, av_time, ds_time, clock_time,
- interface_number*2 + drive_number);
- set_pio_mode(interface_number, drive_number, max_pio);
- cmd640_set_timing(interface_number, drive_number);
+
+ cmd640_set_mode(drive, max_pio, ds_time);
/*
* Disable (or set) readahead mode
@@ -754,10 +823,13 @@
readahead = known_drive_readahead(id->model);
if (readahead == -1)
readahead = 1; /* Mmmm.. probably be 0 ?? */
- set_readahead_mode(readahead, interface_number, drive_number);
- }
+ set_readahead_mode(drive, readahead);
+ }
- printk ("Mode and Timing set to PIO%d, Readahead is %s\n",
- max_pio, readahead ? "enabled" : "disabled");
+ printk ("Drive Timing: PIO Mode %d (%dns) %s/IORDY%s, Read-ahead: %s\n",
+ max_pio,
+ ds_time,
+ iordy ? "w" : "wo",
+ overridden ? " (overriding vendor mode)" : "",
+ readahead ? "enabled" : "disabled");
}
-
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov