Date: Mon, 23 Jun 1997 10:11:19 +0200 (CEST)
From: Geert Uytterhoeven <Geert.Uytterhoeven@cs.kuleuven.ac.be>
To: Linux/m68k <linux-m68k@phil.uni-sb.de>
Subject: L68K: EXPERIMENTAL: IDE doubler
Sender: owner-linux-m68k@phil.uni-sb.de
Reply-To: linux-m68k@phil.uni-sb.de


These are _EXPERIMENTAL_ patches to support the so called `IDE doublers' on
Amiga. They `double' the IDE interface so you can attach 2 chains of 2 devices.

Since I don't have an IDE doubler myself, I don't know whether it will work.
Please let me know, and send me all kernel output (`dmesg'). Of course there's
NO WARRANTY. It may break your machine, erase your disks, etc... USE IT AT YOUR
OWN RISK!!

To activate the IDE doubler: add the option

    ide=doubler

to your kernel command line, _before_ any other ide?= or hd?= options. After
that the IDE driver will probe for hdc and hdd too. Without the ide=doubler
option, the driver will behave exactly the same as the old version.


A test kernel is available from

    http://www.cs.kuleuven.ac.be/~geert/bin/vmlinux-2.1.42-idedoubler.gz

Note that this kernel is highly modularized and doesn't contain support for
other CPUs than '040. If you need video options, don't forget to add the frame
buffer device name, e.g.

    video=amifb:font:PEARL8x8

instead of

    video=font:PEARL8x8


Technical info: an IDE interface has 2 x 8 I/O registers: 8 command registers
and 8 control registers. However, only 2 of the control registers have a real
use, and only 1 of them is used by the Linux IDE driver. That register allows
to soft reset the drive and to disable/enable its interrupt (maybe you
remember: some broken Western Digitals don't honour this interrupt disable
command, causing major headaches some months ago).

The IDE doubler maps the first 8 I/O registers to the 8 command registers of
the first IDE chain, and the second 8 I/O registers to the 8 command registers
of the second IDE chain. As a consequence, the 8 control registers are no
longer available on neither of the 2 chains. This means we'll loose the soft
reset and disable/enable interrupt features. Furthermore the same interrupt
line is used for both chains, and I have no idea whether the driver will handle
it correctly. Test and see :-)

Linux uses the disable/enable interrupt feature for probing on PC hardware,
when it doesn't know which interrupt line the IDE controller is connected to.
Since Amigas and Ataris have fixed interrupts for IDE, this probing isn't
necessary at all and thus we don't need it.

Most changes to the driver are to make it `clean', so the control registers
are never accessed when the IDE doubler is enabled, even for parts of the code
that will never be used on m68k hardware. Maybe someone wants to use it on PC
someday...

According to Oliver Kastl, you can probe for the presence of an IDE doubler by
writing 0xffff to the IDE port and reading it back. The IDE doubler changes the
capacitance of the IDE port. My probe routine also does this, but I don't have
much confidence in this...

Good Luck!

--- linux-2.1.42/arch/m68k/config.in.orig	Mon Jun  9 23:03:16 1997
+++ linux-2.1.42/arch/m68k/config.in	Thu Jun 19 21:18:46 1997
@@ -76,6 +76,9 @@
   dep_tristate '   Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
   dep_tristate '   Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
   dep_tristate '   SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
+  if [ "$CONFIG_AMIGA" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    bool '   Include IDE DOUBLER support' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_IDE
+  fi
 fi
 if [ "$CONFIG_AMIGA" = "y" ]; then
   tristate 'Amiga Zorro II ramdisk support' CONFIG_AMIGA_Z2RAM
--- linux-2.1.42/arch/m68k/defconfig.orig	Mon Jun  9 23:03:16 1997
+++ linux-2.1.42/arch/m68k/defconfig	Thu Jun 19 21:15:19 1997
@@ -57,6 +57,7 @@
 # CONFIG_BLK_DEV_IDETAPE is not set
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_IDEDOUBLER is not set
 # CONFIG_AMIGA_Z2RAM is not set
 # CONFIG_ATARI_ACSI is not set
 # CONFIG_ACSI_MULTI_LUN is not set
--- linux-2.1.42/drivers/block/ide.c.orig	Mon Jun 16 23:31:41 1997
+++ linux-2.1.42/drivers/block/ide.c	Thu Jun 19 23:25:53 1997
@@ -344,6 +344,10 @@
  */
 ide_hwif_t	ide_hwifs[MAX_HWIFS];	/* master data repository */
 
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+int ide_doubler = 0;
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+
 #if (DISK_RECOVERY_TIME > 0)
 /*
  * For really screwy hardware (hey, at least it *can* be used with Linux)
@@ -778,6 +782,10 @@
 		pre_reset(&hwif->drives[unit]);
 
 #if OK_TO_RESET_CONTROLLER
+	if (!HAS_IDE_CONTROL_REG) {
+		restore_flags (flags);
+		return;
+	}
 	/*
 	 * Note that we also set nIEN while resetting the device,
 	 * to mask unwanted interrupts from the interface during the reset.
@@ -976,7 +984,8 @@
 void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
 {
 	ide_set_handler (drive, handler, WAIT_CMD);
-	OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+	if (HAS_IDE_CONTROL_REG)
+		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
 	OUT_BYTE(nsect,IDE_NSECTOR_REG);
 	OUT_BYTE(cmd,IDE_COMMAND_REG);
 }
@@ -1275,7 +1284,8 @@
 		ide_drive_t *drive = choose_drive(hwgroup);
 		if (drive != NULL) {
 			ide_hwif_t *hwif = HWIF(drive);
-			if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif)
+			if (HAS_IDE_CONTROL_REG && hwgroup->hwif->sharing_irq &&
+			    hwif != hwgroup->hwif)
 				OUT_BYTE(hwgroup->drive->ctl|2, hwgroup->hwif->io_ports[IDE_CONTROL_OFFSET]);
 			drive->sleep = 0;
 			blk_dev[hwif->major].current_request = hwgroup->rq = drive->queue;
@@ -1775,7 +1785,8 @@
 	 * allocated for weird IDE interface chipsets.
 	 */
 	ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 8);
-	ide_release_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
+	if (HAS_IDE_CONTROL_REG)
+		ide_release_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
 
 	/*
 	 * Remove us from the hwgroup, and free
@@ -2265,6 +2276,7 @@
  * "ide0=qd6580"	: probe/support qd6580 interface
  * "ide0=ali14xx"	: probe/support ali14xx chipsets (ALI M1439, M1443, M1445)
  * "ide0=umc8672"	: probe/support umc8672 chipsets
+ * "ide=doubler"	: probe/support IDE doublers on Amiga
  */
 __initfunc(void ide_setup (char *s))
 {
@@ -2275,6 +2287,14 @@
 	const char max_drive = 'a' + ((MAX_HWIFS * MAX_DRIVES) - 1);
 	const char max_hwif  = '0' + (MAX_HWIFS - 1);
 
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+	if (!strcmp(s, "ide=doubler")) {
+		printk("+++ Enabled support for IDE doubler\n");
+		ide_doubler = 1;
+		return;
+	}
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+
 	printk("ide_setup: %s", s);
 	init_ide_data ();
 
@@ -2844,6 +2864,9 @@
 EXPORT_SYMBOL(ide_hwifs);
 EXPORT_SYMBOL(ide_register_module);
 EXPORT_SYMBOL(ide_unregister_module);
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+EXPORT_SYMBOL(ide_doubler);
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
 
 /*
  * Probe module
--- linux-2.1.42/drivers/block/ide-cd.c.orig	Thu Apr 17 17:06:48 1997
+++ linux-2.1.42/drivers/block/ide-cd.c	Thu Jun 19 22:16:49 1997
@@ -569,7 +569,8 @@
 
 	OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
 	OUT_BYTE (xferlen >> 8  , IDE_HCYL_REG);
-	OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
+	if (HAS_IDE_CONTROL_REG)
+		OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
  
 	if (info->dma)
 		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
--- linux-2.1.42/drivers/block/ide-disk.c.orig	Sun May 18 23:09:09 1997
+++ linux-2.1.42/drivers/block/ide-disk.c	Thu Jun 19 22:17:05 1997
@@ -313,7 +313,8 @@
 	int use_promise_io = 0;
 #endif /* CONFIG_BLK_DEV_PROMISE */
 
-	OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+	if (HAS_IDE_CONTROL_REG)
+		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
 	OUT_BYTE(rq->nr_sectors,IDE_NSECTOR_REG);
 #ifdef CONFIG_BLK_DEV_PROMISE
 	if (IS_PROMISE_DRIVE) {
--- linux-2.1.42/drivers/block/ide-floppy.c.orig	Sun May 18 23:09:09 1997
+++ linux-2.1.42/drivers/block/ide-floppy.c	Thu Jun 19 22:17:09 1997
@@ -850,7 +850,8 @@
 		dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
 #endif /* CONFIG_BLK_DEV_TRITON */
 
-	OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+	if (HAS_IDE_CONTROL_REG)
+		OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
 	OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG);			/* Use PIO/DMA */
 	OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
 	OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
--- linux-2.1.42/drivers/block/ide-probe.c.orig	Sun May 18 23:09:09 1997
+++ linux-2.1.42/drivers/block/ide-probe.c	Sun Jun 22 22:46:07 1997
@@ -66,6 +66,8 @@
 
 #include "ide.h"
 
+#define DEBUG
+
 static inline void do_identify (ide_drive_t *drive, byte cmd)
 {
 	int bswap = 1;
@@ -176,14 +178,17 @@
 	unsigned long timeout;
 	int irqs = 0;
 
-	if (!HWIF(drive)->irq) {		/* already got an IRQ? */
+	if (HAS_IDE_CONTROL_REG &&
+	    !HWIF(drive)->irq) {		/* already got an IRQ? */
 		probe_irq_off(probe_irq_on());	/* clear dangling irqs */
 		irqs = probe_irq_on();		/* start monitoring irqs */
 		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);	/* enable device irq */
 	}
 
 	delay_50ms();				/* take a deep breath */
-	if ((IN_BYTE(IDE_ALTSTATUS_REG) ^ IN_BYTE(IDE_STATUS_REG)) & ~INDEX_STAT) {
+	if (!HAS_IDE_CONTROL_REG)
+		hd_status = IDE_STATUS_REG;
+	else if ((IN_BYTE(IDE_ALTSTATUS_REG) ^ IN_BYTE(IDE_STATUS_REG)) & ~INDEX_STAT) {
 		printk("%s: probing with STATUS instead of ALTSTATUS\n", drive->name);
 		hd_status = IDE_STATUS_REG;	/* ancient Seagate drives */
 	} else
@@ -221,7 +226,7 @@
 		restore_flags(flags);
 	} else
 		rc = 2;			/* drive refused ID */
-	if (!HWIF(drive)->irq) {
+	if (HAS_IDE_CONTROL_REG && !HWIF(drive)->irq) {
 		irqs = probe_irq_off(irqs);	/* get our irq number */
 		if (irqs > 0) {
 			HWIF(drive)->irq = irqs; /* save it for later */
@@ -396,9 +401,9 @@
 		probe_cmos_for_drives (hwif);
 #if CONFIG_BLK_DEV_PROMISE
 	if (!hwif->is_promise2 &&
-	   (ide_check_region(hwif->io_ports[IDE_DATA_OFFSET],8) || ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET],1))) {
+	   (ide_check_region(hwif->io_ports[IDE_DATA_OFFSET],8) || (HAS_IDE_CONTROL_REG && ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET],1)))) {
 #else
-	if (ide_check_region(hwif->io_ports[IDE_DATA_OFFSET],8) || ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET],1)) {
+	if (ide_check_region(hwif->io_ports[IDE_DATA_OFFSET],8) || (HAS_IDE_CONTROL_REG && ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET],1))) {
 #endif /* CONFIG_BLK_DEV_PROMISE */
 		int msgout = 0;
 		for (unit = 0; unit < MAX_DRIVES; ++unit) {
@@ -426,10 +431,11 @@
 		if (drive->present && !hwif->present) {
 			hwif->present = 1;
 			ide_request_region(hwif->io_ports[IDE_DATA_OFFSET],  8, hwif->name);
-			ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name);
+			if (HAS_IDE_CONTROL_REG)
+				ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name);
 		}
 	}
