Generic PCI IDE bus-mastering DMA support

Christian Brunner (chb@muc.de)
Mon, 15 Sep 1997 14:23:51 +0200 (MET DST)


The attached patch converts the "Intel Triton IDE support" into a "Generic
PCI bus-mastering DMA support". Actually these are quite small
changes. I just had to change the way that PCI IDE interfaces are
probed a little. Hopefully I haven't broken anything. At least it now
works with my SiS 5571 chipset... :)

If a PCI IDE device claims DMA-burst support, then the DMA support is turned
on. Acording to Mark Lord, the error recovery code will turn it off again if
it doesn't work!

Since Mark Lord seems to be quite bussy (I have sent him this patch a
week ago, but didn't receive an answer yet), I decided to send it to
the kernel list.

At the moment I don't know if ther are chipsets, that go wild when treated
like a Intel chipset, so let me know, when you run into trouble.

BTW: I removed the DISPLAY_PIIX_TIMINGS, because they don't make sense with
non Intel chipsets and I think that "pcistat" (as discussed at the
list over the last days) would be the ideal place to deal with such
information.

Bye, Christian Brunner (chb@muc.de)

PS: This is my first attempt to do some kernel hacking, so please bare with
me...

--- linux/Documentation/Configure.help.51 Mon Sep 15 12:04:34 1997
+++ linux/Documentation/Configure.help Mon Sep 15 12:13:06 1997
@@ -294,14 +294,14 @@
Linux. This may slow disk throughput by a few percent, but at least
things will operate 100% reliably. If unsure, say Y.

-Intel 82371 PIIX (Triton I/II), VIA VP-1 DMA support
+Generic PCI bus-mastering DMA support
CONFIG_BLK_DEV_TRITON
If your PCI system uses an IDE harddrive (as opposed to SCSI, say)
- and includes the Intel Triton I/II IDE interface chipset (i82371FB,
- i82371SB or i82371AB), or the VIA VP-1 IDE interface chipset
- (VT82C586), you will want to enable this option to allow use of
- bus-mastering DMA data transfers. Read the comments at the
- beginning of drivers/block/triton.c and Documentation/ide.txt.
+ and includes an PCI controler, that supports bus-mastering DMA,
+ you will want to enable this option to allow use of bus-mastering
+ (Chipsets known to work are: Intel Triton I/II, VIA VP-1 and various
+ SiS interface chipsets). Read the comments at the beginning of
+ drivers/block/triton.c and Documentation/ide.txt.
You can get the latest version of the hdparm utility via
ftp (user: anonymous) from
sunsite.unc.edu/pub/Linux/kernel/patches/diskdrives/; it is
--- linux/drivers/block/ide.c.51 Fri Aug 29 12:20:08 1997
+++ linux/drivers/block/ide.c Wed Sep 10 22:05:00 1997
@@ -2551,29 +2551,62 @@
}

#ifdef CONFIG_PCI
-#if defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) || defined(CONFIG_BLK_DEV_OPTI621)

typedef void (ide_pci_init_proc_t)(byte, byte);

