Final (?) CMD640 driver beta

mlord (mlord@pobox.com)
Fri, 02 Aug 1996 11:15:16 -0400


This is a multi-part message in MIME format.

--------------116121F7C3FAF555575A74D
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

This is (hopefully) the final beta test version of the cmd640.c driver.

This is a fresh patch to go on top of linux-2.0.10.
Please give it a try and report back before Monday if possible.

I have rearranged a LOT of code in cmd640, with the result
that the driver, by default, leaves the existing drive speeds
and prefetch settings as-is under most conditions.

This should help the two cases where the previous patch was
still failing.

You will notice a new config option, "CMD640 enhanced support".

Not selecting this will result in a 2500 byte size saving,
at the expense of no code to change piomodes or prefetch.

As usual, I need to know what happens (messages, please), when:

1. the driver is run with "CMD640 enhanced support".
2. same as (1) but with: ide0=autotune ide1=autotune
3. same as (2) but also with: idebus=33
(or whatever speed your PCI bus runs at).
Note that this is the riskiest test, but if
all goes well, your drives will be *very* fast.
3. the driver is run without "CMD640 enhanced support".

Feel free to optionally experiment at will with:

hdparm -c1 32bit I/O, valid only when prefetch is on
hdparm -u1 IRQ unmasking, valid only when prefetch off
hdparm -p9 turns on prefetch, disables unmasking
hdparm -p8 turns off prefetch, disables 32bit I/O

Those options now all include safeguards to permit only the valid
combinations, as indicated. hdparm-3.1 is required for all of this:

ftp://sunsite.unc.edu/pub/Linux/Incoming/hdparm-3.1.tar.gz

If you can try it out and respond by Monday morning (what a dreamer,
eh!),
then I can release it to Linus for 2.0.xx early next week.

I have taken the liberty of incorporating your email addresses into the
source file (cmd640.c), mostly so the lynch mob can find you all later.
:)

Thanks for all your help thus far. We're just about There.

Cheers,

-ml
mlord@pobox.com
the Linux IDE guy

--------------116121F7C3FAF555575A74D
Content-Type: text/plain; charset=us-ascii; name="d"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="d"

diff -u --recursive --new-file --exclude=.* linux-2.0.10/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.0.10/Documentation/Configure.help Mon Jul 22 20:37:50 1996
+++ linux/Documentation/Configure.help Thu Aug 1 22:14:20 1996
@@ -202,16 +202,22 @@
"SiS" chipset. Unfortunately, it has a number of rather nasty
design flaws that can cause severe data corruption under many common
conditions. Say Y here to include code which tries to automatically
- detect and correct the problems under Linux. This also provides
- support for the enhanced features of the CMD640, for improved
- support/operation under linux, including access to the secondary IDE
- ports in some systems. This driver will work automatically in PCI
+ detect and correct the problems under Linux.
+ This option also enables access to the secondary IDE ports in some
+ CMD640 based systems. This driver will work automatically in PCI
based systems (most new systems have PCI slots). But if your system
uses VESA local bus (VLB) instead of PCI, you must also supply a
kernel boot parameter to enable the CMD640 bugfix/support:
"ide0=cmd640_vlb" The CMD640 chip is also used on add-in cards by
Acculogic, and on the "CSA-6400E PCI to IDE controller" that some
people have. If unsure, say Y.
+
+CMD640 enhanced support
+CONFIG_BLK_DEV_CMD640_ENHANCED
+ This option includes support for setting/autotuning PIO modes
+ and prefetch on CMD640 IDE interfaces. If you have a CMD640 IDE
+ interface and your BIOS does not already do this for you, then say Y
+ here. Otherwise say N.

RZ1000 chipset bugfix/support
CONFIG_BLK_DEV_RZ1000
diff -u --recursive --new-file --exclude=.* linux-2.0.10/Documentation/ide.txt linux/Documentation/ide.txt
--- linux-2.0.10/Documentation/ide.txt Fri Jul 26 17:48:13 1996
+++ linux/Documentation/ide.txt Fri Aug 2 09:49:07 1996
@@ -1,4 +1,4 @@
-ide.txt -- Information regarding the Enhanced IDE drive in Linux 2.0.x
+ide.txt -- Information regarding the Enhanced IDE drive in Linux 2.0.xx
===============================================================================
Supported by:
Mark Lord <mlord@pobox.com> -- disks, interfaces, probing
@@ -37,8 +37,8 @@
NEW! - support for reliable operation of buggy CMD-640 interfaces
- PCI support is automatic when cmd640 support is configured
- for VLB, use kernel command line option: ide0=cmd640_vlb
- - this support also enables the secondary i/f on most cards
- - experimental interface timing parameter support
+ - this support also enables the secondary i/f when needed
+ - interface PIO timing & prefetch parameter support
NEW! - experimental support for UMC 8672 interfaces
NEW! - support for secondary interface on the FGI/Holtek HT-6560B VLB i/f
- use kernel command line option: ide0=ht6560
@@ -62,7 +62,7 @@
and direct reads of audio data.
NEW! - experimental support for Promise DC4030VL caching interface card
NEW! - email thanks/problems to: peterd@pnd-pc.demon.co.uk
-NEW! - the hdparm-2.7 package can be used to set PIO modes for some chipsets.
+NEW! - the hdparm-3.1 package can be used to set PIO modes for some chipsets.

For work in progress, see the comments in ide.c, ide-cd.c, and triton.c.

@@ -71,17 +71,28 @@
Look for this support to be added to the kernel soon.


-*** IMPORTANT NOTICES (for kernel versions after 1.3.21)
+*** IMPORTANT NOTICES: BUGGY IDE CHIPSETS CAN CORRUPT DATA!!
*** =================
*** PCI versions of the CMD640 and RZ1000 interfaces are now detected
*** automatically at startup when PCI BIOS support is configured.
-*** Linux disables the "pre-fetch" or "read-ahead" modes of these interfaces
+***
+*** Linux disables the "prefetch" ("readahead") mode of the RZ1000
*** to prevent data corruption possible due to hardware design flaws.
-*** Use of the "serialize" option is no longer necessary.
+***
+*** For the CMD640, linux disables "IRQ unmasking" (hdparm -u1) on any
+*** drive for which the "prefetch" mode of the CMD640 is turned on.
+*** If "prefetch" is disabled (hdparm -p8), then "IRQ unmasking" can be
+*** used again.
+***
+*** For the CMD640, linux disabled "32bit I/O" (hdparm -c1) on any drive
+*** for which the "prefetch" mode of the CMD640 is turned off.
+*** If "prefetch" is enabled (hdparm -p9), then "32bit I/O" can be
+*** used again.
***
*** The CMD640 is also used on some Vesa Local Bus (VLB) cards, and is *NOT*
*** automatically detected by Linux. For safe, reliable operation with such
*** interfaces, one *MUST* use the "ide0=cmd640_vlb" kernel option.
+***
*** Use of the "serialize" option is no longer necessary.

This is the multiple IDE interface driver, as evolved from hd.c.
@@ -107,7 +118,7 @@
Interfaces beyond the first two are not normally probed for, but may be
specified using kernel "command line" options. For example,

- ide3=0x168,0x36e,10 /* ioports 0x168-0x16f,0x36e, irq 11 */
+ ide3=0x168,0x36e,10 /* ioports 0x168-0x16f,0x36e, irq 10 */

Normally the irq number need not be specified, as ide.c will probe for it:

@@ -244,6 +255,15 @@
Not fully supported by all chipset types,
and quite likely to cause trouble with
older/odd IDE drives.
+
+ "idebus=xx" : inform IDE driver of VESA/PCI bus speed in Mhz,
+ where "xx" is between 25 and 66 inclusive,
+ used when tuning chipset PIO modes.
+ For PCI bus, 25 is correct for a P75 system,
+ 30 is correct for P90,P120,P180 systems,
+ and 33 is used for P100,P133,P166 systems.
+ If in doubt, use idebus=33 for PCI.
+ As for VLB, it is safest to not specify it.

"idex=noprobe" : do not attempt to access/use this interface
"idex=base" : probe for an interface at the addr specified,
diff -u --recursive --new-file --exclude=.* linux-2.0.10/arch/i386/defconfig linux/arch/i386/defconfig
--- linux-2.0.10/arch/i386/defconfig Wed Jun 5 07:51:03 1996
+++ linux/arch/i386/defconfig Thu Aug 1 21:55:00 1996
@@ -44,6 +44,7 @@
# CONFIG_BLK_DEV_IDETAPE is not set
# CONFIG_BLK_DEV_IDE_PCMCIA is not set
CONFIG_BLK_DEV_CMD640=y
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
# CONFIG_BLK_DEV_TRITON is not set
CONFIG_BLK_DEV_RZ1000=y
# CONFIG_IDE_CHIPSETS is not set
diff -u --recursive --new-file --exclude=.* linux-2.0.10/drivers/block/Config.in linux/drivers/block/Config.in
--- linux-2.0.10/drivers/block/Config.in Fri Jul 26 17:48:14 1996
+++ linux/drivers/block/Config.in Thu Aug 1 21:58:33 1996
@@ -15,6 +15,9 @@
bool ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE
bool ' Support removable IDE interfaces (PCMCIA)' CONFIG_BLK_DEV_IDE_PCMCIA
bool ' CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640
+ if [ "$CONFIG_BLK_DEV_CMD640" = "y" ]; then
+ bool ' CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED
+ fi
if [ "$CONFIG_PCI" = "y" ]; then
bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000
bool ' Intel 82371 PIIX (Triton I/II) DMA support' CONFIG_BLK_DEV_TRITON
diff -u --recursive --new-file --exclude=.* linux-2.0.10/drivers/block/ali14xx.c linux/drivers/block/ali14xx.c
--- linux-2.0.10/drivers/block/ali14xx.c Fri Jul 26 17:48:14 1996
+++ linux/drivers/block/ali14xx.c Thu Aug 1 19:48:14 1996
@@ -51,20 +51,6 @@
#include "ide.h"
#include "ide_modes.h"