-	if (hwif->reset) {
+	if (HAS_IDE_CONTROL_REG && hwif->reset) {
 		unsigned long timeout = jiffies + WAIT_WORSTCASE;
 		byte stat;
 
--- linux-2.1.42/drivers/block/ide-tape.c.orig	Sun May 18 23:09:10 1997
+++ linux-2.1.42/drivers/block/ide-tape.c	Thu Jun 19 22:17:28 1997
@@ -1924,7 +1924,8 @@
 		dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
 #endif /* CONFIG_BLK_DEV_TRITON */
 
-	OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+	if (HAS_IDE_CONTROL_REG)
+		OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
 	OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG);			/* Use PIO/DMA */
 	OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
 	OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
--- linux-2.1.42/drivers/block/ide.h.orig	Tue Jun 17 22:53:06 1997
+++ linux-2.1.42/drivers/block/ide.h	Thu Jun 19 23:30:55 1997
@@ -54,6 +54,12 @@
 #endif
 #endif  /* CONFIG_BLK_DEV_CMD640 */
 
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+#define HAS_IDE_CONTROL_REG	(!ide_doubler)
+#else /* !CONFIG_BLK_DEV_IDEDOUBLER */
+#define HAS_IDE_CONTROL_REG	(1)
+#endif /* !CONFIG_BLK_DEV_IDEDOUBLER */
+
 /*
  * IDE_DRIVE_CMD is used to implement many features of the hdparm utility
  */
@@ -626,5 +632,9 @@
 #else
 #define IS_PROMISE_DRIVE (0)	/* auto-NULLs out Promise code */
 #endif /* CONFIG_BLK_DEV_PROMISE */
+
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+extern int ide_doubler;
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
 
 #endif /* _IDE_H */
--- linux-2.1.42/drivers/scsi/ide-scsi.c.orig	Sun May 18 23:10:05 1997
+++ linux-2.1.42/drivers/scsi/ide-scsi.c	Thu Jun 19 22:16:46 1997
@@ -338,7 +338,8 @@
 	if (drive->using_dma && rq->bh)
 		dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
 
-	OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+	if (HAS_IDE_CONTROL_REG)
+		OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
 	OUT_BYTE (dma_ok,IDE_FEATURE_REG);
 	OUT_BYTE (bcount >> 8,IDE_BCOUNTH_REG);
 	OUT_BYTE (bcount & 0xff,IDE_BCOUNTL_REG);
--- linux-2.1.42/include/asm-m68k/ide.h.orig	Tue Jun 17 22:53:06 1997
+++ linux-2.1.42/include/asm-m68k/ide.h	Thu Jun 19 23:30:15 1997
@@ -53,8 +53,16 @@
 
 typedef unsigned char * ide_ioreg_t;
 
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+extern int ide_doubler;
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+
 #ifndef MAX_HWIFS
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+#define MAX_HWIFS	2
+#else /* !CONFIG_BLK_DEV_IDEDOUBLER */
 #define MAX_HWIFS	1
+#endif /* !CONFIG_BLK_DEV_IDEDOUBLER */
 #endif
 
 static __inline int ide_default_irq (ide_ioreg_t base)
@@ -64,10 +72,8 @@
 
 static __inline__ ide_ioreg_t ide_default_io_base (int index)
 {
-	if (index)
-		return NULL;
 #ifdef CONFIG_AMIGA
-	if (MACH_IS_AMIGA) {
+	if (MACH_IS_AMIGA && (index == 0)) {
 		if (AMIGAHW_PRESENT(A4000_IDE)) {
 			printk("Gayle IDE interface (A%d style)\n", 4000);
 			return ((ide_ioreg_t)ZTWO_VADDR(HD_BASE_A4000));
@@ -77,9 +83,27 @@
 			return ((ide_ioreg_t)ZTWO_VADDR(HD_BASE_A1200));
 		}
 	}
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+	if (MACH_IS_AMIGA && ide_doubler && (index == 1)) {
+		volatile u_short *base = NULL;
+
+		if (AMIGAHW_PRESENT(A4000_IDE))
+			base = (u_short *)ZTWO_VADDR(HD_BASE_A4000+AMI_HD_2ND_PORT);
+		else if (AMIGAHW_PRESENT(A1200_IDE))
+			base = (u_short *)ZTWO_VADDR(HD_BASE_A1200+AMI_HD_2ND_PORT);
+		if (base) {
+			u_short probe;
+			printk("+++ Probing for IDE doubler... ");
+			*base = 0xffff;
+			probe = *base;
+			printk("probe returned 0x%02x\n", probe);
+		}
+		return ((ide_ioreg_t)base);
+	}
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
 #endif /* CONFIG_AMIGA */
 #ifdef CONFIG_ATARI
-	if (MACH_IS_ATARI) {
+	if (MACH_IS_ATARI && (index == 0)) {
 		if (ATARIHW_PRESENT(IDE)) {
 			printk("Falcon IDE interface\n");
 			return ((ide_ioreg_t) ATA_HD_BASE);
@@ -101,7 +125,11 @@
 		*p++ = base + AMI_HD_HCYL;
 		*p++ = base + AMI_HD_SELECT;
 		*p++ = base + AMI_HD_STATUS;
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+		*p++ = !ide_doubler ? base + AMI_HD_CMD : NULL;
+#else /* !CONFIG_BLK_DEV_IDEDOUBLER */
 		*p++ = base + AMI_HD_CMD;
+#endif /* !CONFIG_BLK_DEV_IDEDOUBLER */
 		if (AMIGAHW_PRESENT(A4000_IDE))
 			*p++ = (ide_ioreg_t) ZTWO_VADDR(HD_A4000_IRQ);
 		else if (AMIGAHW_PRESENT(A1200_IDE))
--- linux-2.1.42/include/asm-m68k/amihdreg.h.orig	Sun Mar 31 23:50:11 1996
+++ linux-2.1.42/include/asm-m68k/amihdreg.h	Thu Jun 19 21:21:43 1997
@@ -24,6 +24,10 @@
 #define AMI_HD_STATUS	(0x1e)		/* see status-bits */
 #define AMI_HD_CMD	(0x101a)
 
+/* Offset of the secondary port for IDE doublers */
+/* Note that AMI_HD_CMD/ALTSTATUS is NOT available then! */
+#define AMI_HD_2ND_PORT	(0x1000)
+
 /* These are at different offsets from the base */
 #define HD_A4000_IRQ	(0xdd3020)	/* MSB = 1, Harddisk is source of interrupt */
 #define HD_A1200_IRQ	(0xda9000)	/* MSB = 1, Harddisk is source of interrupt */

Greetings,

						Geert

--
Geert Uytterhoeven                     Geert.Uytterhoeven@cs.kuleuven.ac.be
Wavelets, Linux/m68k on Amiga          http://www.cs.kuleuven.ac.be/~geert/
Department of Computer Science -- Katholieke Universiteit Leuven -- Belgium