/*
- * ide_probe_pci() scans PCI for a specific vendor/device function,
- * and invokes the supplied init routine for each instance detected.
+ * ide_probe_pci() scans PCI IDE-Devices for known Chipsets or generic
+ * busmaster DMA support.
*/
-__initfunc(static void ide_probe_pci (unsigned short vendor, unsigned short device, ide_pci_init_proc_t *init, int func_adj))
+
+__initfunc(static void ide_probe_pci (byte bus, byte fn))
{
- unsigned long flags;
- unsigned index;
- byte fn, bus;
-
- save_flags(flags);
- cli();
- for (index = 0; !pcibios_find_device (vendor, device, index, &bus, &fn); ++index) {
- init (bus, fn + func_adj);
+ unsigned short vendor, device, command;
+ unsigned char lat;
+ unsigned long flags;
+ int func_adj = 0;
+
+ save_flags(flags);
+ cli();
+
+ pcibios_read_config_word (bus, fn, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word (bus, fn, PCI_DEVICE_ID, &device);
+
+ if ((vendor == PCI_VENDOR_ID_PCTECH) && (device == PCI_DEVICE_ID_PCTECH_RZ1000)) {
+#ifdef CONFIG_BLK_DEV_RZ1000
+ ide_pci_init_proc_t init_rz1000;
+ init_rz1000 (bus, fn+func_adj);
+#else
+ printk("Warning: RZ1000 chipset detected. You schold turn on CONFIG_BLK_DEV_RZ1000.\n");
+#endif
+ }
+ else if ((vendor == PCI_VENDOR_ID_PCTECH) && (device == PCI_DEVICE_ID_PCTECH_RZ1001)) {
+#ifdef CONFIG_BLK_DEV_RZ1000
+ ide_pci_init_proc_t init_rz1000;
+ init_rz1000 (bus, fn+func_adj);
+#else
+ printk("Warning: RZ1000 chipset detected. You schold turn on CONFIG_BLK_DEV_RZ1000.\n");
+#endif
+ }
+ else if ((vendor == PCI_VENDOR_ID_OPTI) && (device == PCI_DEVICE_ID_OPTI_82C621))
+#ifdef CONFIG_BLK_DEV_OPTI621
+ ide_init_opti621 (bus, fn+func_adj);
+#else
+ printk("Warning: OPTI621 chipset detected. You schold turn on CONFIG_BLK_DEV_OPTI621.\n");
+#endif
+#ifdef CONFIG_BLK_DEV_TRITON
+ else {
+ pcibios_read_config_word (bus, fn, PCI_COMMAND, &command);
+ pcibios_read_config_byte (bus, fn, PCI_LATENCY_TIMER, &lat);
+
+ if (lat && (command & PCI_COMMAND_MASTER))
+ ide_init_triton (bus, fn + func_adj);
}
- restore_flags(flags);
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+ restore_flags(flags);
}

-#endif /* defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) || defined(CONFIG_BLK_DEV_OPTI621) */
#endif /* CONFIG_PCI */

/*
@@ -2585,32 +2618,31 @@
__initfunc(static void probe_for_hwifs (void))
{
#ifdef CONFIG_PCI
+
+ unsigned int index = 0;
+ unsigned int curr = 0;
+ struct pci_dev *dev;
+
/*
* Find/initialize PCI IDE interfaces
*/
if (pcibios_present()) {
-#ifdef CONFIG_BLK_DEV_RZ1000
- ide_pci_init_proc_t init_rz1000;
- ide_probe_pci (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, &init_rz1000, 0);
- ide_probe_pci (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, &init_rz1000, 0);
-#endif /* CONFIG_BLK_DEV_RZ1000 */
-#ifdef CONFIG_BLK_DEV_TRITON
- /*
- * Apparently the BIOS32 services on Intel motherboards are
- * buggy and won't find the PCI_DEVICE_ID_INTEL_82371_1 for us.
- * So instead, we search for PCI_DEVICE_ID_INTEL_82371_0,
- * and then add 1.
- */
- ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1);
- ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0);
- ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, &ide_init_triton, 0);
- ide_probe_pci (PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, &ide_init_triton, 0);
-#endif /* CONFIG_BLK_DEV_TRITON */
-#ifdef CONFIG_BLK_DEV_OPTI621
- ide_probe_pci (PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, &ide_init_opti621, 0);
-#endif /* CONFIG_BLK_DEV_OPTI621 */
+
+ /*
+ * Work around for weird pcibios_find_class() problems
+ */
+ for (dev = pci_devices; dev; dev = dev->next) {
+ if ( (dev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
+ if (curr == index) {
+ ide_probe_pci(dev->bus->number, dev->devfn);
+ ++index;
+ }
+ ++curr;
+ }
+ }
}
#endif /* CONFIG_PCI */
+
#ifdef CONFIG_BLK_DEV_CMD640
{
extern void ide_probe_for_cmd640x (void);
--- linux/drivers/block/ide.h.51 Fri Aug 29 10:10:29 1997
+++ linux/drivers/block/ide.h Thu Sep 11 08:37:38 1997
@@ -295,10 +295,10 @@
* hwif_chipset_t is used to keep track of the specific hardware
* chipset used by each IDE interface, if known.
*/
-typedef enum { ide_unknown, ide_generic, ide_triton,
+typedef enum { ide_unknown, ide_generic, ide_pcidma,
ide_cmd640, ide_dtc2278, ide_ali14xx,
ide_qd6580, ide_umc8672, ide_ht6560b,
- ide_promise, ide_via }
+ ide_promise }
hwif_chipset_t;

typedef struct hwif_s {
--- linux/drivers/block/triton.c.51 Thu Jun 26 21:33:38 1997
+++ linux/drivers/block/triton.c Wed Sep 10 21:45:10 1997
@@ -6,24 +6,24 @@
*/

/*
- * This module provides support for the bus-master IDE DMA function
- * of the Intel PCI Triton chipset families, which use the PIIX (i82371FB,
- * for the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and
- * 440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset).
+ * This module provides support for bus-master IDE DMA functions.
*
- * "PIIX" stands for "PCI ISA IDE Xcellerator".
- *
- * Pretty much the same code could work for other IDE PCI bus-mastering chipsets.
- * Look for DMA support for this someday in the not too distant future.
+ * Once this module supported only Intel Triton chipset families, which use
+ * the PIIX (i82371FB, for the 430 FX chipset), the PIIX3 (i82371SB for the
+ * 430 HX/VX and440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset).
+ * But now it's known to work also with the SIS 5513/5571 chipset and might
+ * work for a lot of other chipset families.
*
* DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies).
*
- * Up to four drives may be enabled for DMA, and the PIIX* chips
- * will arbitrate the PCI bus among them. Note that the PIIX/PIIX3
- * provides a single "line buffer" for the BM IDE function, so performance of
- * multiple (two) drives doing DMA simultaneously will suffer somewhat,
- * as they contest for that resource bottleneck. This is handled transparently
- * inside the PIIX/PIIX3. The PIIX4 does not have this problem.
+ * Up to four drives may be enabled for DMA, and the IDE chips
+ * will arbitrate the PCI bus among them.
+ *
+ * Note that the PIIX/PIIX3 provides a single "line buffer" for the BM IDE
+ * function, so performance of multiple (two) drives doing DMA
+ * simultaneously will suffer somewhat, as they contest for that resource
+ * bottleneck. This is handled transparently inside the PIIX/PIIX3.
+ * The PIIX4 does not have this problem.
*
* By default, DMA support is prepared for use, but is currently enabled only
* for drives which support DMA mode2 (multi/single word), or which are
@@ -64,7 +64,9 @@
* "TX" chipset compatibility and for providing patches for the "TX" chipset.
*
* And, yes, Intel Zappa boards really *do* use both PIIX IDE ports.
+ *
*/
+
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/timer.h>
@@ -82,8 +84,6 @@
#include "ide.h"
#include "ide_modes.h"

-#define DISPLAY_PIIX_TIMINGS /* define this to display timings */
-
/*
* good_dma_drives() lists the model names (from "hdparm -i")
* of drives which do not support mode2 DMA but which are
@@ -123,33 +123,6 @@
#define PIIX_FLAGS_FAST_DMA 8


-union chip_en_reg_u {
- struct {
- unsigned d0_flags :4;
- unsigned d1_flags :4;
- unsigned recovery :2;
- unsigned reserved :2;
- unsigned sample :2;
- unsigned sidetim_enabled:1;
- unsigned ports_enabled :1;
- } piix_s;
- struct {
- unsigned sec_en :1;
- unsigned pri_en :1;
- unsigned reserved :14;
- } via_s;
-};
-
-typedef union chip_en_reg_u piix_timing_t;
-
-typedef struct {
- unsigned pri_recovery :2;
- unsigned pri_sample :2;
- unsigned sec_recovery :2;
- unsigned sec_sample :2;
-} piix_sidetim_t;
-
-
/*
* We currently can handle only one PIIX chip here
*/
@@ -265,7 +238,15 @@
{
unsigned long dma_base = HWIF(drive)->dma_base;
unsigned int reading = (1 << 3);
- piix_timing_t timing;
+ struct {
+ unsigned d0_flags :4;
+ unsigned d1_flags :4;
+ unsigned recovery :2;
+ unsigned reserved :2;
+ unsigned sample :2;
+ unsigned sidetim_enabled:1;
+ unsigned ports_enabled :1;
+ } timing;
unsigned short reg;
byte dflags;

@@ -279,16 +260,16 @@
printk("%s: pcibios read failed\n", HWIF(drive)->name);
return 1;
}
- dflags = drive->select.b.unit ? timing.piix_s.d1_flags : timing.piix_s.d0_flags;
+ dflags = drive->select.b.unit ? timing.d1_flags : timing.d0_flags;
if (dflags & PIIX_FLAGS_FAST_PIO) {
if (func == ide_dma_on && drive->media == ide_disk)
dflags |= PIIX_FLAGS_FAST_DMA;
else
dflags &= ~PIIX_FLAGS_FAST_DMA;
if (drive->select.b.unit == 0)
- timing.piix_s.d0_flags = dflags;
+ timing.d0_flags = dflags;
else
- timing.piix_s.d1_flags = dflags;
+ timing.d1_flags = dflags;
if (pcibios_write_config_word(piix_pci_bus, piix_pci_fn, reg, *(short *)&timing)) {
printk("%s: pcibios write failed\n", HWIF(drive)->name);
return 1;
@@ -352,21 +333,6 @@
return piix_dmaproc(ide_dma_off, drive);
}

-#ifdef DISPLAY_PIIX_TIMINGS
-/*
- * print_piix_drive_flags() displays the currently programmed options
- * in the PIIX/PIIX3/PIIX4 for a given drive.
- */
-static void print_piix_drive_flags (const char *unit, byte dflags)
-{
- printk(" %s ", unit);
- printk( "fastDMA=%s", (dflags & PIIX_FLAGS_FAST_PIO) ? "yes" : "no ");
- printk(" PreFetch=%s", (dflags & PIIX_FLAGS_PREFETCH) ? "on " : "off");
- printk(" IORDY=%s", (dflags & PIIX_FLAGS_USE_IORDY) ? "on " : "off");
- printk(" fastPIO=%s\n", ((dflags & (PIIX_FLAGS_FAST_PIO|PIIX_FLAGS_FAST_DMA)) == PIIX_FLAGS_FAST_PIO) ? "on " : "off");
-}
-#endif /* DISPLAY_PIIX_TIMINGS */
-
static void init_piix_dma (ide_hwif_t *hwif, unsigned short base)
{
static unsigned long dmatable = 0;
@@ -445,7 +411,7 @@
/*
* ide_init_triton() prepares the IDE driver for DMA operation.
* This routine is called once, from ide.c during driver initialization,
- * for each triton chipset which is found (unlikely to be more than one).
+ * for each chipset which is found (unlikely to be more than one).
*/
void ide_init_triton (byte bus, byte fn)
{
@@ -454,7 +420,6 @@
unsigned short pcicmd, devid;
unsigned int bmiba;
const char *chipset = "ide";
- piix_timing_t timings[2];

piix_pci_bus = bus;
piix_pci_fn = fn;
@@ -470,9 +435,11 @@
chipset = "PIIX";
else if (devid == PCI_DEVICE_ID_VIA_82C586_1)
chipset = "VP1";
+ else if (devid == PCI_DEVICE_ID_SI_5513)
+ chipset = "SIS";
else {
printk("Unknown PCI IDE interface 0x%x\n", devid);
- goto quit;
+ chipset = "Unknown";
}

printk("%s: bus-master IDE device on PCI bus %d function %d\n", chipset, bus, fn);
@@ -486,25 +453,6 @@
printk("%s: IDE ports are not enabled (BIOS)\n", chipset);
goto quit;
}
- if (devid == PCI_DEVICE_ID_VIA_82C586_1) {
- /* pri and sec channel enables are in port 0x40 */
- if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0])))
- goto quit;
- if ((!timings[0].via_s.pri_en && (!timings[0].via_s.sec_en))) {
- printk("%s: neither IDE port is enabled\n", chipset);
- goto quit;
- }
- }
- else { /* INTEL piix */
- if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0])))
- goto quit;
- if ((rc = pcibios_read_config_word(bus, fn, 0x42, (short *)&timings[1])))
- goto quit;
- if ((!timings[0].piix_s.ports_enabled) && (!timings[1].piix_s.ports_enabled)) {
- printk("%s: neither IDE port is enabled\n", chipset);
- goto quit;
- }
- }