-/*
- * This should be set to the system's local bus (PCI or VLB) speed,
- * e.g., 33 for a 486DX33 or 486DX2/66. Legal values are anything
- * from 25 to 50. Setting this too *low* will make the EIDE
- * controller unable to communicate with the disks.
- *
- * The value is 50 by default -- this should work ok with any system.
- * (Low values cause problems because it multiplies by bus speed
- * to get cycles, and thus gets a too-small cycle count and tries to
- * access the disks too fast. I tried this once under DOS and it locked
- * up the system.) -- derekn@vw.ece.cmu.edu
- */
-#define ALI_14xx_BUS_SPEED 50 /* PCI / VLB bus speed */
-
/* port addresses for auto-detection */
#define ALI_NUM_PORTS 4
static int ports[ALI_NUM_PORTS] = {0x074, 0x0f4, 0x034, 0x0e4};
@@ -82,15 +68,7 @@
{0x35, 0x03}, {0x00, 0x00}
};

-/* default timing parameters for each PIO mode */
#define ALI_MAX_PIO 4
-static struct { int time1, time2; } timeTab[ALI_MAX_PIO+1] = {
- {600, 165}, /* PIO 0 */
- {383, 125}, /* PIO 1 */
- {240, 100}, /* PIO 2 */
- {180, 80}, /* PIO 3 */
- {120, 70} /* PIO 4 */
-};

/* timing parameter registers for each drive */
static struct { byte reg1, reg2, reg3, reg4; } regTab[4] = {
@@ -134,26 +112,18 @@
static void ali14xx_tune_drive (ide_drive_t *drive, byte pio)
{
int driveNum;
- int time1, time2, time1a;
+ int time1, time2;
byte param1, param2, param3, param4;
- struct hd_driveid *id = drive->id;
unsigned long flags;
+ ide_pio_data_t d;

- if (pio == 255)
- pio = ide_get_best_pio_mode(drive);
- if (pio > ALI_MAX_PIO)
- pio = ALI_MAX_PIO;
+ pio = ide_get_best_pio_mode(drive, pio, ALI_MAX_PIO, &d);

/* calculate timing, according to PIO mode */
- time1 = timeTab[pio].time1;
- time2 = timeTab[pio].time2;
- if (pio >= 3) {
- time1a = (id->capability & 0x08) ? id->eide_pio_iordy : id->eide_pio;
- if (time1a != 0 && time1a < time1)
- time1 = time1a;
- }
- param3 = param1 = (time2 * ALI_14xx_BUS_SPEED + 999) / 1000;
- param4 = param2 = (time1 * ALI_14xx_BUS_SPEED + 999) / 1000 - param1;
+ time1 = d.cycle_time;
+ time2 = ide_pio_timings[pio].active_time;
+ param3 = param1 = (time2 * ide_system_bus_speed + 999) / 1000;
+ param4 = param2 = (time1 * ide_system_bus_speed + 999) / 1000 - param1;
if (pio < 3) {
param3 += 8;
param4 += 8;
@@ -246,4 +216,6 @@
printk("ali14xx: Chip initialization failed\n");
return;
}
+ printk("ali14xx: Assuming %dMhz system bus speed for PIO modes; override with idebus=xx\n",
+ ide_system_bus_speed);
}
diff -u --recursive --new-file --exclude=.* linux-2.0.10/drivers/block/cmd640.c linux/drivers/block/cmd640.c
--- linux-2.0.10/drivers/block/cmd640.c Sat Jul 27 11:36:40 1996
+++ linux/drivers/block/cmd640.c Fri Aug 2 10:45:07 1996
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/cmd640.c Version 0.13 Jul 23, 1996
+ * linux/drivers/block/cmd640.c Version 0.96 Aug 02, 1996
*
* Copyright (C) 1995-1996 Linus Torvalds & authors (see below)
*/
@@ -7,8 +7,8 @@
/*
* Original author: abramov@cecmow.enet.dec.com (Igor Abramov)
*
- * Maintained by: s0033las@sun10.vsz.bme.hu (Laszlo Peter)
- * mlord@pobox.com (Mark Lord)
+ * Maintained by: mlord@pobox.com (Mark Lord)
+ * with fanatic support from a legion of hackers!
*
* This file provides support for the advanced features and bugs
* of IDE interfaces using the CMD Technologies 0640 IDE interface chip.
@@ -17,32 +17,44 @@
* 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.93 worked for just about everybody.
+ * Version 0.96 should still work, but is quite a bit different.
*
+ * User feedback is essential. Many thanks to the beta test team:
+ *
+ * A.Hartgers@stud.tue.nl, JZDQC@CUNYVM.CUNY.edu, abramov@cecmow.enet.dec.com,
+ * bardj@utopia.ppp.sn.no, bart@gaga.tue.nl, bbol001@cs.auckland.ac.nz,
+ * chrisc@dbass.demon.co.uk, dalecki@namu26.Num.Math.Uni-Goettingen.de,
+ * derekn@vw.ece.cmu.edu, florian@btp2x3.phy.uni-bayreuth.de,
+ * flynn@dei.unipd.it, gadio@netvision.net.il, godzilla@futuris.net,
+ * j@pobox.com, jkemp1@mises.uni-paderborn.de, jtoppe@hiwaay.net,
+ * kerouac@ssnet.com, meskes@informatik.rwth-aachen.de,
+ * peter@udgaard.isgtec.com, phil@tazenda.demon.co.uk, roadcapw@cfw.com,
+ * s0033las@sun10.vsz.bme.hu, schaffer@tam.cornell.edu, sjd@slip.net,
+ * steve@ei.org, ulrpeg@bigcomm.gun.de
*
* 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.
*
- * Version 0.02 Fixes for vlb initialization code, enable
- * read-ahead for versions 'B' and 'C' of chip by
- * default, some code cleanup.
+ * Version 0.02 Fixes for vlb initialization code, enable prefetch
+ * for versions 'B' and 'C' of chip by default,
+ * some code cleanup.
*
* Version 0.03 Added reset of secondary interface,
* and black list for devices which are not compatible
- * with read ahead mode. Separate function for setting
- * readahead is added, possibly it will be called some
+ * with prefetch mode. Separate function for setting
+ * prefetch 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
*
* Version 0.05 Major rewrite of interface timing code.
* Added new function cmd640_set_mode to set PIO mode
* from ioctl call. New drives added to black list.
*
- * Version 0.06 More code cleanup. Readahead is enabled only for
- * detected hard drives, not included in readahead
+ * Version 0.06 More code cleanup. Prefetch is enabled only for
+ * detected hard drives, not included in prefetch
* black list.
*
* Version 0.07 Changed to more conservative drive tuning policy.
@@ -51,17 +63,30 @@
* List of known drives extended by info provided by
* CMD at their ftp site.
*
- * Version 0.08 Added autotune/noautotune support. -ml
+ * Version 0.08 Added autotune/noautotune support.
*
- * 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.09 Try to be smarter about 2nd port enabling.
+ * Version 0.10 Be nice and don't reset 2nd port.
+ * Version 0.11 Try to handle more wierd situations.
*
* 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
- * Version 0.13 permit irq unmasking again. -ml
+ * irq unmasking disabled for reliability.
+ * try to be even smarter about the second port.
+ * tidy up source code formatting.
+ * Version 0.13 permit irq unmasking again.
+ * Version 0.90 massive code cleanup, some bugs fixed.
+ * defaults all drives to PIO mode0, prefetch off.
+ * autotune is OFF by default, with compile time flag.
+ * prefetch can be turned OFF/ON using "hdparm -p8/-p9"
+ * (requires hdparm-3.1 or newer)
+ * Version 0.91 first release to linux-kernel list.
+ * Version 0.92 move initial reg dump to separate callable function
+ * change "readahead" to "prefetch" to avoid confusion
+ * Version 0.95 respect original BIOS timings unless autotuning.
+ * tons of code cleanup and rearrangement.
+ * added CONFIG_BLK_DEV_CMD640_ENHANCED option
+ * prevent use of unmask when prefetch is on
+ * Version 0.96 prevent use of io_32bit when prefetch is off
*/

#undef REALLY_SLOW_IO /* most systems can safely undef this */
@@ -78,8 +103,9 @@
#include "ide.h"
#include "ide_modes.h"

-#define PARANOID_ABOUT_CMD640 1 /* used to tag superstitious code */
-
+/*
+ * This flag is set in ide.c by the parameter: ide0=cmd640_vlb
+ */
int cmd640_vlb = 0;

/*
@@ -120,70 +146,97 @@
#define ARTTIM1 0x55
#define DRWTIM1 0x56
#define ARTTIM23 0x57
-#define DIS_RA2 0x04
-#define DIS_RA3 0x08
+#define ARTTIM23_DIS_RA2 0x04
+#define ARTTIM23_DIS_RA3 0x08
#define DRWTIM23 0x58
#define BRST 0x59

-static ide_tuneproc_t cmd640_tune_drive;
+/*
+ * Registers and masks for easy access by drive index:
+ */
+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};
+
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+
+static byte arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23};
+static byte drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM23, DRWTIM23};
+
+/*
+ * Current cmd640 timing values for each drive.
+ * The defaults for each are the slowest possible timings.
+ */
+static byte setup_counts[4] = {4, 4, 4, 4}; /* Address setup count (in clocks) */
+static byte active_counts[4] = {16, 16, 16, 16}; /* Active count (encoded) */
+static byte recovery_counts[4] = {16, 16, 16, 16}; /* Recovery count (encoded) */
+
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
+/*
+ * These are initialized to point at the devices we control
+ */
+static ide_hwif_t *cmd_hwif0, *cmd_hwif1;
+static ide_drive_t *cmd_drives[4];
+
+/*
+ * Interface to access cmd640x registers
+ */
+static unsigned int cmd640_key;
+static void (*put_cmd640_reg)(unsigned short reg, byte val);
+static byte (*get_cmd640_reg)(unsigned short reg);

-/* Interface to access cmd640x registers */
-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;
-static int cmd640_chip_version;
-static int cmd640_key;
-static int bus_speed; /* MHz */
+/*
+ * This is read from the CFR reg, and is used in several places.
+ */
+static unsigned int cmd640_chip_version;

/*
* 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.
+ * Therefore, we must use direct IO instead.
*/

/* PCI method 1 access */

-static void put_cmd640_reg_pci1(int reg_no, int val)
+static void put_cmd640_reg_pci1 (unsigned short reg, byte val)
{
unsigned long flags;

save_flags(flags);
cli();
- outl_p((reg_no & 0xfc) | cmd640_key, 0xcf8);
- outb_p(val, (reg_no & 3) + 0xcfc);
+ outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
+ outb_p(val, (reg & 3) | 0xcfc);
restore_flags(flags);
}

-static byte get_cmd640_reg_pci1(int reg_no)
+static byte get_cmd640_reg_pci1 (unsigned short reg)
{
byte b;
unsigned long flags;

save_flags(flags);
cli();
- outl_p((reg_no & 0xfc) | cmd640_key, 0xcf8);
- b = inb_p(0xcfc + (reg_no & 3));
+ outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
+ b = inb_p((reg & 3) | 0xcfc);
restore_flags(flags);
return b;
}

/* PCI method 2 access (from CMD datasheet) */

-static void put_cmd640_reg_pci2(int reg_no, int val)
+static void put_cmd640_reg_pci2 (unsigned short reg, byte val)
{
unsigned long flags;

save_flags(flags);
cli();
outb_p(0x10, 0xcf8);
- outb_p(val, cmd640_key + reg_no);
+ outb_p(val, cmd640_key + reg);
outb_p(0, 0xcf8);
restore_flags(flags);
}