/*
* See if Bus-Mastered DMA is enabled
@@ -546,7 +494,6 @@
*/
for (h = 0; h < MAX_HWIFS; ++h) {
unsigned int pri_sec;
- piix_timing_t timing;
ide_hwif_t *hwif = &ide_hwifs[h];
switch (hwif->io_ports[IDE_DATA_OFFSET]) {
case 0x1f0: pri_sec = 0; break;
@@ -554,77 +501,10 @@
default: continue;
}

- if (devid == PCI_DEVICE_ID_VIA_82C586_1) {
- timing = timings[0];
- switch (h) {
- case 0:
- if (!timing.piix_s.ports_enabled) {
- printk("port 0 DMA not enabled\n");
- continue;
- }
- case 1:
- if (!timing.piix_s.sidetim_enabled) {
- printk("port 1 DMA not enabled\n");
- continue;
- }
- }
- hwif->chipset = ide_via;
- }
- else { /* PIIX */
+ hwif->chipset = ide_pcidma;

- timing = timings[pri_sec];
- if (!timing.piix_s.ports_enabled) /* interface disabled? */
- continue;
- hwif->chipset = ide_triton;
- }
if (dma_enabled)
init_piix_dma(hwif, bmiba + (pri_sec ? 8 : 0));
-#ifdef DISPLAY_PIIX_TIMINGS
- /*
- * Display drive timings/modes
- */
- {
- const char *slave;
- piix_sidetim_t sidetim;
- byte sample = 5 - timing.piix_s.sample;
- byte recovery = 4 - timing.piix_s.recovery;
- unsigned int drvtim;
-
- if (devid == PCI_DEVICE_ID_VIA_82C586_1) {
- pcibios_read_config_dword(bus, fn, 0x48, &drvtim);
- if (pri_sec == 0) {
- printk(" %s master: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+(drvtim>>28), 1+((drvtim & 0x0f000000)>>24));
- printk(" %s slave: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf00000)>>20), 1+((drvtim & 0x0f0000)>>16));
- continue;
- } else {
- printk(" %s master: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf000)>>12), 1+((drvtim & 0x0f00)>>8));
- printk(" %s slave: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf0)>>4), 1+(drvtim & 0x0f));
- continue;
- }
- }
-
- if ((devid == PCI_DEVICE_ID_INTEL_82371SB_1
- || devid == PCI_DEVICE_ID_INTEL_82371AB)
- && timing.piix_s.sidetim_enabled
- && !pcibios_read_config_byte(bus, fn, 0x44, (byte *) &sidetim))
- slave = ""; /* PIIX3 and later */
- else
- slave = "/slave"; /* PIIX, or PIIX3 in compatibility mode */
- printk(" %s master%s: sample_CLKs=%d, recovery_CLKs=%d\n", hwif->name, slave, sample, recovery);
- print_piix_drive_flags ("master:", timing.piix_s.d0_flags);
- if (!*slave) {
- if (pri_sec == 0) {
- sample = 5 - sidetim.pri_sample;
- recovery = 4 - sidetim.pri_recovery;
- } else {
- sample = 5 - sidetim.sec_sample;
- recovery = 4 - sidetim.sec_recovery;
- }
- printk(" slave : sample_CLKs=%d, recovery_CLKs=%d\n", sample, recovery);
- }
- print_piix_drive_flags ("slave :", timing.piix_s.d1_flags);
- }
-#endif /* DISPLAY_PIIX_TIMINGS */
}

quit: if (rc) printk("%s: pcibios access failed - %s\n", chipset, pcibios_strerror(rc));
--- linux/drivers/block/Config.in.51 Mon Sep 15 12:26:11 1997
+++ linux/drivers/block/Config.in Mon Sep 15 12:28:08 1997
@@ -23,7 +23,7 @@
fi
if [ "$CONFIG_PCI" = "y" ]; then
bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000
- bool ' Intel PIIX/PIIX3/PIIX4 (Triton 430FX/HX/VX/TX, 440FX) DMA support' CONFIG_BLK_DEV_TRITON
+ bool ' Generic PCI bus-mastering DMA support' CONFIG_BLK_DEV_TRITON
fi
bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS
if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
--- linux/Documentation/ide.txt.55 Mon Sep 15 13:02:07 1997
+++ linux/Documentation/ide.txt Mon Sep 15 13:05:41 1997
@@ -58,6 +58,8 @@
- ide-cd.c now compiles separate from ide.c
- Bus-Master DMA support for Intel PCI Triton chipset IDE interfaces
- for details, see comments at top of triton.c
+NEW! - Bus-Master DMA support extended for generic chipset IDE interfaces
+ (courtesy of Christian Brunner <chb@muc.de>).
- ide-cd.c now supports door locking and auto-loading.
- Also preliminary support for multisession
and direct reads of audio data.