-static byte get_cmd640_reg_pci2(int reg_no)
+static byte get_cmd640_reg_pci2 (unsigned short reg)
{
byte b;
unsigned long flags;
@@ -191,7 +244,7 @@
save_flags(flags);
cli();
outb_p(0x10, 0xcf8);
- b = inb_p(cmd640_key + reg_no);
+ b = inb_p(cmd640_key + reg);
outb_p(0, 0xcf8);
restore_flags(flags);
return b;
@@ -199,48 +252,51 @@

/* VLB access */

-static void put_cmd640_reg_vlb(int reg_no, int val)
+static void put_cmd640_reg_vlb (unsigned short reg, byte val)
{
unsigned long flags;

save_flags(flags);
cli();
- outb_p(reg_no, cmd640_key + 8);
- outb_p(val, cmd640_key + 0xc);
+ outb_p(reg, cmd640_key);
+ outb_p(val, cmd640_key + 4);
restore_flags(flags);
}

-static byte get_cmd640_reg_vlb(int reg_no)
+static byte get_cmd640_reg_vlb (unsigned short reg)
{
byte b;
unsigned long flags;

save_flags(flags);
cli();
- outb_p(reg_no, cmd640_key + 8);
- b = inb_p(cmd640_key + 0xc);
+ outb_p(reg, cmd640_key);
+ b = inb_p(cmd640_key + 4);
restore_flags(flags);
return b;
}

+static int match_pci_cmd640_device (void)
+{
+ const byte ven_dev[4] = {0x95, 0x10, 0x40, 0x06};
+ unsigned int i;
+ for (i = 0; i < 4; i++) {
+ if (get_cmd640_reg(i) != ven_dev[i])
+ return 0;
+ }
+ return 1; /* success */
+}
+
/*
* Probe for CMD640x -- pci method 1
*/
-
-static int probe_for_cmd640_pci1(void)
+static int probe_for_cmd640_pci1 (void)
{
- long id;
- int k;
-
- for (k = 0x80000000; k <= 0x8000f800; k += 0x800) {
- outl(k, 0xcf8);
- id = inl(0xcfc);
- if (id != 0x06401095)
- continue;
- put_cmd640_reg = put_cmd640_reg_pci1;
- get_cmd640_reg = get_cmd640_reg_pci1;
- cmd640_key = k;
- return 1;
+ get_cmd640_reg = get_cmd640_reg_pci1;
+ put_cmd640_reg = put_cmd640_reg_pci1;
+ for (cmd640_key = 0x80000000; cmd640_key <= 0x8000f800; cmd640_key += 0x800) {
+ if (match_pci_cmd640_device())
+ return 1; /* success */
}
return 0;
}
@@ -248,24 +304,13 @@
/*
* Probe for CMD640x -- pci method 2
*/
-
-static int probe_for_cmd640_pci2(void)
+static int probe_for_cmd640_pci2 (void)
{
- int i;
- int v_id;
- int d_id;
-
- for (i = 0xc000; i <= 0xcf00; i += 0x100) {
- outb(0x10, 0xcf8);
- v_id = inw(i);
- d_id = inw(i + 2);
- outb(0, 0xcf8);
- if (v_id != 0x1095 || d_id != 0x640)
- continue;
- put_cmd640_reg = put_cmd640_reg_pci2;
- get_cmd640_reg = get_cmd640_reg_pci2;
- cmd640_key = i;
- return 1;
+ get_cmd640_reg = get_cmd640_reg_pci2;
+ put_cmd640_reg = put_cmd640_reg_pci2;
+ for (cmd640_key = 0xc000; cmd640_key <= 0xcf00; cmd640_key += 0x100) {
+ if (match_pci_cmd640_device())
+ return 1; /* success */
}
return 0;
}
@@ -273,567 +318,479 @@
/*
* Probe for CMD640x -- vlb
*/
-
-static int probe_for_cmd640_vlb(void) {
+static int probe_for_cmd640_vlb (void)
+{
byte b;

- outb(CFR, 0x178);
- b = inb(0x17c);
- if (b == 0xff || b == 0 || (b & CFR_AT_VESA_078h)) {
- outb(CFR, 0x78);
- b = inb(0x7c);
- if (b == 0xff || b == 0 || !(b & CFR_AT_VESA_078h))
+ get_cmd640_reg = get_cmd640_reg_vlb;
+ put_cmd640_reg = put_cmd640_reg_vlb;
+ cmd640_key = 0x178;
+ b = get_cmd640_reg(CFR);
+ if (b == 0xff || b == 0x00 || (b & CFR_AT_VESA_078h)) {
+ cmd640_key = 0x78;
+ b = get_cmd640_reg(CFR);
+ if (b == 0xff || b == 0x00 || !(b & CFR_AT_VESA_078h))
return 0;
- cmd640_key = 0x70;
- } else {
- cmd640_key = 0x170;
}
- put_cmd640_reg = put_cmd640_reg_vlb;
- get_cmd640_reg = get_cmd640_reg_vlb;
- return 1;
+ return 1; /* success */
}

-#if 0
-/*
- * Low level reset for controller, actually it has nothing specific for
- * CMD640, but I don't know how to use standard reset routine before
- * we recognized any drives.
- */
-static void cmd640_reset_controller(int iface_no)
-{
- int retry_count = 600;
- int base_port = iface_no ? 0x170 : 0x1f0;
-
- outb_p(4, base_port + 7);
- udelay(5);
- outb_p(0, base_port + 7);
-
- do {
- udelay(5);
- retry_count -= 1;
- } while ((inb_p(base_port + 7) & 0x80) && retry_count);
-
- if (retry_count == 0)
- printk("cmd640: failed to reset controller %d\n", iface_no);
-}
-#endif /* 0 */
-
/*
* Returns 1 if an IDE interface/drive exists at 0x170,
* Returns 0 otherwise.
*/
static int secondary_port_responding (void)
{
- /*
- * Test for hardware at 0x170 (secondary IDE port).
- */
- outb_p(0xa0, 0x170 + IDE_SELECT_OFFSET); /* select drive0 */
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ outb_p(0x0a, 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 */
+ if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x0a) {
+ outb_p(0x1a, 0x170 + IDE_SELECT_OFFSET); /* select drive1 */
udelay(100);
- if (inb_p(0x170 + IDE_SELECT_OFFSET) != 0xb0)
- return 0; /* nothing is there */
+ if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x1a) {
+ restore_flags(flags);
+ return 0; /* nothing responded */
+ }
}
- return 1; /* something is there */
+ restore_flags(flags);
+ return 1; /* success */
}

+#ifdef CMD640_DUMP_REGS
/*
- * Probe for Cmd640x and initialize it if found
+ * Dump out entire cmd640 register space. May be called from ide.c
*/
-
-int ide_probe_for_cmd640x(void)
+void cmd640_dump_regs (void)
{
- int second_port_toggled = 0;
- int second_port_cmd640 = 0;
- byte b;
-
- if (probe_for_cmd640_pci1()) {
- bus_type = pci1;
- } else if (probe_for_cmd640_pci2()) {
- bus_type = pci2;
- } else if (cmd640_vlb && probe_for_cmd640_vlb()) {
- /* May be remove cmd640_vlb at all, and probe in any case */
- bus_type = vlb;
- } else {
- 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
+ unsigned int reg;

-#if 0
- /* Dump initial state of chip registers */
- for (b = 0; b != 0xff; b++) {
- printk(" %2x%c", get_cmd640_reg(b),
- ((b&0xf) == 0xf) ? '\n' : ',');
+ /* Dump current state of chip registers */
+ printk("ide: cmd640 internal register dump:");
+ for (reg = 0; reg < 0x100; reg++) {
+ if (!(reg & 0x0f))
+ printk("\n%04x:", reg);
+ printk(" %02x", get_cmd640_reg(reg));
}
-#endif
-
- /*
- * Undocumented magic. (There is no 0x5b port in specs)
- */
-
- put_cmd640_reg(0x5b, 0xbd);
- if (get_cmd640_reg(0x5b) != 0xbd) {
- printk("ide: can't initialize cmd640 -- wrong value in 0x5b\n");
- return 0;
- }
- put_cmd640_reg(0x5b, 0);
-
- /*
- * Documented magic.
- */
-
- cmd640_chip_version = get_cmd640_reg(CFR) & CFR_DEVREV;
- if (cmd640_chip_version == 0) {
- printk ("ide: wrong CMD640 version -- 0\n");
- return 0;
- }
-
- /*
- * 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); /* 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;
-
- /*
- * Setup Control Register
- */
- 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)) {
-
- 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,
- * and that the second interface, if enabled, is at 0x170.
- */
- ide_hwifs[0].chipset = ide_cmd640;
- ide_hwifs[0].tuneproc = &cmd640_tune_drive;
-#if 0
- ide_hwifs[0].no_unmask = 1;
-#endif
-
- if (ide_hwifs[0].drives[0].autotune == 0)
- ide_hwifs[0].drives[0].autotune = 1;
- if (ide_hwifs[0].drives[1].autotune == 0)
- ide_hwifs[0].drives[1].autotune = 1;
-
- /*
- * Initialize 2nd IDE port, if required
- */
- 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;
-#if 0
- ide_hwifs[1].no_unmask = 1;
-#endif
- 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;
-
-#if 0
- /* reset the secondary interface */
- cmd640_reset_controller(1);
-#endif
- }
-
- printk("ide: buggy CMD640%c interface on ",
- 'A' - 1 + cmd640_chip_version);
- switch (bus_type) {
- case vlb :
- printk("vlb (0x%x)", cmd640_key);
- break;
- case pci1:
- printk("pci (0x%x)", cmd640_key);
- break;
- case pci2:
- printk("pci (access method 2) (0x%x)", cmd640_key);
- break;
- }
-
- /*
- * Tell everyone what we did to their system
- */
- printk(":%s serialized, second port %s\n",
- second_port_cmd640 ? "" : " not",
- second_port_toggled ? "toggled" : "untouched");
- return 1;
+ printk("\n");
}
-
-#if 0 /* not used anywhere */
-int cmd640_off(void) {
- static int a = 0;
- byte b;
-
- if (bus_type == none || a == 1)
- return 0;
- a = 1;
- b = get_cmd640_reg(CNTRL);
- b &= ~CNTRL_ENA_2ND;
- put_cmd640_reg(CNTRL, b);
- return 1;
-}
-#endif /* 0 */
+#endif

/*
- * Sets readahead mode for specific drive
- * in the future it could be called from ioctl
+ * Check whether prefetch is on for a drive,
+ * and initialize the unmask flags for safe operation.
*/
-
-static void set_readahead_mode(ide_drive_t* drive, int mode)
+static void check_prefetch (unsigned int index)
{
- static int masks[2][2] =
- {
- {CNTRL_DIS_RA0, CNTRL_DIS_RA1},
- {DIS_RA2, DIS_RA3}
- };
- 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;
+ ide_drive_t *drive = cmd_drives[index];
+ byte b = get_cmd640_reg(prefetch_regs[index]);

- if (mode)
- b &= ~mask; /* Enable readahead for specific drive */
- else
- b |= mask; /* Disable readahead for specific drive */
-
- put_cmd640_reg(port, b);
-}
-
-static const struct readahead_black_list {
- const char* name;
- int mode;
-} drives_ra[] = {
- { "QUANTUM LIGHTNING 540A", 0 },
- { "ST3655A", 0 },
- { "SAMSUNG", 0 }, /* Be conservative */
- { NULL, 0 }
-};
-
-static int strmatch(const char* pattern, const char* name) {
- char c1, c2;
-
- while (1) {
- c1 = *pattern++;
- c2 = *name++;
- if (c1 == 0) {
- return 0;
- }
- if (c1 != c2)
- return 1;
+ if (b & prefetch_masks[index]) {
+ drive->no_unmask = 0;
+ drive->io_32bit = 0;
+ } else {
+ drive->no_unmask = 1;
+ drive->unmask = 0;
}
}

-static int known_drive_readahead(char* name) {
- int i;
+/*
+ * Figure out which devices we control
+ */
+static void setup_device_ptrs (void)
+{
+ unsigned int i;

- for (i = 0; drives_ra[i].name != NULL; i++) {
- if (strmatch(drives_ra[i].name, name) == 0) {
- return drives_ra[i].mode;
+ cmd_hwif0 = &ide_hwifs[0]; /* default, if not found below */
+ cmd_hwif1 = &ide_hwifs[1]; /* default, if not found below */
+ for (i = 0; i < MAX_HWIFS; i++) {
+ ide_hwif_t *hwif = &ide_hwifs[i];
+ if (hwif->chipset == ide_unknown || hwif->chipset == ide_generic) {
+ if (hwif->io_base == 0x1f0)
+ cmd_hwif0 = hwif;
+ else if (hwif->io_base == 0x170)
+ cmd_hwif1 = hwif;
}
}
- return -1;
+ cmd_drives[0] = &cmd_hwif0->drives[0];
+ cmd_drives[1] = &cmd_hwif0->drives[1];
+ cmd_drives[2] = &cmd_hwif1->drives[0];
+ cmd_drives[3] = &cmd_hwif1->drives[1];
}

-static int arttim[4] = {2, 2, 2, 2}; /* Address setup count (in clocks) */
-static int a_count[4] = {1, 1, 1, 1}; /* Active count (encoded) */
-static int r_count[4] = {1, 1, 1, 1}; /* Recovery count (encoded) */
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED

/*
- * Convert address setup count from number of clocks
- * to representation used by controller
+ * Sets prefetch mode for a drive.
*/
-
-inline static int pack_arttim(int clocks)
+static void set_prefetch_mode (unsigned int index, int mode)
{
- if (clocks <= 2) return 0x40;
- else if (clocks == 3) return 0x80;
- else if (clocks == 4) return 0x00;
- else return 0xc0;
+ ide_drive_t *drive = cmd_drives[index];
+ int reg = prefetch_regs[index];
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ b = get_cmd640_reg(reg);
+ if ((drive->no_unmask = mode)) {
+ drive->unmask = 0;
+ b &= ~prefetch_masks[index]; /* enable prefetch */
+ } else {
+ drive->io_32bit = 0;
+ b |= prefetch_masks[index]; /* disable prefetch */
+ }
+ put_cmd640_reg(reg, b);
+ restore_flags(flags);
}

/*
* Pack active and recovery counts into single byte representation
* used by controller
*/
-
-inline static int pack_counts(int act_count, int rec_count)
+inline static byte pack_nibbles (byte upper, byte lower)
{
- return ((act_count & 0x0f)<<4) | (rec_count & 0x0f);
+ return ((upper & 0x0f) << 4) | (lower & 0x0f);
}

-inline int max(int a, int b) { return a > b ? a : b; }
-inline int max4(int *p) { return max(p[0], max(p[1], max(p[2], p[3]))); }
-
/*
- * Set timing parameters
+ * This routine retrieves the initial drive timings from the chipset.
*/
-
-static void cmd640_set_timing(int if_num, int dr_num)
+static void retrieve_drive_counts (unsigned int index)
{
- int b_reg;
- int ac, rc, at;
byte b;

/*
- * Set address setup count and drive read/write timing registers.
- * Primary interface has individual count/timing registers for
- * each drive. Secondary interface has common set of registers, and
- * we should set timings for the slowest drive.
+ * Get the internal setup timing, and convert to clock count
*/
-
- if (if_num == 0) {
- b_reg = dr_num ? ARTTIM1 : ARTTIM0;
- 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));
+ b = get_cmd640_reg(arttim_regs[index]) & ~0x3f;
+ switch (b) {
+ case 0x00: b = 4; break;
+ case 0x80: b = 3; break;
+ case 0x40: b = 2; break;
+ default: b = 5; break;
}
-
- put_cmd640_reg(b_reg, b);
- put_cmd640_reg(b_reg + 1, pack_counts(ac, rc));
+ setup_counts[index] = b;

/*
- * Update CMDTIM (IDE Command Block Timing Register)
+ * Get the active/recovery counts
*/
-
- ac = max4(a_count);
- rc = max4(r_count);
- put_cmd640_reg(CMDTIM, pack_counts(ac, rc));
+ b = get_cmd640_reg(drwtim_regs[index]);
+ active_counts[index] = (b >> 4) ? (b >> 4) : 0x10;
+ recovery_counts[index] = (b & 0x0f) ? (b & 0x0f) : 0x10;
}

+
/*
- * Standard timings for PIO modes
+ * This routine writes the prepared setup/active/recovery counts
+ * for a drive into the cmd640 chipset registers to active them.
*/
-
-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 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,
- int clock_time, int drv_idx)
+static void program_drive_counts (unsigned int index)
{
- int a, b;
+ unsigned long flags;
+ byte setup_count = setup_counts[index];
+ byte active_count = active_counts[index];
+ byte recovery_count = recovery_counts[index];

- arttim[drv_idx] = (mc_time + clock_time - 1)/clock_time;
+ /*
+ * Set up address setup count and drive read/write timing registers.
+ * Primary interface has individual count/timing registers for
+ * each drive. Secondary interface has one common set of registers,
+ * so we merge the timings, using the slowest value for each timing.
+ */
+ if (index > 1) {
+ unsigned int mate;
+ if (cmd_drives[mate = index ^ 1]->present) {
+ if (setup_count < setup_counts[mate])
+ setup_count = setup_counts[mate];
+ if (active_count < active_counts[mate])
+ active_count = active_counts[mate];
+ if (recovery_count < recovery_counts[mate])
+ recovery_count = recovery_counts[mate];
+ }
+ }

- a = (av_time + clock_time - 1)/clock_time;
- if (a < 2)
- a = 2;
- b = (ds_time + clock_time - 1)/clock_time - a;
- if (b < 2)
- b = 2;
- if (b > 0x11) {
- a += b - 0x11;
- b = 0x11;
+ /*
+ * Convert setup_count to internal chipset representation
+ */
+ switch (setup_count) {
+ case 4: setup_count = 0x00;
+ case 3: setup_count = 0x80;
+ case 2: setup_count = 0x40;
+ default: setup_count = 0xc0; /* case 5 */
}
- if (a > 0x10)
- a = 0x10;
- if (cmd640_chip_version > 1)
- b -= 1;
- if (b > 0x10)
- b = 0x10;

- a_count[drv_idx] = a;
- r_count[drv_idx] = b;
+ /*
+ * 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
+ * (this converts counts of 16 into counts of zero -- okay).
+ */
+ setup_count |= get_cmd640_reg(arttim_regs[index]) & 0x3f;
+ put_cmd640_reg(arttim_regs[index], setup_count);
+ put_cmd640_reg(drwtim_regs[index], pack_nibbles(active_count, recovery_count));
+ restore_flags(flags);
}

-static void set_pio_mode(int if_num, int drv_num, int mode_num) {
- int p_base;
- int i;
+/*
+ * Set a specific pio_mode for a drive
+ */
+static void cmd640_set_mode (unsigned int index, byte pio_mode, unsigned int cycle_time)
+{
+ int setup_time, active_time, recovery_time, clock_time;
+ byte setup_count, active_count, recovery_count, recovery_count2, cycle_count;

- p_base = if_num ? 0x170 : 0x1f0;
+ 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 / ide_system_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;
+ if (active_count < 2)
+ active_count = 2; /* minimum allowed by cmd640 */
+
+ 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 < 2)
+ recovery_count = 2; /* minimum allowed by cmd640 */
+ if (recovery_count > 17) {
+ active_count += recovery_count - 17;
+ recovery_count = 17;
+ }
+ if (active_count > 16)
+ active_count = 16; /* maximum allowed by cmd640 */
+ if (cmd640_chip_version > 1)
+ recovery_count -= 1; /* cmd640b uses (count + 1)*/
+ if (recovery_count > 16)
+ recovery_count = 16; /* maximum allowed by cmd640 */

- 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);
+ setup_counts[index] = setup_count;
+ active_counts[index] = active_count;
+ recovery_counts[index] = recovery_count;

- for (i = 0; (i < 100) && (inb(p_base + IDE_STATUS_OFFSET) & BUSY_STAT); i++)
- udelay(10000);
+ /*
+ * 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 (index);
}

/*
- * Set a specific pio_mode for a drive
+ * Drive PIO mode selection:
*/
+static void cmd640_tune_drive (ide_drive_t *drive, byte mode_wanted)
+{
+ ide_pio_data_t d;
+ unsigned int index = 0;

-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;
-
- if (pio_mode > 5) return;
-
- interface_number = HWIF(drive)->index;
- drive_number = drive->select.b.unit;
- clock_time = 1000/bus_speed;
+ while (drive != cmd_drives[index]) {
+ if (++index > 3) {
+ printk("%s: bad news in cmd640_tune_drive\n", drive->name);
+ return;
+ }
+ }
+ /*
+ * If the user asks for pio_mode 9 (no such mode),
+ * we take it to mean "turn ON prefetch" for this drive.
+ *
+ * If the user asks for pio_mode 8 (no such mode),
+ * we take it to mean "turn OFF prefetch" for this drive.
+ */
+ if ((mode_wanted & 0xfe) == 0x08) { /* program prefetch? */
+ mode_wanted &= 1;
+ set_prefetch_mode(index, mode_wanted);
+ printk("%s: %sabled cmd640 prefetch\n", drive->name, mode_wanted ? "en" : "dis");
+ return;
+ }

- mc_time = pio_timings[pio_mode].mc_time;
- av_time = pio_timings[pio_mode].av_time;
- ds_time = (ds_time != 0) ? ds_time : pio_timings[pio_mode].ds_time;
+ (void) ide_get_best_pio_mode (drive, mode_wanted, 5, &d);
+ cmd640_set_mode (index, d.pio_mode, d.cycle_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, pio_mode);
- cmd640_set_timing(interface_number, drive_number);
+ printk ("%s: selected cmd640 PIO mode%d (%dns) %s/IORDY%s\n",
+ drive->name,
+ d.pio_mode,
+ d.cycle_time,
+ d.use_iordy ? "w" : "wo",
+ d.overridden ? " (overriding vendor mode)" : "");
}

+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
/*
- * Drive PIO mode "autoconfiguration".
+ * Probe for a cmd640 chipset, and initialize it if found. Called from ide.c
*/
+int ide_probe_for_cmd640x (void)
+{
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ int second_port_toggled = 0;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+ int second_port_cmd640 = 0;
+ const char *bus_type, *port2;
+ unsigned int index;
+ byte b;

-static void cmd640_tune_drive(ide_drive_t *drive, byte pio_mode) {
- int max_pio;
- int ds_time;
- int readahead; /* there is a global named read_ahead */
- int overridden;
- int iordy;
- struct hd_driveid* id;
+ if (cmd640_vlb && probe_for_cmd640_vlb()) {
+ bus_type = "VLB";
+ } else {
+ cmd640_vlb = 0;
+ if (probe_for_cmd640_pci1())
+ bus_type = "PCI (type1)";
+ else if (probe_for_cmd640_pci2())
+ bus_type = "PCI (type2)";
+ else
+ return 0;
+ }

- if (pio_mode != 255) {
- cmd640_set_mode(drive, pio_mode, 0);
- return;
+#ifdef CMD640_DUMP_REGS
+ CMD640_DUMP_REGS;
+#endif
+ /*
+ * Undocumented magic (there is no 0x5b reg in specs)
+ */
+ put_cmd640_reg(0x5b, 0xbd);
+ if (get_cmd640_reg(0x5b) != 0xbd) {
+ printk("ide: cmd640 init failed: wrong value in reg 0x5b\n");
+ return 0;
+ }
+ put_cmd640_reg(0x5b, 0);
+
+ /*
+ * Documented magic begins here
+ */
+ cmd640_chip_version = get_cmd640_reg(CFR) & CFR_DEVREV;
+ if (cmd640_chip_version == 0) {
+ printk ("ide: bad cmd640 revision: %d\n", cmd640_chip_version);
+ return 0;
}

- 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);
+ /*
+ * Initialize data for primary port
+ */
+ setup_device_ptrs ();
+ printk("%s: buggy cmd640%c interface on %s\n",
+ cmd_hwif0->name, 'A' + cmd640_chip_version - 1, bus_type);
+ cmd_hwif0->chipset = ide_cmd640;
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ cmd_hwif0->tuneproc = &cmd640_tune_drive;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
+ /*
+ * Ensure compatibility by always using the slowest timings
+ * for access to the drive's command register block,
+ * and reset the prefetch burstsize to default (512 bytes)
+ */
+ put_cmd640_reg(CMDTIM, 0);
+ put_cmd640_reg(BRST, 0x40);
+
+ /*
+ * Try to enable the secondary interface, if not already enabled
+ */
+ if (cmd_hwif1->noprobe) {
+ port2 = "not probed";
} else {
- max_pio = id->tPIO;
- ds_time = pio_timings[max_pio].ds_time;
- if (id->field_valid & 2) {
- if ((id->capability & 8) && (id->eide_pio_modes & 7)) {
- if (id->eide_pio_modes & 4) max_pio = 5;
- else if (id->eide_pio_modes & 2) max_pio = 4;
- else max_pio = 3;
- ds_time = id->eide_pio_iordy;
- iordy = 1;
+ b = get_cmd640_reg(CNTRL);
+ if (secondary_port_responding()) {
+#if 0
+ if ((b & CNTRL_ENA_2ND) || cmd640_vlb) {
+#else
+ if ((b & CNTRL_ENA_2ND)) {
+#endif
+ second_port_cmd640 = 1;
+ port2 = "okay";
+ } else
+ port2 = "not cmd640";
+ } else {
+ put_cmd640_reg(CNTRL, b ^ CNTRL_ENA_2ND); /* toggle the bit */
+ if (secondary_port_responding()) {
+ second_port_cmd640 = 1;
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ second_port_toggled = 1;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+ port2 = "enabled";
} else {
- ds_time = id->eide_pio;
+ put_cmd640_reg(CNTRL, b); /* restore original setting */
+ port2 = "not responding";
}
- 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--;
- overridden = 1;
- iordy = (max_pio > 2);
- ds_time = pio_timings[max_pio].ds_time;
}
}

- cmd640_set_mode(drive, max_pio, ds_time);
-
/*
- * Disable (or set) readahead mode
+ * Initialize data for secondary cmd640 port, if enabled
*/
-
- readahead = 0;
- if (cmd640_chip_version > 1) { /* Mmmm.. probably should be > 2 ?? */
- readahead = known_drive_readahead(id->model);
- if (readahead == -1)
- readahead = 1; /* Mmmm.. probably be 0 ?? */
- set_readahead_mode(drive, readahead);
+ if (second_port_cmd640) {
+ cmd_hwif0->serialized = 1;
+ cmd_hwif1->serialized = 1;
+ cmd_hwif1->chipset = ide_cmd640;
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ cmd_hwif1->tuneproc = &cmd640_tune_drive;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+ }
+ printk("%s: %sserialized, secondary interface %s\n", cmd_hwif1->name,
+ cmd_hwif0->serialized ? "" : "not ", port2);
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ printk("cmd640: Assuming %dMhz %s bus speed for PIO modes (override with idebus=xx)\n",
+ ide_system_bus_speed, cmd640_vlb ? "VESA" : "PCI");
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
+ /*
+ * Establish initial timings/prefetch for all drives.
+ * Do not unnecessarily disturb any prior BIOS setup of these.
+ */
+ for (index = 0; index < (2 + (second_port_cmd640 << 1)); index++) {
+ ide_drive_t *drive = cmd_drives[index];
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ if (drive->autotune || ((index > 1) && second_port_toggled)) {
+ /*
+ * Reset timing to the slowest speed and turn off prefetch.
+ * This way, the drive identify code has a better chance.
+ */
+ setup_counts [index] = 4; /* max possible */
+ active_counts [index] = 16; /* max possible */
+ recovery_counts [index] = 16; /* max possible */
+ program_drive_counts (index);
+ set_prefetch_mode (index, 0);
+ printk("cmd640: drive%d timings/prefetch cleared\n", index);
+ } else {
+ /*
+ * Record timings/prefetch without changing them.
+ * This preserves any prior BIOS setup.
+ */
+ retrieve_drive_counts (index);
+ check_prefetch (index);
+ printk("cmd640: drive%d timings/prefetch(%s) preserved\n",
+ index, drive->no_unmask ? "on" : "off");
+ }
+#else
+ /*
+ * Set the drive unmask flags to match the prefetch setting
+ */
+ check_prefetch (index);
+ printk("cmd640: drive%d timings/prefetch(%s) preserved\n",
+ index, drive->no_unmask ? "on" : "off");
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
}

- 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");
+#ifdef CMD640_DUMP_REGS
+ CMD640_DUMP_REGS;
+#endif
+ return 1;
}
+
diff -u --recursive --new-file --exclude=.* linux-2.0.10/drivers/block/dtc2278.c linux/drivers/block/dtc2278.c
--- linux-2.0.10/drivers/block/dtc2278.c Fri Jul 26 17:48:14 1996
+++ linux/drivers/block/dtc2278.c Thu Aug 1 19:32:46 1996
@@ -71,8 +71,7 @@
{
unsigned long flags;

- if (pio == 255)
- pio = ide_get_best_pio_mode(drive);
+ pio = ide_get_best_pio_mode(drive, pio, 4, NULL);

if (pio >= 3) {
save_flags(flags);
@@ -122,6 +121,8 @@
ide_hwifs[0].chipset = ide_dtc2278;
ide_hwifs[1].chipset = ide_dtc2278;
ide_hwifs[0].tuneproc = &tune_dtc2278;
- ide_hwifs[0].no_unmask = 1;
- ide_hwifs[1].no_unmask = 1;
+ ide_hwifs[0].drives[0].no_unmask = 1;
+ ide_hwifs[0].drives[1].no_unmask = 1;
+ ide_hwifs[1].drives[0].no_unmask = 1;
+ ide_hwifs[1].drives[1].no_unmask = 1;
}
diff -u --recursive --new-file --exclude=.* linux-2.0.10/drivers/block/ht6560b.c linux/drivers/block/ht6560b.c
--- linux-2.0.10/drivers/block/ht6560b.c Fri Apr 12 02:49:32 1996
+++ linux/drivers/block/ht6560b.c Tue Jul 30 00:18:17 1996
@@ -201,10 +201,8 @@
if (drive->media != ide_disk)
pio = 0; /* some cdroms don't like fast modes (?) */
else
- pio = ide_get_best_pio_mode (drive);
+ pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
}
- if (pio > 5)
- pio = 5;
unit = drive->select.b.unit;
hwif = HWIF(drive)->index;
ht6560b_timings[hwif][unit] = pio_to_timings[pio];
diff -u --recursive --new-file --exclude=.* linux-2.0.10/drivers/block/ide.c linux/drivers/block/ide.c
--- linux-2.0.10/drivers/block/ide.c Sat Jul 27 11:36:40 1996
+++ linux/drivers/block/ide.c Fri Aug 2 10:17:05 1996
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/ide.c Version 5.46 Jul 23, 1996
+ * linux/drivers/block/ide.c Version 5.48 Jul 31, 1996
*
* Copyright (C) 1994-1996 Linus Torvalds & authors (see below)
*/
@@ -246,6 +246,14 @@
* include mc68000 patches from Geert Uytterhoeven
* add Gadi's fix for PCMCIA cdroms
* Version 5.46 remove the mc68000 #ifdefs for 2.0.x
+ * Version 5.47 fix set_tune race condition
+ * Version 5.48 if def'd, invoke CMD640_DUMP_REGS when irq probe fails
+ * lengthen the do_reset1() pulse, for laptops
+ * add idebus=xx parameter for cmd640 and ali chipsets
+ * no_unmask flag now per-drive instead of per-hwif
+ * fix tune_req so that it gets done immediately
+ * fix missing restore_flags() in ide_ioctl
+ * prevent use of io_32bit on cmd640 with no prefetch
*
* Some additional driver compile-time options are in ide.h
*
@@ -402,6 +410,12 @@

for (index = 0; index < MAX_HWIFS; ++index)
init_hwif_data(index);
+
+ ide_system_bus_speed = 50;
+#ifdef CONFIG_PCI
+ if (pcibios_present())
+ ide_system_bus_speed = 40;
+#endif /* CONFIG_PCI */
}

#if SUPPORT_VLB_SYNC
@@ -770,9 +784,9 @@
* recover from reset very quickly, saving us the first 50ms wait time.
*/
OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG); /* set SRST and nIEN */
- udelay(5); /* more than enough time */
+ udelay(10); /* more than enough time */
OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */
- udelay(5); /* more than enough time */
+ udelay(10); /* more than enough time */
hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
ide_set_handler (drive, &reset_pollfunc, HZ/20);
#endif /* OK_TO_RESET_CONTROLLER */
@@ -1183,7 +1197,7 @@
static inline void do_special (ide_drive_t *drive)
{
special_t *s = &drive->special;
-next:
+
#ifdef DEBUG
printk("%s: do_special: 0x%02x\n", drive->name, s->all);
#endif
@@ -1201,12 +1215,11 @@
s->b.recalibrate = 0;
if (drive->media == ide_disk && !IS_PROMISE_DRIVE)
ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr);
- } else if (s->b.set_pio) {
+ } else if (s->b.set_tune) {
ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
- s->b.set_pio = 0;
+ s->b.set_tune = 0;
if (tuneproc != NULL)
- tuneproc(drive, drive->pio_req);
- goto next;
+ tuneproc(drive, drive->tune_req);
} else if (s->b.set_multmode) {
s->b.set_multmode = 0;
if (drive->media == ide_disk) {
@@ -2046,7 +2059,7 @@
drive->keep_settings = arg;
break;
case HDIO_SET_UNMASKINTR:
- if (arg && HWIF(drive)->no_unmask) {
+ if (arg && drive->no_unmask) {
restore_flags(flags);
return -EPERM;
}
@@ -2056,8 +2069,18 @@
drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
break;
case HDIO_SET_32BIT:
- if (arg > (1 + (SUPPORT_VLB_SYNC<<1)))
+ if (arg > (1 + (SUPPORT_VLB_SYNC<<1))) {
+ restore_flags(flags);
return -EINVAL;
+ }
+#ifdef CONFIG_BLK_DEV_CMD640
+ if (arg && HWIF(drive)->chipset == ide_cmd640) {
+ if (!drive->no_unmask) {
+ restore_flags(flags);
+ return -EPERM;
+ }
+ }
+#endif /* CONFIG_BLK_DEV_CMD640 */
drive->io_32bit = arg;
#ifdef CONFIG_BLK_DEV_DTC2278
if (HWIF(drive)->chipset == ide_dtc2278)
@@ -2123,9 +2146,14 @@
return -ENOSYS;
save_flags(flags);
cli();
- drive->pio_req = (int) arg;
- drive->special.b.set_pio = 1;
+ if (drive->special.b.set_tune) {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ drive->tune_req = (byte) arg;
+ drive->special.b.set_tune = 1;
restore_flags(flags);
+ (void) ide_do_drive_cmd (drive, &rq, ide_wait);
return 0;

RO_IOCTLS(inode->i_rdev, arg);
@@ -2431,11 +2459,6 @@
save_flags(flags);
cli(); /* some systems need this */
do_identify(drive, cmd); /* drive returned ID */
- if (drive->present && drive->media != ide_tape) {
- ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
- if (tuneproc != NULL && drive->autotune == 1)
- tuneproc(drive, 255); /* auto-tune PIO mode */
- }
rc = 0; /* drive responded with ID */
(void) GET_STAT(); /* clear drive IRQ */
restore_flags(flags);
@@ -2455,12 +2478,12 @@
} else { /* Mmmm.. multiple IRQs.. don't know which was ours */
printk("%s: IRQ probe failed (%d)\n", drive->name, irqs);
#ifdef CONFIG_BLK_DEV_CMD640
+#ifdef CMD640_DUMPREGS
if (HWIF(drive)->chipset == ide_cmd640) {
- extern byte (*get_cmd640_reg)(int);
printk("%s: Hmmm.. probably a driver problem.\n", drive->name);
- printk("%s: cmd640 reg 09h == 0x%02x\n", drive->name, get_cmd640_reg(9));
- printk("%s: cmd640 reg 51h == 0x%02x\n", drive->name, get_cmd640_reg(0x51));
+ CMD640_DUMP_REGS;
}
+#endif /* CMD640_DUMPREGS */
#endif /* CONFIG_BLK_DEV_CMD640 */
}
}
@@ -2665,6 +2688,14 @@
}
}
restore_flags(flags);
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (drive->present && drive->media != ide_tape) {
+ ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
+ if (tuneproc != NULL && drive->autotune == 1)
+ tuneproc(drive, 255); /* auto-tune PIO mode */
+ }
+ }
}
}

@@ -2701,9 +2732,11 @@
* Try matching against the supplied keywords,
* and return -(index+1) if we match one
*/
- for (i = 0; *keywords != NULL; ++i) {
- if (!strcmp(s, *keywords++))
- return -(i+1);
+ if (keywords != NULL) {
+ for (i = 0; *keywords != NULL; ++i) {
+ if (!strcmp(s, *keywords++))
+ return -(i+1);
+ }
}
/*
* Look for a series of no more than "max_vals"
@@ -2750,6 +2783,15 @@
* and quite likely to cause trouble with
* older/odd IDE drives.
*
+ * "idebus=xx" : inform IDE driver of VESA/PCI bus speed in Mhz,
+ * where "xx" is between 25 and 66 inclusive,
+ * used when tuning chipset PIO modes.
+ * For PCI bus, 25 is correct for a P75 system,
+ * 30 is correct for P90,P120,P180 systems,
+ * and 33 is used for P100,P133,P166 systems.
+ * If in doubt, use idebus=33 for PCI.
+ * As for VLB, it is safest to not specify it.
+ *
* "idex=noprobe" : do not attempt to access/use this interface
* "idex=base" : probe for an interface at the addr specified,
* where "base" is usually 0x1f0 or 0x170
@@ -2838,10 +2880,26 @@
goto bad_option;
}
}
+
+ if (s[0] != 'i' || s[1] != 'd' || s[2] != 'e')
+ goto bad_option;
+
+ /*
+ * Look for bus speed option: "idebus="
+ */
+ if (s[3] == 'b' && s[4] == 'u' && s[5] == 's') {
+ if (match_parm(&s[6], NULL, vals, 1) != 1)
+ goto bad_option;
+ if (vals[0] >= 25 && vals[0] <= 66)
+ ide_system_bus_speed = vals[0];
+ else
+ printk(" -- BAD BUS SPEED! Expected value from 25 to 66");
+ goto done;
+ }
/*
* Look for interface options: "idex="
*/
- if (s[0] == 'i' && s[1] == 'd' && s[2] == 'e' && s[3] >= '0' && s[3] <= max_hwif) {
+ if (s[3] >= '0' && s[3] <= max_hwif) {
/*
* Be VERY CAREFUL changing this: note hardcoded indexes below
*/
@@ -3304,6 +3362,7 @@
int index;

init_ide_data ();
+
/*
* Probe for special "known" interface chipsets
*/
diff -u --recursive --new-file --exclude=.* linux-2.0.10/drivers/block/ide.h linux/drivers/block/ide.h
--- linux-2.0.10/drivers/block/ide.h Sat Jul 27 11:36:40 1996
+++ linux/drivers/block/ide.h Thu Aug 1 21:59:55 1996
@@ -41,6 +41,11 @@
#define FANCY_STATUS_DUMPS 1 /* 0 to reduce kernel size */
#endif

+#ifdef CONFIG_BLK_DEV_CMD640
+void cmd640_dump_regs (void);
+#define CMD640_DUMP_REGS cmd640_dump_regs() /* for debugging cmd640 chipset */
+#endif /* CONFIG_BLK_DEV_CMD640 */
+
#if defined(CONFIG_BLK_DEV_IDECD) || defined(CONFIG_BLK_DEV_IDETAPE)
#define CONFIG_BLK_DEV_IDEATAPI 1
#endif
@@ -291,7 +296,7 @@
unsigned set_geometry : 1; /* respecify drive geometry */
unsigned recalibrate : 1; /* seek to cyl 0 */
unsigned set_multmode : 1; /* set multmode count */
- unsigned set_pio : 1; /* set pio mode */
+ unsigned set_tune : 1; /* tune interface for drive */
unsigned reserved : 4; /* unused */
} b;
} special_t;
@@ -317,6 +322,7 @@
unsigned using_dma : 1; /* disk is using dma for read/write */
unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */
unsigned unmask : 1; /* flag: okay to unmask other irqs */
+ unsigned no_unmask : 1; /* disallow setting unmask bit */
unsigned nobios : 1; /* flag: do not probe bios for drive */
unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */
#if FAKE_FDISK_FOR_EZDRIVE
@@ -328,7 +334,7 @@
byte ready_stat; /* min status value for drive ready */
byte mult_count; /* current multiple sector setting */
byte mult_req; /* requested multiple sector setting */
- byte pio_req; /* requested drive pio setting */
+ byte tune_req; /* requested drive tuning setting */
byte io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
byte bad_wstat; /* used for ignoring WRERR_STAT */
byte sect0; /* offset of first sector for DM6:DDO */
@@ -422,7 +428,6 @@
unsigned noprobe : 1; /* don't probe for this interface */
unsigned present : 1; /* this interface exists */
unsigned serialized : 1; /* serialized operation with mate hwif */
- unsigned no_unmask : 1; /* disallow setting unmask bits */
unsigned sharing_irq: 1; /* 1 = sharing irq with another hwif */
#ifdef CONFIG_BLK_DEV_PROMISE
unsigned is_promise2: 1; /* 2nd i/f on promise DC4030 */
@@ -461,11 +466,15 @@
* should be using pointers to a drive (ide_drive_t *) or
* pointers to a hwif (ide_hwif_t *), rather than indexing this
* structure directly (the allocation/layout may change!).
+ *
+ * ide_system_bus_speed is used for calculating interface PIO timings.
*/
#ifdef _IDE_C
ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */
+ int ide_system_bus_speed; /* VESA/PCI bus speed in Mhz */
#else
extern ide_hwif_t ide_hwifs[];
+extern int ide_system_bus_speed;
#endif

/*
diff -u --recursive --new-file --exclude=.* linux-2.0.10/drivers/block/ide_modes.h linux/drivers/block/ide_modes.h
--- linux-2.0.10/drivers/block/ide_modes.h Sun May 12 14:21:04 1996
+++ linux/drivers/block/ide_modes.h Thu Aug 1 00:16:17 1996
@@ -15,13 +15,43 @@

#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS)

+/*
+ * Standard (generic) timings for PIO modes, from ATA2 specification.
+ * These timings are for access to the IDE data port register *only*.
+ * Some drives may specify a mode, while also specifying a different
+ * value for cycle_time (from drive identification data).
+ */
+typedef struct ide_pio_timings_s {
+ int setup_time; /* Address setup (ns) minimum */
+ int active_time; /* Active pulse (ns) minimum */
+ int cycle_time; /* Cycle time (ns) minimum = (setup + active + recovery) */
+} ide_pio_timings_t;
+
+typedef struct ide_pio_data_s {
+ byte pio_mode;
+ byte use_iordy;
+ byte overridden;
+ byte blacklisted;
+ unsigned int cycle_time;
+} ide_pio_data_t;
+
#ifndef _IDE_C

int ide_scan_pio_blacklist (char *model);
-unsigned int ide_get_best_pio_mode (ide_drive_t *drive);
+byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d);
+extern const ide_pio_timings_t ide_pio_timings[6];

#else /* _IDE_C */

+const ide_pio_timings_t ide_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 with IORDY */
+ { 25, 70, 120 }, /* PIO Mode 4 with IORDY */
+ { 20, 50, 100 } /* PIO Mode 5 with IORDY (nonstandard) */
+};
+
/*
* Black list. Some drives incorrectly report their maximal PIO mode,
* at least in respect to CMD640. Here we keep info on some known drives.
@@ -112,31 +142,80 @@
}

/*
- * This routine returns the recommended PIO mode for a given drive,
+ * This routine returns the recommended PIO settings for a given drive,
* based on the drive->id information and the ide_pio_blacklist[].
* This is used by most chipset support modules when "auto-tuning".
*/
-unsigned int ide_get_best_pio_mode (ide_drive_t *drive)
-{
- unsigned int pio = 0;
- struct hd_driveid *id = drive->id;

- if (id != NULL) {
- if (HWIF(drive)->chipset != ide_cmd640 && !strcmp("QUANTUM FIREBALL1080A", id->model))
- pio = 4;
- else
- pio = ide_scan_pio_blacklist(id->model);
- if (pio == -1) {
- pio = (id->tPIO < 2) ? id->tPIO : 2;
- if (id->field_valid & 2) {
- byte modes = id->eide_pio_modes;
- if (modes & 4) pio = 5;
- else if (modes & 2) pio = 4;
- else if (modes & 1) pio = 3;
+/*
+ * Drive PIO mode auto selection
+ */
+byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d)
+{
+ int pio_mode;
+ int cycle_time = 0;
+ int use_iordy = 0;
+ struct hd_driveid* id = drive->id;
+ int overridden = 0;
+ int blacklisted = 0;
+
+ if (mode_wanted != 255) {
+ pio_mode = mode_wanted;
+ } else if (!drive->id) {
+ pio_mode = 0;
+ } else if ((pio_mode = ide_scan_pio_blacklist(id->model)) != -1) {
+ overridden = 1;
+ blacklisted = 1;
+ use_iordy = (pio_mode > 2);
+ } else {
+ pio_mode = id->tPIO;
+ if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */
+ pio_mode = 2;
+ overridden = 1;
+ }
+ if (id->field_valid & 2) { /* drive implements ATA2? */
+ if (id->capability & 8) { /* drive supports use_iordy? */
+ use_iordy = 1;
+ cycle_time = id->eide_pio_iordy;
+ if (id->eide_pio_modes & 7) {
+ overridden = 0;
+ if (id->eide_pio_modes & 4)
+ pio_mode = 5;
+ else if (id->eide_pio_modes & 2)
+ pio_mode = 4;
+ else
+ pio_mode = 3;
+ }
+ } else {
+ cycle_time = id->eide_pio;
}
}
+
+ /*
+ * Conservative "downgrade" for all pre-ATA2 drives
+ */
+ if (pio_mode && pio_mode < 4) {
+ pio_mode--;
+ overridden = 1;
+#if 0
+ use_iordy = (pio_mode > 2);
+#endif
+ if (cycle_time && cycle_time < ide_pio_timings[pio_mode].cycle_time)
+ cycle_time = 0; /* use standard timing */
+ }
+ }
+ if (pio_mode > max_mode) {
+ pio_mode = max_mode;
+ cycle_time = 0;
+ }
+ if (d) {
+ d->pio_mode = pio_mode;
+ d->cycle_time = cycle_time ? cycle_time : ide_pio_timings[pio_mode].cycle_time;
+ d->use_iordy = use_iordy;
+ d->overridden = overridden;
+ d->blacklisted = blacklisted;
}
- return pio;
+ return pio_mode;
}

#endif /* _IDE_C */
diff -u --recursive --new-file --exclude=.* linux-2.0.10/drivers/block/qd6580.c linux/drivers/block/qd6580.c
--- linux-2.0.10/drivers/block/qd6580.c Fri May 3 04:07:24 1996
+++ linux/drivers/block/qd6580.c Tue Jul 30 00:19:05 1996
@@ -47,10 +47,7 @@
{
unsigned long flags;

- if (pio == 255)
- pio = ide_get_best_pio_mode (drive);
- if (pio > 3)
- pio = 3;
+ pio = ide_get_best_pio_mode(drive, pio, 3, NULL);

save_flags(flags);
cli();
diff -u --recursive --new-file --exclude=.* linux-2.0.10/drivers/block/rz1000.c linux/drivers/block/rz1000.c
--- linux-2.0.10/drivers/block/rz1000.c Thu Mar 28 01:19:44 1996
+++ linux/drivers/block/rz1000.c Thu Aug 1 19:32:21 1996
@@ -45,8 +45,10 @@
if ((rc = pcibios_read_config_word(bus, fn, 0x40, &reg))
|| (rc = pcibios_write_config_word(bus, fn, 0x40, reg & 0xdfff)))
{
- ide_hwifs[0].no_unmask = 1;
- ide_hwifs[1].no_unmask = 1;
+ ide_hwifs[0].drives[0].no_unmask = 1;
+ ide_hwifs[0].drives[1].no_unmask = 1;
+ ide_hwifs[1].drives[0].no_unmask = 1;
+ ide_hwifs[1].drives[1].no_unmask = 1;
ide_hwifs[0].serialized = 1;
ide_hwifs[1].serialized = 1;
ide_pci_access_error (rc);
diff -u --recursive --new-file --exclude=.* linux-2.0.10/drivers/block/umc8672.c linux/drivers/block/umc8672.c
--- linux-2.0.10/drivers/block/umc8672.c Fri May 10 01:03:33 1996
+++ linux/drivers/block/umc8672.c Thu Aug 1 01:13:28 1996
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/umc8672.c Version 0.04 May 09, 1996
+ * linux/drivers/block/umc8672.c Version 0.05 Jul 31, 1996
*
* Copyright (C) 1995-1996 Linus Torvalds & author (see below)
*/
@@ -16,6 +16,8 @@
*
* Version 0.02 now configs/compiles separate from ide.c -ml
* Version 0.03 enhanced auto-tune, fix display bug
+ * Version 0.05 replace sti() with restore_flags() -ml
+ * add detection of possible race condition -ml
*/

/*
@@ -81,10 +83,7 @@
static void umc_set_speeds (byte speeds[])
{
int i, tmp;
- unsigned long flags;

- save_flags(flags);
- cli ();
outb_p (0x5A,0x108); /* enable umc */

out_umc (0xd7,(speedtab[0][speeds[2]] | (speedtab[0][speeds[3]]<<4)));
@@ -101,7 +100,6 @@
out_umc (0xd8+i,speedtab[2][speeds[i]]);
}
outb_p (0xa5,0x108); /* disable umc */
- restore_flags(flags);

printk ("umc8672: drive speeds [0 to 11]: %d %d %d %d\n",
speeds[0], speeds[1], speeds[2], speeds[3]);
@@ -109,36 +107,44 @@

static void tune_umc (ide_drive_t *drive, byte pio)
{
- if (pio == 255)
- pio = ide_get_best_pio_mode(drive);
- if (pio > 4)
- pio = 4;
+ unsigned long flags;
+ ide_hwgroup_t *hwgroup = ide_hwifs[HWIF(drive)->index^1].hwgroup;

- current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
- umc_set_speeds (current_speeds);
+ pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+ printk("%s: setting umc8672 to PIO mode%d (speed %d)\n", drive->name, pio, pio_to_umc[pio]);
+ save_flags(flags);
+ cli();
+ if (hwgroup->handler != NULL) {
+ printk("umc8672: other interface is busy: exiting tune_umc()\n");
+ } else {
+ current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
+ umc_set_speeds (current_speeds);
+ }
+ restore_flags(flags);
}

void init_umc8672 (void) /* called from ide.c */
{
unsigned long flags;

+ save_flags(flags);
+ cli ();
if (check_region(0x108, 2)) {
+ restore_flags(flags);
printk("\numc8672: PORTS 0x108-0x109 ALREADY IN USE\n");
return;
}
- save_flags(flags);
- cli ();
outb_p (0x5A,0x108); /* enable umc */
if (in_umc (0xd5) != 0xa0)
{
- sti ();
+ restore_flags(flags);
printk ("umc8672: not found\n");
return;
}
outb_p (0xa5,0x108); /* disable umc */
- restore_flags(flags);

umc_set_speeds (current_speeds);
+ restore_flags(flags);

request_region(0x108, 2, "umc8672");
ide_hwifs[0].chipset = ide_umc8672;

--------------116121F7C3FAF555575A74D--