aboutsummaryrefslogtreecommitdiff
path: root/drivers/ide/legacy
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/ide/legacy
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/ide/legacy')
-rw-r--r--drivers/ide/legacy/Makefile13
-rw-r--r--drivers/ide/legacy/ali14xx.c253
-rw-r--r--drivers/ide/legacy/buddha.c235
-rw-r--r--drivers/ide/legacy/dtc2278.c165
-rw-r--r--drivers/ide/legacy/falconide.c78
-rw-r--r--drivers/ide/legacy/gayle.c186
-rw-r--r--drivers/ide/legacy/hd.c864
-rw-r--r--drivers/ide/legacy/ht6560b.c370
-rw-r--r--drivers/ide/legacy/ide-cs.c481
-rw-r--r--drivers/ide/legacy/macide.c155
-rw-r--r--drivers/ide/legacy/q40ide.c150
-rw-r--r--drivers/ide/legacy/qd65xx.c511
-rw-r--r--drivers/ide/legacy/qd65xx.h140
-rw-r--r--drivers/ide/legacy/umc8672.c183
14 files changed, 3784 insertions, 0 deletions
diff --git a/drivers/ide/legacy/Makefile b/drivers/ide/legacy/Makefile
new file mode 100644
index 00000000000..c7971061767
--- /dev/null
+++ b/drivers/ide/legacy/Makefile
@@ -0,0 +1,13 @@
+
+obj-$(CONFIG_BLK_DEV_ALI14XX) += ali14xx.o
+obj-$(CONFIG_BLK_DEV_DTC2278) += dtc2278.o
+obj-$(CONFIG_BLK_DEV_HT6560B) += ht6560b.o
+obj-$(CONFIG_BLK_DEV_QD65XX) += qd65xx.o
+obj-$(CONFIG_BLK_DEV_UMC8672) += umc8672.o
+
+obj-$(CONFIG_BLK_DEV_IDECS) += ide-cs.o
+
+# Last of all
+obj-$(CONFIG_BLK_DEV_HD) += hd.o
+
+EXTRA_CFLAGS := -Idrivers/ide
diff --git a/drivers/ide/legacy/ali14xx.c b/drivers/ide/legacy/ali14xx.c
new file mode 100644
index 00000000000..fb88711812e
--- /dev/null
+++ b/drivers/ide/legacy/ali14xx.c
@@ -0,0 +1,253 @@
+/*
+ * linux/drivers/ide/legacy/ali14xx.c Version 0.03 Feb 09, 1996
+ *
+ * Copyright (C) 1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * ALI M14xx chipset EIDE controller
+ *
+ * Works for ALI M1439/1443/1445/1487/1489 chipsets.
+ *
+ * Adapted from code developed by derekn@vw.ece.cmu.edu. -ml
+ * Derek's notes follow:
+ *
+ * I think the code should be pretty understandable,
+ * but I'll be happy to (try to) answer questions.
+ *
+ * The critical part is in the setupDrive function. The initRegisters
+ * function doesn't seem to be necessary, but the DOS driver does it, so
+ * I threw it in.
+ *
+ * I've only tested this on my system, which only has one disk. I posted
+ * it to comp.sys.linux.hardware, so maybe some other people will try it
+ * out.
+ *
+ * Derek Noonburg (derekn@ece.cmu.edu)
+ * 95-sep-26
+ *
+ * Update 96-jul-13:
+ *
+ * I've since upgraded to two disks and a CD-ROM, with no trouble, and
+ * I've also heard from several others who have used it successfully.
+ * This driver appears to work with both the 1443/1445 and the 1487/1489
+ * chipsets. I've added support for PIO mode 4 for the 1487. This
+ * seems to work just fine on the 1443 also, although I'm not sure it's
+ * advertised as supporting mode 4. (I've been running a WDC AC21200 in
+ * mode 4 for a while now with no trouble.) -Derek
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+
+/* port addresses for auto-detection */
+#define ALI_NUM_PORTS 4
+static int ports[ALI_NUM_PORTS] __initdata = {0x074, 0x0f4, 0x034, 0x0e4};
+
+/* register initialization data */
+typedef struct { u8 reg, data; } RegInitializer;
+
+static RegInitializer initData[] __initdata = {
+ {0x01, 0x0f}, {0x02, 0x00}, {0x03, 0x00}, {0x04, 0x00},
+ {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x2b}, {0x0a, 0x0f},
+ {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00}, {0x28, 0x00},
+ {0x29, 0x00}, {0x2a, 0x00}, {0x2f, 0x00}, {0x2b, 0x00},
+ {0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x30, 0x00},
+ {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, {0x34, 0xff},
+ {0x35, 0x03}, {0x00, 0x00}
+};
+
+#define ALI_MAX_PIO 4
+
+/* timing parameter registers for each drive */
+static struct { u8 reg1, reg2, reg3, reg4; } regTab[4] = {
+ {0x03, 0x26, 0x04, 0x27}, /* drive 0 */
+ {0x05, 0x28, 0x06, 0x29}, /* drive 1 */
+ {0x2b, 0x30, 0x2c, 0x31}, /* drive 2 */
+ {0x2d, 0x32, 0x2e, 0x33}, /* drive 3 */
+};
+
+static int basePort; /* base port address */
+static int regPort; /* port for register number */
+static int dataPort; /* port for register data */
+static u8 regOn; /* output to base port to access registers */
+static u8 regOff; /* output to base port to close registers */
+
+/*------------------------------------------------------------------------*/
+
+/*
+ * Read a controller register.
+ */
+static inline u8 inReg (u8 reg)
+{
+ outb_p(reg, regPort);
+ return inb(dataPort);
+}
+
+/*
+ * Write a controller register.
+ */
+static void outReg (u8 data, u8 reg)
+{
+ outb_p(reg, regPort);
+ outb_p(data, dataPort);
+}
+
+/*
+ * Set PIO mode for the specified drive.
+ * This function computes timing parameters
+ * and sets controller registers accordingly.
+ */
+static void ali14xx_tune_drive (ide_drive_t *drive, u8 pio)
+{
+ int driveNum;
+ int time1, time2;
+ u8 param1, param2, param3, param4;
+ unsigned long flags;
+ ide_pio_data_t d;
+ int bus_speed = system_bus_clock();
+
+ pio = ide_get_best_pio_mode(drive, pio, ALI_MAX_PIO, &d);
+
+ /* calculate timing, according to PIO mode */
+ time1 = d.cycle_time;
+ time2 = ide_pio_timings[pio].active_time;
+ param3 = param1 = (time2 * bus_speed + 999) / 1000;
+ param4 = param2 = (time1 * bus_speed + 999) / 1000 - param1;
+ if (pio < 3) {
+ param3 += 8;
+ param4 += 8;
+ }
+ printk(KERN_DEBUG "%s: PIO mode%d, t1=%dns, t2=%dns, cycles = %d+%d, %d+%d\n",
+ drive->name, pio, time1, time2, param1, param2, param3, param4);
+
+ /* stuff timing parameters into controller registers */
+ driveNum = (HWIF(drive)->index << 1) + drive->select.b.unit;
+ spin_lock_irqsave(&ide_lock, flags);
+ outb_p(regOn, basePort);
+ outReg(param1, regTab[driveNum].reg1);
+ outReg(param2, regTab[driveNum].reg2);
+ outReg(param3, regTab[driveNum].reg3);
+ outReg(param4, regTab[driveNum].reg4);
+ outb_p(regOff, basePort);
+ spin_unlock_irqrestore(&ide_lock, flags);
+}
+
+/*
+ * Auto-detect the IDE controller port.
+ */
+static int __init findPort (void)
+{
+ int i;
+ u8 t;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ for (i = 0; i < ALI_NUM_PORTS; ++i) {
+ basePort = ports[i];
+ regOff = inb(basePort);
+ for (regOn = 0x30; regOn <= 0x33; ++regOn) {
+ outb_p(regOn, basePort);
+ if (inb(basePort) == regOn) {
+ regPort = basePort + 4;
+ dataPort = basePort + 8;
+ t = inReg(0) & 0xf0;
+ outb_p(regOff, basePort);
+ local_irq_restore(flags);
+ if (t != 0x50)
+ return 0;
+ return 1; /* success */
+ }
+ }
+ outb_p(regOff, basePort);
+ }
+ local_irq_restore(flags);
+ return 0;
+}
+
+/*
+ * Initialize controller registers with default values.
+ */
+static int __init initRegisters (void) {
+ RegInitializer *p;
+ u8 t;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ outb_p(regOn, basePort);
+ for (p = initData; p->reg != 0; ++p)
+ outReg(p->data, p->reg);
+ outb_p(0x01, regPort);
+ t = inb(regPort) & 0x01;
+ outb_p(regOff, basePort);
+ local_irq_restore(flags);
+ return t;
+}
+
+static int __init ali14xx_probe(void)
+{
+ ide_hwif_t *hwif, *mate;
+
+ printk(KERN_DEBUG "ali14xx: base=0x%03x, regOn=0x%02x.\n",
+ basePort, regOn);
+
+ /* initialize controller registers */
+ if (!initRegisters()) {
+ printk(KERN_ERR "ali14xx: Chip initialization failed.\n");
+ return 1;
+ }
+
+ hwif = &ide_hwifs[0];
+ mate = &ide_hwifs[1];
+
+ hwif->chipset = ide_ali14xx;
+ hwif->tuneproc = &ali14xx_tune_drive;
+ hwif->mate = mate;
+
+ mate->chipset = ide_ali14xx;
+ mate->tuneproc = &ali14xx_tune_drive;
+ mate->mate = hwif;
+ mate->channel = 1;
+
+ probe_hwif_init(hwif);
+ probe_hwif_init(mate);
+
+ create_proc_ide_interfaces();
+
+ return 0;
+}
+
+/* Can be called directly from ide.c. */
+int __init ali14xx_init(void)
+{
+ /* auto-detect IDE controller port */
+ if (findPort()) {
+ if (ali14xx_probe())
+ return -ENODEV;
+ return 0;
+ }
+ printk(KERN_ERR "ali14xx: not found.\n");
+ return -ENODEV;
+}
+
+#ifdef MODULE
+module_init(ali14xx_init);
+#endif
+
+MODULE_AUTHOR("see local file");
+MODULE_DESCRIPTION("support of ALI 14XX IDE chipsets");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c
new file mode 100644
index 00000000000..0391a312287
--- /dev/null
+++ b/drivers/ide/legacy/buddha.c
@@ -0,0 +1,235 @@
+/*
+ * linux/drivers/ide/legacy/buddha.c -- Amiga Buddha, Catweasel and X-Surf IDE Driver
+ *
+ * Copyright (C) 1997, 2001 by Geert Uytterhoeven and others
+ *
+ * This driver was written based on the specifications in README.buddha and
+ * the X-Surf info from Inside_XSurf.txt available at
+ * http://www.jschoenfeld.com
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * TODO:
+ * - test it :-)
+ * - tune the timings using the speed-register
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/zorro.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+
+ /*
+ * The Buddha has 2 IDE interfaces, the Catweasel has 3, X-Surf has 2
+ */
+
+#define BUDDHA_NUM_HWIFS 2
+#define CATWEASEL_NUM_HWIFS 3
+#define XSURF_NUM_HWIFS 2
+
+ /*
+ * Bases of the IDE interfaces (relative to the board address)
+ */
+
+#define BUDDHA_BASE1 0x800
+#define BUDDHA_BASE2 0xa00
+#define BUDDHA_BASE3 0xc00
+
+#define XSURF_BASE1 0xb000 /* 2.5" Interface */
+#define XSURF_BASE2 0xd000 /* 3.5" Interface */
+
+static u_int buddha_bases[CATWEASEL_NUM_HWIFS] __initdata = {
+ BUDDHA_BASE1, BUDDHA_BASE2, BUDDHA_BASE3
+};
+
+static u_int xsurf_bases[XSURF_NUM_HWIFS] __initdata = {
+ XSURF_BASE1, XSURF_BASE2
+};
+
+
+ /*
+ * Offsets from one of the above bases
+ */
+
+#define BUDDHA_DATA 0x00
+#define BUDDHA_ERROR 0x06 /* see err-bits */
+#define BUDDHA_NSECTOR 0x0a /* nr of sectors to read/write */
+#define BUDDHA_SECTOR 0x0e /* starting sector */
+#define BUDDHA_LCYL 0x12 /* starting cylinder */
+#define BUDDHA_HCYL 0x16 /* high byte of starting cyl */
+#define BUDDHA_SELECT 0x1a /* 101dhhhh , d=drive, hhhh=head */
+#define BUDDHA_STATUS 0x1e /* see status-bits */
+#define BUDDHA_CONTROL 0x11a
+#define XSURF_CONTROL -1 /* X-Surf has no CS1* (Control/AltStat) */
+
+static int buddha_offsets[IDE_NR_PORTS] __initdata = {
+ BUDDHA_DATA, BUDDHA_ERROR, BUDDHA_NSECTOR, BUDDHA_SECTOR, BUDDHA_LCYL,
+ BUDDHA_HCYL, BUDDHA_SELECT, BUDDHA_STATUS, BUDDHA_CONTROL, -1
+};
+
+static int xsurf_offsets[IDE_NR_PORTS] __initdata = {
+ BUDDHA_DATA, BUDDHA_ERROR, BUDDHA_NSECTOR, BUDDHA_SECTOR, BUDDHA_LCYL,
+ BUDDHA_HCYL, BUDDHA_SELECT, BUDDHA_STATUS, XSURF_CONTROL, -1
+};
+
+ /*
+ * Other registers
+ */
+
+#define BUDDHA_IRQ1 0xf00 /* MSB = 1, Harddisk is source of */
+#define BUDDHA_IRQ2 0xf40 /* interrupt */
+#define BUDDHA_IRQ3 0xf80
+
+#define XSURF_IRQ1 0x7e
+#define XSURF_IRQ2 0x7e
+
+static int buddha_irqports[CATWEASEL_NUM_HWIFS] __initdata = {
+ BUDDHA_IRQ1, BUDDHA_IRQ2, BUDDHA_IRQ3
+};
+
+static int xsurf_irqports[XSURF_NUM_HWIFS] __initdata = {
+ XSURF_IRQ1, XSURF_IRQ2
+};
+
+#define BUDDHA_IRQ_MR 0xfc0 /* master interrupt enable */
+
+
+ /*
+ * Board information
+ */
+
+typedef enum BuddhaType_Enum {
+ BOARD_BUDDHA, BOARD_CATWEASEL, BOARD_XSURF
+} BuddhaType;
+
+
+ /*
+ * Check and acknowledge the interrupt status
+ */
+
+static int buddha_ack_intr(ide_hwif_t *hwif)
+{
+ unsigned char ch;
+
+ ch = z_readb(hwif->io_ports[IDE_IRQ_OFFSET]);
+ if (!(ch & 0x80))
+ return 0;
+ return 1;
+}
+
+static int xsurf_ack_intr(ide_hwif_t *hwif)
+{
+ unsigned char ch;
+
+ ch = z_readb(hwif->io_ports[IDE_IRQ_OFFSET]);
+ /* X-Surf needs a 0 written to IRQ register to ensure ISA bit A11 stays at 0 */
+ z_writeb(0, hwif->io_ports[IDE_IRQ_OFFSET]);
+ if (!(ch & 0x80))
+ return 0;
+ return 1;
+}
+
+ /*
+ * Probe for a Buddha or Catweasel IDE interface
+ */
+
+void __init buddha_init(void)
+{
+ hw_regs_t hw;
+ ide_hwif_t *hwif;
+ int i, index;
+
+ struct zorro_dev *z = NULL;
+ u_long buddha_board = 0;
+ BuddhaType type;
+ int buddha_num_hwifs;
+
+ while ((z = zorro_find_device(ZORRO_WILDCARD, z))) {
+ unsigned long board;
+ if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) {
+ buddha_num_hwifs = BUDDHA_NUM_HWIFS;
+ type=BOARD_BUDDHA;
+ } else if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL) {
+ buddha_num_hwifs = CATWEASEL_NUM_HWIFS;
+ type=BOARD_CATWEASEL;
+ } else if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF) {
+ buddha_num_hwifs = XSURF_NUM_HWIFS;
+ type=BOARD_XSURF;
+ } else
+ continue;
+
+ board = z->resource.start;
+
+/*
+ * FIXME: we now have selectable mmio v/s iomio transports.
+ */
+
+ if(type != BOARD_XSURF) {
+ if (!request_mem_region(board+BUDDHA_BASE1, 0x800, "IDE"))
+ continue;
+ } else {
+ if (!request_mem_region(board+XSURF_BASE1, 0x1000, "IDE"))
+ continue;
+ if (!request_mem_region(board+XSURF_BASE2, 0x1000, "IDE"))
+ goto fail_base2;
+ if (!request_mem_region(board+XSURF_IRQ1, 0x8, "IDE")) {
+ release_mem_region(board+XSURF_BASE2, 0x1000);
+fail_base2:
+ release_mem_region(board+XSURF_BASE1, 0x1000);
+ continue;
+ }
+ }
+ buddha_board = ZTWO_VADDR(board);
+
+ /* write to BUDDHA_IRQ_MR to enable the board IRQ */
+ /* X-Surf doesn't have this. IRQs are always on */
+ if (type != BOARD_XSURF)
+ z_writeb(0, buddha_board+BUDDHA_IRQ_MR);
+
+ for(i=0;i<buddha_num_hwifs;i++) {
+ if(type != BOARD_XSURF) {
+ ide_setup_ports(&hw, (buddha_board+buddha_bases[i]),
+ buddha_offsets, 0,
+ (buddha_board+buddha_irqports[i]),
+ buddha_ack_intr,
+// budda_iops,
+ IRQ_AMIGA_PORTS);
+ } else {
+ ide_setup_ports(&hw, (buddha_board+xsurf_bases[i]),
+ xsurf_offsets, 0,
+ (buddha_board+xsurf_irqports[i]),
+ xsurf_ack_intr,
+// xsurf_iops,
+ IRQ_AMIGA_PORTS);
+ }
+
+ index = ide_register_hw(&hw, &hwif);
+ if (index != -1) {
+ hwif->mmio = 2;
+ printk("ide%d: ", index);
+ switch(type) {
+ case BOARD_BUDDHA:
+ printk("Buddha");
+ break;
+ case BOARD_CATWEASEL:
+ printk("Catweasel");
+ break;
+ case BOARD_XSURF:
+ printk("X-Surf");
+ break;
+ }
+ printk(" IDE interface\n");
+ }
+ }
+ }
+}
diff --git a/drivers/ide/legacy/dtc2278.c b/drivers/ide/legacy/dtc2278.c
new file mode 100644
index 00000000000..20eb5b872ca
--- /dev/null
+++ b/drivers/ide/legacy/dtc2278.c
@@ -0,0 +1,165 @@
+/*
+ * linux/drivers/ide/legacy/dtc2278.c Version 0.02 Feb 10, 1996
+ *
+ * Copyright (C) 1996 Linus Torvalds & author (see below)
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+
+/*
+ * Changing this #undef to #define may solve start up problems in some systems.
+ */
+#undef ALWAYS_SET_DTC2278_PIO_MODE
+
+/*
+ * From: andy@cercle.cts.com (Dyan Wile)
+ *
+ * Below is a patch for DTC-2278 - alike software-programmable controllers
+ * The code enables the secondary IDE controller and the PIO4 (3?) timings on
+ * the primary (EIDE). You may probably have to enable the 32-bit support to
+ * get the full speed. You better get the disk interrupts disabled ( hdparm -u0
+ * /dev/hd.. ) for the drives connected to the EIDE interface. (I get my
+ * filesystem corrupted with -u1, but under heavy disk load only :-)
+ *
+ * This card is now forced to use the "serialize" feature,
+ * and irq-unmasking is disallowed. If io_32bit is enabled,
+ * it must be done for BOTH drives on each interface.
+ *
+ * This code was written for the DTC2278E, but might work with any of these:
+ *
+ * DTC2278S has only a single IDE interface.
+ * DTC2278D has two IDE interfaces and is otherwise identical to the S version.
+ * DTC2278E also has serial ports and a printer port
+ * DTC2278EB: has onboard BIOS, and "works like a charm" -- Kent Bradford <kent@theory.caltech.edu>
+ *
+ * There may be a fourth controller type. The S and D versions use the
+ * Winbond chip, and I think the E version does also.
+ *
+ */
+
+static void sub22 (char b, char c)
+{
+ int i;
+
+ for(i = 0; i < 3; ++i) {
+ inb(0x3f6);
+ outb_p(b,0xb0);
+ inb(0x3f6);
+ outb_p(c,0xb4);
+ inb(0x3f6);
+ if(inb(0xb4) == c) {
+ outb_p(7,0xb0);
+ inb(0x3f6);
+ return; /* success */
+ }
+ }
+}
+
+static void tune_dtc2278 (ide_drive_t *drive, u8 pio)
+{
+ unsigned long flags;
+
+ pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+
+ if (pio >= 3) {
+ spin_lock_irqsave(&ide_lock, flags);
+ /*
+ * This enables PIO mode4 (3?) on the first interface
+ */
+ sub22(1,0xc3);
+ sub22(0,0xa0);
+ spin_unlock_irqrestore(&ide_lock, flags);
+ } else {
+ /* we don't know how to set it back again.. */
+ }
+
+ /*
+ * 32bit I/O has to be enabled for *both* drives at the same time.
+ */
+ drive->io_32bit = 1;
+ HWIF(drive)->drives[!drive->select.b.unit].io_32bit = 1;
+}
+
+static int __init probe_dtc2278(void)
+{
+ unsigned long flags;
+ ide_hwif_t *hwif, *mate;
+
+ hwif = &ide_hwifs[0];
+ mate = &ide_hwifs[1];
+
+ if (hwif->chipset != ide_unknown || mate->chipset != ide_unknown)
+ return 1;
+
+ local_irq_save(flags);
+ /*
+ * This enables the second interface
+ */
+ outb_p(4,0xb0);
+ inb(0x3f6);
+ outb_p(0x20,0xb4);
+ inb(0x3f6);
+#ifdef ALWAYS_SET_DTC2278_PIO_MODE
+ /*
+ * This enables PIO mode4 (3?) on the first interface
+ * and may solve start-up problems for some people.
+ */
+ sub22(1,0xc3);
+ sub22(0,0xa0);
+#endif
+ local_irq_restore(flags);
+
+ hwif->serialized = 1;
+ hwif->chipset = ide_dtc2278;
+ hwif->tuneproc = &tune_dtc2278;
+ hwif->drives[0].no_unmask = 1;
+ hwif->drives[1].no_unmask = 1;
+ hwif->mate = mate;
+
+ mate->serialized = 1;
+ mate->chipset = ide_dtc2278;
+ mate->drives[0].no_unmask = 1;
+ mate->drives[1].no_unmask = 1;
+ mate->mate = hwif;
+ mate->channel = 1;
+
+ probe_hwif_init(hwif);
+ probe_hwif_init(mate);
+
+ create_proc_ide_interfaces();
+
+ return 0;
+}
+
+/* Can be called directly from ide.c. */
+int __init dtc2278_init(void)
+{
+ if (probe_dtc2278()) {
+ printk(KERN_ERR "dtc2278: ide interfaces already in use!\n");
+ return -EBUSY;
+ }
+ return 0;
+}
+
+#ifdef MODULE
+module_init(dtc2278_init);
+#endif
+
+MODULE_AUTHOR("See Local File");
+MODULE_DESCRIPTION("support of DTC-2278 VLB IDE chipsets");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c
new file mode 100644
index 00000000000..a9f2cd5bb81
--- /dev/null
+++ b/drivers/ide/legacy/falconide.c
@@ -0,0 +1,78 @@
+/*
+ * linux/drivers/ide/legacy/falconide.c -- Atari Falcon IDE Driver
+ *
+ * Created 12 Jul 1997 by Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+
+#include <asm/setup.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atari_stdma.h>
+
+
+ /*
+ * Base of the IDE interface
+ */
+
+#define ATA_HD_BASE 0xfff00000
+
+ /*
+ * Offsets from the above base
+ */
+
+#define ATA_HD_DATA 0x00
+#define ATA_HD_ERROR 0x05 /* see err-bits */
+#define ATA_HD_NSECTOR 0x09 /* nr of sectors to read/write */
+#define ATA_HD_SECTOR 0x0d /* starting sector */
+#define ATA_HD_LCYL 0x11 /* starting cylinder */
+#define ATA_HD_HCYL 0x15 /* high byte of starting cyl */
+#define ATA_HD_SELECT 0x19 /* 101dhhhh , d=drive, hhhh=head */
+#define ATA_HD_STATUS 0x1d /* see status-bits */
+#define ATA_HD_CONTROL 0x39
+
+static int falconide_offsets[IDE_NR_PORTS] __initdata = {
+ ATA_HD_DATA, ATA_HD_ERROR, ATA_HD_NSECTOR, ATA_HD_SECTOR, ATA_HD_LCYL,
+ ATA_HD_HCYL, ATA_HD_SELECT, ATA_HD_STATUS, ATA_HD_CONTROL, -1
+};
+
+
+ /*
+ * falconide_intr_lock is used to obtain access to the IDE interrupt,
+ * which is shared between several drivers.
+ */
+
+int falconide_intr_lock;
+
+
+ /*
+ * Probe for a Falcon IDE interface
+ */
+
+void __init falconide_init(void)
+{
+ if (MACH_IS_ATARI && ATARIHW_PRESENT(IDE)) {
+ hw_regs_t hw;
+ int index;
+
+ ide_setup_ports(&hw, ATA_HD_BASE, falconide_offsets,
+ 0, 0, NULL,
+// falconide_iops,
+ IRQ_MFP_IDE);
+ index = ide_register_hw(&hw, NULL);
+
+ if (index != -1)
+ printk("ide%d: Falcon IDE interface\n", index);
+ }
+}
diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c
new file mode 100644
index 00000000000..3fac3e9ec47
--- /dev/null
+++ b/drivers/ide/legacy/gayle.c
@@ -0,0 +1,186 @@
+/*
+ * linux/drivers/ide/legacy/gayle.c -- Amiga Gayle IDE Driver
+ *
+ * Created 9 Jul 1997 by Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+#include <linux/zorro.h>
+
+#include <asm/setup.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/amigayle.h>
+
+
+ /*
+ * Bases of the IDE interfaces
+ */
+
+#define GAYLE_BASE_4000 0xdd2020 /* A4000/A4000T */
+#define GAYLE_BASE_1200 0xda0000 /* A1200/A600 and E-Matrix 530 */
+
+ /*
+ * Offsets from one of the above bases
+ */
+
+#define GAYLE_DATA 0x00
+#define GAYLE_ERROR 0x06 /* see err-bits */
+#define GAYLE_NSECTOR 0x0a /* nr of sectors to read/write */
+#define GAYLE_SECTOR 0x0e /* starting sector */
+#define GAYLE_LCYL 0x12 /* starting cylinder */
+#define GAYLE_HCYL 0x16 /* high byte of starting cyl */
+#define GAYLE_SELECT 0x1a /* 101dhhhh , d=drive, hhhh=head */
+#define GAYLE_STATUS 0x1e /* see status-bits */
+#define GAYLE_CONTROL 0x101a
+
+static int gayle_offsets[IDE_NR_PORTS] __initdata = {
+ GAYLE_DATA, GAYLE_ERROR, GAYLE_NSECTOR, GAYLE_SECTOR, GAYLE_LCYL,
+ GAYLE_HCYL, GAYLE_SELECT, GAYLE_STATUS, -1, -1
+};
+
+
+ /*
+ * These are at different offsets from the base
+ */
+
+#define GAYLE_IRQ_4000 0xdd3020 /* MSB = 1, Harddisk is source of */
+#define GAYLE_IRQ_1200 0xda9000 /* interrupt */
+
+
+ /*
+ * Offset of the secondary port for IDE doublers
+ * Note that GAYLE_CONTROL is NOT available then!
+ */
+
+#define GAYLE_NEXT_PORT 0x1000
+
+#ifndef CONFIG_BLK_DEV_IDEDOUBLER
+#define GAYLE_NUM_HWIFS 1
+#define GAYLE_NUM_PROBE_HWIFS GAYLE_NUM_HWIFS
+#define GAYLE_HAS_CONTROL_REG 1
+#define GAYLE_IDEREG_SIZE 0x2000
+#else /* CONFIG_BLK_DEV_IDEDOUBLER */
+#define GAYLE_NUM_HWIFS 2
+#define GAYLE_NUM_PROBE_HWIFS (ide_doubler ? GAYLE_NUM_HWIFS : \
+ GAYLE_NUM_HWIFS-1)
+#define GAYLE_HAS_CONTROL_REG (!ide_doubler)
+#define GAYLE_IDEREG_SIZE (ide_doubler ? 0x1000 : 0x2000)
+int ide_doubler = 0; /* support IDE doublers? */
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+
+
+ /*
+ * Check and acknowledge the interrupt status
+ */
+
+static int gayle_ack_intr_a4000(ide_hwif_t *hwif)
+{
+ unsigned char ch;
+
+ ch = z_readb(hwif->io_ports[IDE_IRQ_OFFSET]);
+ if (!(ch & GAYLE_IRQ_IDE))
+ return 0;
+ return 1;
+}
+
+static int gayle_ack_intr_a1200(ide_hwif_t *hwif)
+{
+ unsigned char ch;
+
+ ch = z_readb(hwif->io_ports[IDE_IRQ_OFFSET]);
+ if (!(ch & GAYLE_IRQ_IDE))
+ return 0;
+ (void)z_readb(hwif->io_ports[IDE_STATUS_OFFSET]);
+ z_writeb(0x7c, hwif->io_ports[IDE_IRQ_OFFSET]);
+ return 1;
+}
+
+ /*
+ * Probe for a Gayle IDE interface (and optionally for an IDE doubler)
+ */
+
+void __init gayle_init(void)
+{
+ int a4000, i;
+
+ if (!MACH_IS_AMIGA)
+ return;
+
+ if ((a4000 = AMIGAHW_PRESENT(A4000_IDE)) || AMIGAHW_PRESENT(A1200_IDE))
+ goto found;
+
+#ifdef CONFIG_ZORRO
+ if (zorro_find_device(ZORRO_PROD_MTEC_VIPER_MK_V_E_MATRIX_530_SCSI_IDE,
+ NULL))
+ goto found;
+#endif
+ return;
+
+found:
+ for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) {
+ unsigned long base, ctrlport, irqport;
+ ide_ack_intr_t *ack_intr;
+ hw_regs_t hw;
+ ide_hwif_t *hwif;
+ int index;
+ unsigned long phys_base, res_start, res_n;
+
+ if (a4000) {
+ phys_base = GAYLE_BASE_4000;
+ irqport = (unsigned long)ZTWO_VADDR(GAYLE_IRQ_4000);
+ ack_intr = gayle_ack_intr_a4000;
+ } else {
+ phys_base = GAYLE_BASE_1200;
+ irqport = (unsigned long)ZTWO_VADDR(GAYLE_IRQ_1200);
+ ack_intr = gayle_ack_intr_a1200;
+ }
+/*
+ * FIXME: we now have selectable modes between mmio v/s iomio
+ */
+
+ phys_base += i*GAYLE_NEXT_PORT;
+
+ res_start = ((unsigned long)phys_base) & ~(GAYLE_NEXT_PORT-1);
+ res_n = GAYLE_IDEREG_SIZE;
+
+ if (!request_mem_region(res_start, res_n, "IDE"))
+ continue;
+
+ base = (unsigned long)ZTWO_VADDR(phys_base);
+ ctrlport = GAYLE_HAS_CONTROL_REG ? (base + GAYLE_CONTROL) : 0;
+
+ ide_setup_ports(&hw, base, gayle_offsets,
+ ctrlport, irqport, ack_intr,
+// &gayle_iops,
+ IRQ_AMIGA_PORTS);
+
+ index = ide_register_hw(&hw, &hwif);
+ if (index != -1) {
+ hwif->mmio = 2;
+ switch (i) {
+ case 0:
+ printk("ide%d: Gayle IDE interface (A%d style)\n", index,
+ a4000 ? 4000 : 1200);
+ break;
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+ case 1:
+ printk("ide%d: IDE doubler\n", index);
+ break;
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+ }
+ } else
+ release_mem_region(res_start, res_n);
+ }
+}
diff --git a/drivers/ide/legacy/hd.c b/drivers/ide/legacy/hd.c
new file mode 100644
index 00000000000..c4090550ec1
--- /dev/null
+++ b/drivers/ide/legacy/hd.c
@@ -0,0 +1,864 @@
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * This is the low-level hd interrupt support. It traverses the
+ * request-list, using interrupts to jump between functions. As
+ * all the functions are called within interrupts, we may not
+ * sleep. Special care is recommended.
+ *
+ * modified by Drew Eckhardt to check nr of hd's from the CMOS.
+ *
+ * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ * in the early extended-partition checks and added DM partitions
+ *
+ * IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
+ * and general streamlining by Mark Lord.
+ *
+ * Removed 99% of above. Use Mark's ide driver for those options.
+ * This is now a lightweight ST-506 driver. (Paul Gortmaker)
+ *
+ * Modified 1995 Russell King for ARM processor.
+ *
+ * Bugfix: max_sectors must be <= 255 or the wheels tend to come
+ * off in a hurry once you queue things up - Paul G. 02/2001
+ */
+
+/* Uncomment the following if you want verbose error reports. */
+/* #define VERBOSE_ERRORS */
+
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/genhd.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/mc146818rtc.h> /* CMOS defines */
+#include <linux/init.h>
+#include <linux/blkpg.h>
+#include <linux/hdreg.h>
+
+#define REALLY_SLOW_IO
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#ifdef __arm__
+#undef HD_IRQ
+#endif
+#include <asm/irq.h>
+#ifdef __arm__
+#define HD_IRQ IRQ_HARDDISK
+#endif
+
+/* Hd controller regster ports */
+
+#define HD_DATA 0x1f0 /* _CTL when writing */
+#define HD_ERROR 0x1f1 /* see err-bits */
+#define HD_NSECTOR 0x1f2 /* nr of sectors to read/write */
+#define HD_SECTOR 0x1f3 /* starting sector */
+#define HD_LCYL 0x1f4 /* starting cylinder */
+#define HD_HCYL 0x1f5 /* high byte of starting cyl */
+#define HD_CURRENT 0x1f6 /* 101dhhhh , d=drive, hhhh=head */
+#define HD_STATUS 0x1f7 /* see status-bits */
+#define HD_FEATURE HD_ERROR /* same io address, read=error, write=feature */
+#define HD_PRECOMP HD_FEATURE /* obsolete use of this port - predates IDE */
+#define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */
+
+#define HD_CMD 0x3f6 /* used for resets */
+#define HD_ALTSTATUS 0x3f6 /* same as HD_STATUS but doesn't clear irq */
+
+/* Bits of HD_STATUS */
+#define ERR_STAT 0x01
+#define INDEX_STAT 0x02
+#define ECC_STAT 0x04 /* Corrected error */
+#define DRQ_STAT 0x08
+#define SEEK_STAT 0x10
+#define SERVICE_STAT SEEK_STAT
+#define WRERR_STAT 0x20
+#define READY_STAT 0x40
+#define BUSY_STAT 0x80
+
+/* Bits for HD_ERROR */
+#define MARK_ERR 0x01 /* Bad address mark */
+#define TRK0_ERR 0x02 /* couldn't find track 0 */
+#define ABRT_ERR 0x04 /* Command aborted */
+#define MCR_ERR 0x08 /* media change request */
+#define ID_ERR 0x10 /* ID field not found */
+#define MC_ERR 0x20 /* media changed */
+#define ECC_ERR 0x40 /* Uncorrectable ECC error */
+#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */
+#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */
+
+static DEFINE_SPINLOCK(hd_lock);
+static struct request_queue *hd_queue;
+
+#define MAJOR_NR HD_MAJOR
+#define QUEUE (hd_queue)
+#define CURRENT elv_next_request(hd_queue)
+
+#define TIMEOUT_VALUE (6*HZ)
+#define HD_DELAY 0
+
+#define MAX_ERRORS 16 /* Max read/write errors/sector */
+#define RESET_FREQ 8 /* Reset controller every 8th retry */
+#define RECAL_FREQ 4 /* Recalibrate every 4th retry */
+#define MAX_HD 2
+
+#define STAT_OK (READY_STAT|SEEK_STAT)
+#define OK_STATUS(s) (((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK)
+
+static void recal_intr(void);
+static void bad_rw_intr(void);
+
+static int reset;
+static int hd_error;
+
+/*
+ * This struct defines the HD's and their types.
+ */
+struct hd_i_struct {
+ unsigned int head,sect,cyl,wpcom,lzone,ctl;
+ int unit;
+ int recalibrate;
+ int special_op;
+};
+
+#ifdef HD_TYPE
+static struct hd_i_struct hd_info[] = { HD_TYPE };
+static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct)));
+#else
+static struct hd_i_struct hd_info[MAX_HD];
+static int NR_HD;
+#endif
+
+static struct gendisk *hd_gendisk[MAX_HD];
+
+static struct timer_list device_timer;
+
+#define TIMEOUT_VALUE (6*HZ)
+
+#define SET_TIMER \
+ do { \
+ mod_timer(&device_timer, jiffies + TIMEOUT_VALUE); \
+ } while (0)
+
+static void (*do_hd)(void) = NULL;
+#define SET_HANDLER(x) \
+if ((do_hd = (x)) != NULL) \
+ SET_TIMER; \
+else \
+ del_timer(&device_timer);
+
+
+#if (HD_DELAY > 0)
+unsigned long last_req;
+
+unsigned long read_timer(void)
+{
+ extern spinlock_t i8253_lock;
+ unsigned long t, flags;
+ int i;
+
+ spin_lock_irqsave(&i8253_lock, flags);
+ t = jiffies * 11932;
+ outb_p(0, 0x43);
+ i = inb_p(0x40);
+ i |= inb(0x40) << 8;
+ spin_unlock_irqrestore(&i8253_lock, flags);
+ return(t - i);
+}
+#endif
+
+static void __init hd_setup(char *str, int *ints)
+{
+ int hdind = 0;
+
+ if (ints[0] != 3)
+ return;
+ if (hd_info[0].head != 0)
+ hdind=1;
+ hd_info[hdind].head = ints[2];
+ hd_info[hdind].sect = ints[3];
+ hd_info[hdind].cyl = ints[1];
+ hd_info[hdind].wpcom = 0;
+ hd_info[hdind].lzone = ints[1];
+ hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
+ NR_HD = hdind+1;
+}
+
+static void dump_status (const char *msg, unsigned int stat)
+{
+ char *name = "hd?";
+ if (CURRENT)
+ name = CURRENT->rq_disk->disk_name;
+
+#ifdef VERBOSE_ERRORS
+ printk("%s: %s: status=0x%02x { ", name, msg, stat & 0xff);
+ if (stat & BUSY_STAT) printk("Busy ");
+ if (stat & READY_STAT) printk("DriveReady ");
+ if (stat & WRERR_STAT) printk("WriteFault ");
+ if (stat & SEEK_STAT) printk("SeekComplete ");
+ if (stat & DRQ_STAT) printk("DataRequest ");
+ if (stat & ECC_STAT) printk("CorrectedError ");
+ if (stat & INDEX_STAT) printk("Index ");
+ if (stat & ERR_STAT) printk("Error ");
+ printk("}\n");
+ if ((stat & ERR_STAT) == 0) {
+ hd_error = 0;
+ } else {
+ hd_error = inb(HD_ERROR);
+ printk("%s: %s: error=0x%02x { ", name, msg, hd_error & 0xff);
+ if (hd_error & BBD_ERR) printk("BadSector ");
+ if (hd_error & ECC_ERR) printk("UncorrectableError ");
+ if (hd_error & ID_ERR) printk("SectorIdNotFound ");
+ if (hd_error & ABRT_ERR) printk("DriveStatusError ");
+ if (hd_error & TRK0_ERR) printk("TrackZeroNotFound ");
+ if (hd_error & MARK_ERR) printk("AddrMarkNotFound ");
+ printk("}");
+ if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
+ printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),
+ inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));
+ if (CURRENT)
+ printk(", sector=%ld", CURRENT->sector);
+ }
+ printk("\n");
+ }
+#else
+ printk("%s: %s: status=0x%02x.\n", name, msg, stat & 0xff);
+ if ((stat & ERR_STAT) == 0) {
+ hd_error = 0;
+ } else {
+ hd_error = inb(HD_ERROR);
+ printk("%s: %s: error=0x%02x.\n", name, msg, hd_error & 0xff);
+ }
+#endif
+}
+
+static void check_status(void)
+{
+ int i = inb_p(HD_STATUS);
+
+ if (!OK_STATUS(i)) {
+ dump_status("check_status", i);
+ bad_rw_intr();
+ }
+}
+
+static int controller_busy(void)
+{
+ int retries = 100000;
+ unsigned char status;
+
+ do {
+ status = inb_p(HD_STATUS);
+ } while ((status & BUSY_STAT) && --retries);
+ return status;
+}
+
+static int status_ok(void)
+{
+ unsigned char status = inb_p(HD_STATUS);
+
+ if (status & BUSY_STAT)
+ return 1; /* Ancient, but does it make sense??? */
+ if (status & WRERR_STAT)
+ return 0;
+ if (!(status & READY_STAT))
+ return 0;
+ if (!(status & SEEK_STAT))
+ return 0;
+ return 1;
+}
+
+static int controller_ready(unsigned int drive, unsigned int head)
+{
+ int retry = 100;
+
+ do {
+ if (controller_busy() & BUSY_STAT)
+ return 0;
+ outb_p(0xA0 | (drive<<4) | head, HD_CURRENT);
+ if (status_ok())
+ return 1;
+ } while (--retry);
+ return 0;
+}
+
+
+static void hd_out(struct hd_i_struct *disk,
+ unsigned int nsect,
+ unsigned int sect,
+ unsigned int head,
+ unsigned int cyl,
+ unsigned int cmd,
+ void (*intr_addr)(void))
+{
+ unsigned short port;
+
+#if (HD_DELAY > 0)
+ while (read_timer() - last_req < HD_DELAY)
+ /* nothing */;
+#endif
+ if (reset)
+ return;
+ if (!controller_ready(disk->unit, head)) {
+ reset = 1;
+ return;
+ }
+ SET_HANDLER(intr_addr);
+ outb_p(disk->ctl,HD_CMD);
+ port=HD_DATA;
+ outb_p(disk->wpcom>>2,++port);
+ outb_p(nsect,++port);
+ outb_p(sect,++port);
+ outb_p(cyl,++port);
+ outb_p(cyl>>8,++port);
+ outb_p(0xA0|(disk->unit<<4)|head,++port);
+ outb_p(cmd,++port);
+}
+
+static void hd_request (void);
+
+static int drive_busy(void)
+{
+ unsigned int i;
+ unsigned char c;
+
+ for (i = 0; i < 500000 ; i++) {
+ c = inb_p(HD_STATUS);
+ if ((c & (BUSY_STAT | READY_STAT | SEEK_STAT)) == STAT_OK)
+ return 0;
+ }
+ dump_status("reset timed out", c);
+ return 1;
+}
+
+static void reset_controller(void)
+{
+ int i;
+
+ outb_p(4,HD_CMD);
+ for(i = 0; i < 1000; i++) barrier();
+ outb_p(hd_info[0].ctl & 0x0f,HD_CMD);
+ for(i = 0; i < 1000; i++) barrier();
+ if (drive_busy())
+ printk("hd: controller still busy\n");
+ else if ((hd_error = inb(HD_ERROR)) != 1)
+ printk("hd: controller reset failed: %02x\n",hd_error);
+}
+
+static void reset_hd(void)
+{
+ static int i;
+
+repeat:
+ if (reset) {
+ reset = 0;
+ i = -1;
+ reset_controller();
+ } else {
+ check_status();
+ if (reset)
+ goto repeat;
+ }
+ if (++i < NR_HD) {
+ struct hd_i_struct *disk = &hd_info[i];
+ disk->special_op = disk->recalibrate = 1;
+ hd_out(disk,disk->sect,disk->sect,disk->head-1,
+ disk->cyl,WIN_SPECIFY,&reset_hd);
+ if (reset)
+ goto repeat;
+ } else
+ hd_request();
+}
+
+/*
+ * Ok, don't know what to do with the unexpected interrupts: on some machines
+ * doing a reset and a retry seems to result in an eternal loop. Right now I
+ * ignore it, and just set the timeout.
+ *
+ * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
+ * drive enters "idle", "standby", or "sleep" mode, so if the status looks
+ * "good", we just ignore the interrupt completely.
+ */
+static void unexpected_hd_interrupt(void)
+{
+ unsigned int stat = inb_p(HD_STATUS);
+
+ if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) {
+ dump_status ("unexpected interrupt", stat);
+ SET_TIMER;
+ }
+}
+
+/*
+ * bad_rw_intr() now tries to be a bit smarter and does things
+ * according to the error returned by the controller.
+ * -Mika Liljeberg (liljeber@cs.Helsinki.FI)
+ */
+static void bad_rw_intr(void)
+{
+ struct request *req = CURRENT;
+ if (req != NULL) {
+ struct hd_i_struct *disk = req->rq_disk->private_data;
+ if (++req->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
+ end_request(req, 0);
+ disk->special_op = disk->recalibrate = 1;
+ } else if (req->errors % RESET_FREQ == 0)
+ reset = 1;
+ else if ((hd_error & TRK0_ERR) || req->errors % RECAL_FREQ == 0)
+ disk->special_op = disk->recalibrate = 1;
+ /* Otherwise just retry */
+ }
+}
+
+static inline int wait_DRQ(void)
+{
+ int retries = 100000, stat;
+
+ while (--retries > 0)
+ if ((stat = inb_p(HD_STATUS)) & DRQ_STAT)
+ return 0;
+ dump_status("wait_DRQ", stat);
+ return -1;
+}
+
+static void read_intr(void)
+{
+ struct request *req;
+ int i, retries = 100000;
+
+ do {
+ i = (unsigned) inb_p(HD_STATUS);
+ if (i & BUSY_STAT)
+ continue;
+ if (!OK_STATUS(i))
+ break;
+ if (i & DRQ_STAT)
+ goto ok_to_read;
+ } while (--retries > 0);
+ dump_status("read_intr", i);
+ bad_rw_intr();
+ hd_request();
+ return;
+ok_to_read:
+ req = CURRENT;
+ insw(HD_DATA,req->buffer,256);
+ req->sector++;
+ req->buffer += 512;
+ req->errors = 0;
+ i = --req->nr_sectors;
+ --req->current_nr_sectors;
+#ifdef DEBUG
+ printk("%s: read: sector %ld, remaining = %ld, buffer=%p\n",
+ req->rq_disk->disk_name, req->sector, req->nr_sectors,
+ req->buffer+512));
+#endif
+ if (req->current_nr_sectors <= 0)
+ end_request(req, 1);
+ if (i > 0) {
+ SET_HANDLER(&read_intr);
+ return;
+ }
+ (void) inb_p(HD_STATUS);
+#if (HD_DELAY > 0)
+ last_req = read_timer();
+#endif
+ if (elv_next_request(QUEUE))
+ hd_request();
+ return;
+}
+
+static void write_intr(void)
+{
+ struct request *req = CURRENT;
+ int i;
+ int retries = 100000;
+
+ do {
+ i = (unsigned) inb_p(HD_STATUS);
+ if (i & BUSY_STAT)
+ continue;
+ if (!OK_STATUS(i))
+ break;
+ if ((req->nr_sectors <= 1) || (i & DRQ_STAT))
+ goto ok_to_write;
+ } while (--retries > 0);
+ dump_status("write_intr", i);
+ bad_rw_intr();
+ hd_request();
+ return;
+ok_to_write:
+ req->sector++;
+ i = --req->nr_sectors;
+ --req->current_nr_sectors;
+ req->buffer += 512;
+ if (!i || (req->bio && req->current_nr_sectors <= 0))
+ end_request(req, 1);
+ if (i > 0) {
+ SET_HANDLER(&write_intr);
+ outsw(HD_DATA,req->buffer,256);
+ local_irq_enable();
+ } else {
+#if (HD_DELAY > 0)
+ last_req = read_timer();
+#endif
+ hd_request();
+ }
+ return;
+}
+
+static void recal_intr(void)
+{
+ check_status();
+#if (HD_DELAY > 0)
+ last_req = read_timer();
+#endif
+ hd_request();
+}
+
+/*
+ * This is another of the error-routines I don't know what to do with. The
+ * best idea seems to just set reset, and start all over again.
+ */
+static void hd_times_out(unsigned long dummy)
+{
+ char *name;
+
+ do_hd = NULL;
+
+ if (!CURRENT)
+ return;
+
+ disable_irq(HD_IRQ);
+ local_irq_enable();
+ reset = 1;
+ name = CURRENT->rq_disk->disk_name;
+ printk("%s: timeout\n", name);
+ if (++CURRENT->errors >= MAX_ERRORS) {
+#ifdef DEBUG
+ printk("%s: too many errors\n", name);
+#endif
+ end_request(CURRENT, 0);
+ }
+ local_irq_disable();
+ hd_request();
+ enable_irq(HD_IRQ);
+}
+
+static int do_special_op(struct hd_i_struct *disk, struct request *req)
+{
+ if (disk->recalibrate) {
+ disk->recalibrate = 0;
+ hd_out(disk,disk->sect,0,0,0,WIN_RESTORE,&recal_intr);
+ return reset;
+ }
+ if (disk->head > 16) {
+ printk ("%s: cannot handle device with more than 16 heads - giving up\n", req->rq_disk->disk_name);
+ end_request(req, 0);
+ }
+ disk->special_op = 0;
+ return 1;
+}
+
+/*
+ * The driver enables interrupts as much as possible. In order to do this,
+ * (a) the device-interrupt is disabled before entering hd_request(),
+ * and (b) the timeout-interrupt is disabled before the sti().
+ *
+ * Interrupts are still masked (by default) whenever we are exchanging
+ * data/cmds with a drive, because some drives seem to have very poor
+ * tolerance for latency during I/O. The IDE driver has support to unmask
+ * interrupts for non-broken hardware, so use that driver if required.
+ */
+static void hd_request(void)
+{
+ unsigned int block, nsect, sec, track, head, cyl;
+ struct hd_i_struct *disk;
+ struct request *req;
+
+ if (do_hd)
+ return;
+repeat:
+ del_timer(&device_timer);
+ local_irq_enable();
+
+ req = CURRENT;
+ if (!req) {
+ do_hd = NULL;
+ return;
+ }
+
+ if (reset) {
+ local_irq_disable();
+ reset_hd();
+ return;
+ }
+ disk = req->rq_disk->private_data;
+ block = req->sector;
+ nsect = req->nr_sectors;
+ if (block >= get_capacity(req->rq_disk) ||
+ ((block+nsect) > get_capacity(req->rq_disk))) {
+ printk("%s: bad access: block=%d, count=%d\n",
+ req->rq_disk->disk_name, block, nsect);
+ end_request(req, 0);
+ goto repeat;
+ }
+
+ if (disk->special_op) {
+ if (do_special_op(disk, req))
+ goto repeat;
+ return;
+ }
+ sec = block % disk->sect + 1;
+ track = block / disk->sect;
+ head = track % disk->head;
+ cyl = track / disk->head;
+#ifdef DEBUG
+ printk("%s: %sing: CHS=%d/%d/%d, sectors=%d, buffer=%p\n",
+ req->rq_disk->disk_name, (req->cmd == READ)?"read":"writ",
+ cyl, head, sec, nsect, req->buffer);
+#endif
+ if (req->flags & REQ_CMD) {
+ switch (rq_data_dir(req)) {
+ case READ:
+ hd_out(disk,nsect,sec,head,cyl,WIN_READ,&read_intr);
+ if (reset)
+ goto repeat;
+ break;
+ case WRITE:
+ hd_out(disk,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
+ if (reset)
+ goto repeat;
+ if (wait_DRQ()) {
+ bad_rw_intr();
+ goto repeat;
+ }
+ outsw(HD_DATA,req->buffer,256);
+ break;
+ default:
+ printk("unknown hd-command\n");
+ end_request(req, 0);
+ break;
+ }
+ }
+}
+
+static void do_hd_request (request_queue_t * q)
+{
+ disable_irq(HD_IRQ);
+ hd_request();
+ enable_irq(HD_IRQ);
+}
+
+static int hd_ioctl(struct inode * inode, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct hd_i_struct *disk = inode->i_bdev->bd_disk->private_data;
+ struct hd_geometry __user *loc = (struct hd_geometry __user *) arg;
+ struct hd_geometry g;
+
+ if (cmd != HDIO_GETGEO)
+ return -EINVAL;
+ if (!loc)
+ return -EINVAL;
+ g.heads = disk->head;
+ g.sectors = disk->sect;
+ g.cylinders = disk->cyl;
+ g.start = get_start_sect(inode->i_bdev);
+ return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0;
+}
+
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+
+static irqreturn_t hd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ void (*handler)(void) = do_hd;
+
+ do_hd = NULL;
+ del_timer(&device_timer);
+ if (!handler)
+ handler = unexpected_hd_interrupt;
+ handler();
+ local_irq_enable();
+ return IRQ_HANDLED;
+}
+
+static struct block_device_operations hd_fops = {
+ .ioctl = hd_ioctl,
+};
+
+/*
+ * This is the hard disk IRQ description. The SA_INTERRUPT in sa_flags
+ * means we run the IRQ-handler with interrupts disabled: this is bad for
+ * interrupt latency, but anything else has led to problems on some
+ * machines.
+ *
+ * We enable interrupts in some of the routines after making sure it's
+ * safe.
+ */
+
+static int __init hd_init(void)
+{
+ int drive;
+
+ if (register_blkdev(MAJOR_NR,"hd"))
+ return -1;
+
+ hd_queue = blk_init_queue(do_hd_request, &hd_lock);
+ if (!hd_queue) {
+ unregister_blkdev(MAJOR_NR,"hd");
+ return -ENOMEM;
+ }
+
+ blk_queue_max_sectors(hd_queue, 255);
+ init_timer(&device_timer);
+ device_timer.function = hd_times_out;
+ blk_queue_hardsect_size(hd_queue, 512);
+
+#ifdef __i386__
+ if (!NR_HD) {
+ extern struct drive_info drive_info;
+ unsigned char *BIOS = (unsigned char *) &drive_info;
+ unsigned long flags;
+ int cmos_disks;
+
+ for (drive=0 ; drive<2 ; drive++) {
+ hd_info[drive].cyl = *(unsigned short *) BIOS;
+ hd_info[drive].head = *(2+BIOS);
+ hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
+ hd_info[drive].ctl = *(8+BIOS);
+ hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
+ hd_info[drive].sect = *(14+BIOS);
+#ifdef does_not_work_for_everybody_with_scsi_but_helps_ibm_vp
+ if (hd_info[drive].cyl && NR_HD == drive)
+ NR_HD++;
+#endif
+ BIOS += 16;
+ }
+
+ /*
+ We query CMOS about hard disks : it could be that
+ we have a SCSI/ESDI/etc controller that is BIOS
+ compatible with ST-506, and thus showing up in our
+ BIOS table, but not register compatible, and therefore
+ not present in CMOS.
+
+ Furthermore, we will assume that our ST-506 drives
+ <if any> are the primary drives in the system, and
+ the ones reflected as drive 1 or 2.
+
+ The first drive is stored in the high nibble of CMOS
+ byte 0x12, the second in the low nibble. This will be
+ either a 4 bit drive type or 0xf indicating use byte 0x19
+ for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.
+
+ Needless to say, a non-zero value means we have
+ an AT controller hard disk for that drive.
+
+ Currently the rtc_lock is a bit academic since this
+ driver is non-modular, but someday... ? Paul G.
+ */
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ cmos_disks = CMOS_READ(0x12);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+
+ if (cmos_disks & 0xf0) {
+ if (cmos_disks & 0x0f)
+ NR_HD = 2;
+ else
+ NR_HD = 1;
+ }
+ }
+#endif /* __i386__ */
+#ifdef __arm__
+ if (!NR_HD) {
+ /* We don't know anything about the drive. This means
+ * that you *MUST* specify the drive parameters to the
+ * kernel yourself.
+ */
+ printk("hd: no drives specified - use hd=cyl,head,sectors"
+ " on kernel command line\n");
+ }
+#endif
+ if (!NR_HD)
+ goto out;
+
+ for (drive=0 ; drive < NR_HD ; drive++) {
+ struct gendisk *disk = alloc_disk(64);
+ struct hd_i_struct *p = &hd_info[drive];
+ if (!disk)
+ goto Enomem;
+ disk->major = MAJOR_NR;
+ disk->first_minor = drive << 6;
+ disk->fops = &hd_fops;
+ sprintf(disk->disk_name, "hd%c", 'a'+drive);
+ disk->private_data = p;
+ set_capacity(disk, p->head * p->sect * p->cyl);
+ disk->queue = hd_queue;
+ p->unit = drive;
+ hd_gendisk[drive] = disk;
+ printk ("%s: %luMB, CHS=%d/%d/%d\n",
+ disk->disk_name, (unsigned long)get_capacity(disk)/2048,
+ p->cyl, p->head, p->sect);
+ }
+
+ if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd", NULL)) {
+ printk("hd: unable to get IRQ%d for the hard disk driver\n",
+ HD_IRQ);
+ goto out1;
+ }
+ if (!request_region(HD_DATA, 8, "hd")) {
+ printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA);
+ goto out2;
+ }
+ if (!request_region(HD_CMD, 1, "hd(cmd)")) {
+ printk(KERN_WARNING "hd: port 0x%x busy\n", HD_CMD);
+ goto out3;
+ }
+
+ /* Let them fly */
+ for(drive=0; drive < NR_HD; drive++)
+ add_disk(hd_gendisk[drive]);
+
+ return 0;
+
+out3:
+ release_region(HD_DATA, 8);
+out2:
+ free_irq(HD_IRQ, NULL);
+out1:
+ for (drive = 0; drive < NR_HD; drive++)
+ put_disk(hd_gendisk[drive]);
+ NR_HD = 0;
+out:
+ del_timer(&device_timer);
+ unregister_blkdev(MAJOR_NR,"hd");
+ blk_cleanup_queue(hd_queue);
+ return -1;
+Enomem:
+ while (drive--)
+ put_disk(hd_gendisk[drive]);
+ goto out;
+}
+
+static int parse_hd_setup (char *line) {
+ int ints[6];
+
+ (void) get_options(line, ARRAY_SIZE(ints), ints);
+ hd_setup(NULL, ints);
+
+ return 1;
+}
+__setup("hd=", parse_hd_setup);
+
+module_init(hd_init);
diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c
new file mode 100644
index 00000000000..a77fb249d5c
--- /dev/null
+++ b/drivers/ide/legacy/ht6560b.c
@@ -0,0 +1,370 @@
+/*
+ * linux/drivers/ide/legacy/ht6560b.c Version 0.07 Feb 1, 2000
+ *
+ * Copyright (C) 1995-2000 Linus Torvalds & author (see below)
+ */
+
+/*
+ *
+ * Version 0.01 Initial version hacked out of ide.c
+ *
+ * Version 0.02 Added support for PIO modes, auto-tune
+ *
+ * Version 0.03 Some cleanups
+ *
+ * Version 0.05 PIO mode cycle timings auto-tune using bus-speed
+ *
+ * Version 0.06 Prefetch mode now defaults no OFF. To set
+ * prefetch mode OFF/ON use "hdparm -p8/-p9".
+ * Unmask irq is disabled when prefetch mode
+ * is enabled.
+ *
+ * Version 0.07 Trying to fix CD-ROM detection problem.
+ * "Prefetch" mode bit OFF for ide disks and
+ * ON for anything else.
+ *
+ *
+ * HT-6560B EIDE-controller support
+ * To activate controller support use kernel parameter "ide0=ht6560b".
+ * Use hdparm utility to enable PIO mode support.
+ *
+ * Author: Mikko Ala-Fossi <maf@iki.fi>
+ * Jan Evert van Grootheest <janevert@iae.nl>
+ *
+ * Try: http://www.maf.iki.fi/~maf/ht6560b/
+ */
+
+#define HT6560B_VERSION "v0.07"
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+
+/* #define DEBUG */ /* remove comments for DEBUG messages */
+
+/*
+ * The special i/o-port that HT-6560B uses to configuration:
+ * bit0 (0x01): "1" selects secondary interface
+ * bit2 (0x04): "1" enables FIFO function
+ * bit5 (0x20): "1" enables prefetched data read function (???)
+ *
+ * The special i/o-port that HT-6560A uses to configuration:
+ * bit0 (0x01): "1" selects secondary interface
+ * bit1 (0x02): "1" enables prefetched data read function
+ * bit2 (0x04): "0" enables multi-master system (?)
+ * bit3 (0x08): "1" 3 cycle time, "0" 2 cycle time (?)
+ */
+#define HT_CONFIG_PORT 0x3e6
+#define HT_CONFIG(drivea) (u8)(((drivea)->drive_data & 0xff00) >> 8)
+/*
+ * FIFO + PREFETCH (both a/b-model)
+ */
+#define HT_CONFIG_DEFAULT 0x1c /* no prefetch */
+/* #define HT_CONFIG_DEFAULT 0x3c */ /* with prefetch */
+#define HT_SECONDARY_IF 0x01
+#define HT_PREFETCH_MODE 0x20
+
+/*
+ * ht6560b Timing values:
+ *
+ * I reviewed some assembler source listings of htide drivers and found
+ * out how they setup those cycle time interfacing values, as they at Holtek
+ * call them. IDESETUP.COM that is supplied with the drivers figures out
+ * optimal values and fetches those values to drivers. I found out that
+ * they use IDE_SELECT_REG to fetch timings to the ide board right after
+ * interface switching. After that it was quite easy to add code to
+ * ht6560b.c.
+ *
+ * IDESETUP.COM gave me values 0x24, 0x45, 0xaa, 0xff that worked fine
+ * for hda and hdc. But hdb needed higher values to work, so I guess
+ * that sometimes it is necessary to give higher value than IDESETUP
+ * gives. [see cmd640.c for an extreme example of this. -ml]
+ *
+ * Perhaps I should explain something about these timing values:
+ * The higher nibble of value is the Recovery Time (rt) and the lower nibble
+ * of the value is the Active Time (at). Minimum value 2 is the fastest and
+ * the maximum value 15 is the slowest. Default values should be 15 for both.
+ * So 0x24 means 2 for rt and 4 for at. Each of the drives should have
+ * both values, and IDESETUP gives automatically rt=15 st=15 for CDROMs or
+ * similar. If value is too small there will be all sorts of failures.
+ *
+ * Timing byte consists of
+ * High nibble: Recovery Cycle Time (rt)
+ * The valid values range from 2 to 15. The default is 15.
+ *
+ * Low nibble: Active Cycle Time (at)
+ * The valid values range from 2 to 15. The default is 15.
+ *
+ * You can obtain optimized timing values by running Holtek IDESETUP.COM
+ * for DOS. DOS drivers get their timing values from command line, where
+ * the first value is the Recovery Time and the second value is the
+ * Active Time for each drive. Smaller value gives higher speed.
+ * In case of failures you should probably fall back to a higher value.
+ */
+#define HT_TIMING(drivea) (u8)((drivea)->drive_data & 0x00ff)
+#define HT_TIMING_DEFAULT 0xff
+
+/*
+ * This routine handles interface switching for the peculiar hardware design
+ * on the F.G.I./Holtek HT-6560B VLB IDE interface.
+ * The HT-6560B can only enable one IDE port at a time, and requires a
+ * silly sequence (below) whenever we switch between primary and secondary.
+ */
+
+/*
+ * This routine is invoked from ide.c to prepare for access to a given drive.
+ */
+static void ht6560b_selectproc (ide_drive_t *drive)
+{
+ unsigned long flags;
+ static u8 current_select = 0;
+ static u8 current_timing = 0;
+ u8 select, timing;
+
+ local_irq_save(flags);
+
+ select = HT_CONFIG(drive);
+ timing = HT_TIMING(drive);
+
+ if (select != current_select || timing != current_timing) {
+ current_select = select;
+ current_timing = timing;
+ if (drive->media != ide_disk || !drive->present)
+ select |= HT_PREFETCH_MODE;
+ (void) HWIF(drive)->INB(HT_CONFIG_PORT);
+ (void) HWIF(drive)->INB(HT_CONFIG_PORT);
+ (void) HWIF(drive)->INB(HT_CONFIG_PORT);
+ (void) HWIF(drive)->INB(HT_CONFIG_PORT);
+ HWIF(drive)->OUTB(select, HT_CONFIG_PORT);
+ /*
+ * Set timing for this drive:
+ */
+ HWIF(drive)->OUTB(timing, IDE_SELECT_REG);
+ (void) HWIF(drive)->INB(IDE_STATUS_REG);
+#ifdef DEBUG
+ printk("ht6560b: %s: select=%#x timing=%#x\n",
+ drive->name, select, timing);
+#endif
+ }
+ local_irq_restore(flags);
+}
+
+/*
+ * Autodetection and initialization of ht6560b
+ */
+static int __init try_to_init_ht6560b(void)
+{
+ u8 orig_value;
+ int i;
+
+ /* Autodetect ht6560b */
+ if ((orig_value = inb(HT_CONFIG_PORT)) == 0xff)
+ return 0;
+
+ for (i=3;i>0;i--) {
+ outb(0x00, HT_CONFIG_PORT);
+ if (!( (~inb(HT_CONFIG_PORT)) & 0x3f )) {
+ outb(orig_value, HT_CONFIG_PORT);
+ return 0;
+ }
+ }
+ outb(0x00, HT_CONFIG_PORT);
+ if ((~inb(HT_CONFIG_PORT))& 0x3f) {
+ outb(orig_value, HT_CONFIG_PORT);
+ return 0;
+ }
+ /*
+ * Ht6560b autodetected
+ */
+ outb(HT_CONFIG_DEFAULT, HT_CONFIG_PORT);
+ outb(HT_TIMING_DEFAULT, 0x1f6); /* IDE_SELECT_REG */
+ (void) inb(0x1f7); /* IDE_STATUS_REG */
+
+ printk("\nht6560b " HT6560B_VERSION
+ ": chipset detected and initialized"
+#ifdef DEBUG
+ " with debug enabled"
+#endif
+ );
+ return 1;
+}
+
+static u8 ht_pio2timings(ide_drive_t *drive, u8 pio)
+{
+ int active_time, recovery_time;
+ int active_cycles, recovery_cycles;
+ ide_pio_data_t d;
+ int bus_speed = system_bus_clock();
+
+ if (pio) {
+ pio = ide_get_best_pio_mode(drive, pio, 5, &d);
+
+ /*
+ * Just like opti621.c we try to calculate the
+ * actual cycle time for recovery and activity
+ * according system bus speed.
+ */
+ active_time = ide_pio_timings[pio].active_time;
+ recovery_time = d.cycle_time
+ - active_time
+ - ide_pio_timings[pio].setup_time;
+ /*
+ * Cycle times should be Vesa bus cycles
+ */
+ active_cycles = (active_time * bus_speed + 999) / 1000;
+ recovery_cycles = (recovery_time * bus_speed + 999) / 1000;
+ /*
+ * Upper and lower limits
+ */
+ if (active_cycles < 2) active_cycles = 2;
+ if (recovery_cycles < 2) recovery_cycles = 2;
+ if (active_cycles > 15) active_cycles = 15;
+ if (recovery_cycles > 15) recovery_cycles = 0; /* 0==16 */
+
+#ifdef DEBUG
+ printk("ht6560b: drive %s setting pio=%d recovery=%d (%dns) active=%d (%dns)\n", drive->name, pio, recovery_cycles, recovery_time, active_cycles, active_time);
+#endif
+
+ return (u8)((recovery_cycles << 4) | active_cycles);
+ } else {
+
+#ifdef DEBUG
+ printk("ht6560b: drive %s setting pio=0\n", drive->name);
+#endif
+
+ return HT_TIMING_DEFAULT; /* default setting */
+ }
+}
+
+/*
+ * Enable/Disable so called prefetch mode
+ */
+static void ht_set_prefetch(ide_drive_t *drive, u8 state)
+{
+ unsigned long flags;
+ int t = HT_PREFETCH_MODE << 8;
+
+ spin_lock_irqsave(&ide_lock, flags);
+
+ /*
+ * Prefetch mode and unmask irq seems to conflict
+ */
+ if (state) {
+ drive->drive_data |= t; /* enable prefetch mode */
+ drive->no_unmask = 1;
+ drive->unmask = 0;
+ } else {
+ drive->drive_data &= ~t; /* disable prefetch mode */
+ drive->no_unmask = 0;
+ }
+
+ spin_unlock_irqrestore(&ide_lock, flags);
+
+#ifdef DEBUG
+ printk("ht6560b: drive %s prefetch mode %sabled\n", drive->name, (state ? "en" : "dis"));
+#endif
+}
+
+static void tune_ht6560b (ide_drive_t *drive, u8 pio)
+{
+ unsigned long flags;
+ u8 timing;
+
+ switch (pio) {
+ case 8: /* set prefetch off */
+ case 9: /* set prefetch on */
+ ht_set_prefetch(drive, pio & 1);
+ return;
+ }
+
+ timing = ht_pio2timings(drive, pio);
+
+ spin_lock_irqsave(&ide_lock, flags);
+
+ drive->drive_data &= 0xff00;
+ drive->drive_data |= timing;
+
+ spin_unlock_irqrestore(&ide_lock, flags);
+
+#ifdef DEBUG
+ printk("ht6560b: drive %s tuned to pio mode %#x timing=%#x\n", drive->name, pio, timing);
+#endif
+}
+
+/* Can be called directly from ide.c. */
+int __init ht6560b_init(void)
+{
+ ide_hwif_t *hwif, *mate;
+ int t;
+
+ hwif = &ide_hwifs[0];
+ mate = &ide_hwifs[1];
+
+ if (!request_region(HT_CONFIG_PORT, 1, hwif->name)) {
+ printk(KERN_NOTICE "%s: HT_CONFIG_PORT not found\n",
+ __FUNCTION__);
+ return -ENODEV;
+ }
+
+ if (!try_to_init_ht6560b()) {
+ printk(KERN_NOTICE "%s: HBA not found\n", __FUNCTION__);
+ goto release_region;
+ }
+
+ hwif->chipset = ide_ht6560b;
+ hwif->selectproc = &ht6560b_selectproc;
+ hwif->tuneproc = &tune_ht6560b;
+ hwif->serialized = 1; /* is this needed? */
+ hwif->mate = mate;
+
+ mate->chipset = ide_ht6560b;
+ mate->selectproc = &ht6560b_selectproc;
+ mate->tuneproc = &tune_ht6560b;
+ mate->serialized = 1; /* is this needed? */
+ mate->mate = hwif;
+ mate->channel = 1;
+
+ /*
+ * Setting default configurations for drives
+ */
+ t = (HT_CONFIG_DEFAULT << 8);
+ t |= HT_TIMING_DEFAULT;
+ hwif->drives[0].drive_data = t;
+ hwif->drives[1].drive_data = t;
+
+ t |= (HT_SECONDARY_IF << 8);
+ mate->drives[0].drive_data = t;
+ mate->drives[1].drive_data = t;
+
+ probe_hwif_init(hwif);
+ probe_hwif_init(mate);
+
+ create_proc_ide_interfaces();
+
+ return 0;
+
+release_region:
+ release_region(HT_CONFIG_PORT, 1);
+ return -ENODEV;
+}
+
+#ifdef MODULE
+module_init(ht6560b_init);
+#endif
+
+MODULE_AUTHOR("See Local File");
+MODULE_DESCRIPTION("HT-6560B EIDE-controller support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c
new file mode 100644
index 00000000000..e20327e54b1
--- /dev/null
+++ b/drivers/ide/legacy/ide-cs.c
@@ -0,0 +1,481 @@
+/*======================================================================
+
+ A driver for PCMCIA IDE/ATA disk cards
+
+ ide-cs.c 1.3 2002/10/26 05:45:31
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The initial developer of the original code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU General Public License version 2 (the "GPL"), in
+ which case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the MPL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/ide.h>
+#include <linux/hdreg.h>
+#include <linux/major.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("PCMCIA ATA/IDE card driver");
+MODULE_LICENSE("Dual MPL/GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+#ifdef PCMCIA_DEBUG
+INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"ide-cs.c 1.3 2002/10/26 05:45:31 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+static const char ide_major[] = {
+ IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR,
+ IDE4_MAJOR, IDE5_MAJOR
+};
+
+typedef struct ide_info_t {
+ dev_link_t link;
+ int ndev;
+ dev_node_t node;
+ int hd;
+} ide_info_t;
+
+static void ide_release(dev_link_t *);
+static int ide_event(event_t event, int priority,
+ event_callback_args_t *args);
+
+static dev_info_t dev_info = "ide-cs";
+
+static dev_link_t *ide_attach(void);
+static void ide_detach(dev_link_t *);
+
+static dev_link_t *dev_list = NULL;
+
+/*======================================================================
+
+ ide_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+
+======================================================================*/
+
+static dev_link_t *ide_attach(void)
+{
+ ide_info_t *info;
+ dev_link_t *link;
+ client_reg_t client_reg;
+ int ret;
+
+ DEBUG(0, "ide_attach()\n");
+
+ /* Create new ide device */
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) return NULL;
+ memset(info, 0, sizeof(*info));
+ link = &info->link; link->priv = info;
+
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = 3;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.EventMask =
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.event_handler = &ide_event;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = pcmcia_register_client(&link->handle, &client_reg);
+ if (ret != CS_SUCCESS) {
+ cs_error(link->handle, RegisterClient, ret);
+ ide_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* ide_attach */
+
+/*======================================================================
+
+ This deletes a driver "instance". The device is de-registered
+ with Card Services. If it has been released, all local data
+ structures are freed. Otherwise, the structures will be freed
+ when the device is released.
+
+======================================================================*/
+
+static void ide_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+ int ret;
+
+ DEBUG(0, "ide_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link) break;
+ if (*linkp == NULL)
+ return;
+
+ if (link->state & DEV_CONFIG)
+ ide_release(link);
+
+ if (link->handle) {
+ ret = pcmcia_deregister_client(link->handle);
+ if (ret != CS_SUCCESS)
+ cs_error(link->handle, DeregisterClient, ret);
+ }
+
+ /* Unlink, free device structure */
+ *linkp = link->next;
+ kfree(link->priv);
+
+} /* ide_detach */
+
+static int idecs_register(unsigned long io, unsigned long ctl, unsigned long irq)
+{
+ hw_regs_t hw;
+ memset(&hw, 0, sizeof(hw));
+ ide_init_hwif_ports(&hw, io, ctl, NULL);
+ hw.irq = irq;
+ hw.chipset = ide_pci;
+ return ide_register_hw_with_fixup(&hw, NULL, ide_undecoded_slave);
+}
+
+/*======================================================================
+
+ ide_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ ide device available to the system.
+
+======================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void ide_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ ide_info_t *info = link->priv;
+ tuple_t tuple;
+ struct {
+ u_short buf[128];
+ cisparse_t parse;
+ config_info_t conf;
+ cistpl_cftable_entry_t dflt;
+ } *stk = NULL;
+ cistpl_cftable_entry_t *cfg;
+ int i, pass, last_ret = 0, last_fn = 0, hd, is_kme = 0;
+ unsigned long io_base, ctl_base;
+
+ DEBUG(0, "ide_config(0x%p)\n", link);
+
+ stk = kmalloc(sizeof(*stk), GFP_KERNEL);
+ if (!stk) goto err_mem;
+ memset(stk, 0, sizeof(*stk));
+ cfg = &stk->parse.cftable_entry;
+
+ tuple.TupleData = (cisdata_t *)&stk->buf;
+ tuple.TupleOffset = 0;
+ tuple.TupleDataMax = 255;
+ tuple.Attributes = 0;
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &stk->parse));
+ link->conf.ConfigBase = stk->parse.config.base;
+ link->conf.Present = stk->parse.config.rmask[0];
+
+ tuple.DesiredTuple = CISTPL_MANFID;
+ if (!pcmcia_get_first_tuple(handle, &tuple) &&
+ !pcmcia_get_tuple_data(handle, &tuple) &&
+ !pcmcia_parse_tuple(handle, &tuple, &stk->parse))
+ is_kme = ((stk->parse.manfid.manf == MANFID_KME) &&
+ ((stk->parse.manfid.card == PRODID_KME_KXLC005_A) ||
+ (stk->parse.manfid.card == PRODID_KME_KXLC005_B)));
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ /* Not sure if this is right... look up the current Vcc */
+ CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &stk->conf));
+ link->conf.Vcc = stk->conf.Vcc;
+
+ pass = io_base = ctl_base = 0;
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ tuple.Attributes = 0;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ while (1) {
+ if (pcmcia_get_tuple_data(handle, &tuple) != 0) goto next_entry;
+ if (pcmcia_parse_tuple(handle, &tuple, &stk->parse) != 0) goto next_entry;
+
+ /* Check for matching Vcc, unless we're desperate */
+ if (!pass) {
+ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (stk->conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000)
+ goto next_entry;
+ } else if (stk->dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (stk->conf.Vcc != stk->dflt.vcc.param[CISTPL_POWER_VNOM] / 10000)
+ goto next_entry;
+ }
+ }
+
+ if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ link->conf.Vpp1 = link->conf.Vpp2 =
+ cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ else if (stk->dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
+ link->conf.Vpp1 = link->conf.Vpp2 =
+ stk->dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ if ((cfg->io.nwin > 0) || (stk->dflt.io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &stk->dflt.io;
+ link->conf.ConfigIndex = cfg->index;
+ link->io.BasePort1 = io->win[0].base;
+ link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ if (io->nwin == 2) {
+ link->io.NumPorts1 = 8;
+ link->io.BasePort2 = io->win[1].base;
+ link->io.NumPorts2 = (is_kme) ? 2 : 1;
+ if (pcmcia_request_io(link->handle, &link->io) != 0)
+ goto next_entry;
+ io_base = link->io.BasePort1;
+ ctl_base = link->io.BasePort2;
+ } else if ((io->nwin == 1) && (io->win[0].len >= 16)) {
+ link->io.NumPorts1 = io->win[0].len;
+ link->io.NumPorts2 = 0;
+ if (pcmcia_request_io(link->handle, &link->io) != 0)
+ goto next_entry;
+ io_base = link->io.BasePort1;
+ ctl_base = link->io.BasePort1 + 0x0e;
+ } else goto next_entry;
+ /* If we've got this far, we're done */
+ break;
+ }
+
+ next_entry:
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ memcpy(&stk->dflt, cfg, sizeof(stk->dflt));
+ if (pass) {
+ CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+ } else if (pcmcia_get_next_tuple(handle, &tuple) != 0) {
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ memset(&stk->dflt, 0, sizeof(stk->dflt));
+ pass++;
+ }
+ }
+
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+
+ /* disable drive interrupts during IDE probe */
+ outb(0x02, ctl_base);
+
+ /* special setup for KXLC005 card */
+ if (is_kme)
+ outb(0x81, ctl_base+1);
+
+ /* retry registration in case device is still spinning up */
+ for (hd = -1, i = 0; i < 10; i++) {
+ hd = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ);
+ if (hd >= 0) break;
+ if (link->io.NumPorts1 == 0x20) {
+ outb(0x02, ctl_base + 0x10);
+ hd = idecs_register(io_base + 0x10, ctl_base + 0x10,
+ link->irq.AssignedIRQ);
+ if (hd >= 0) {
+ io_base += 0x10;
+ ctl_base += 0x10;
+ break;
+ }
+ }
+ __set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/10);
+ }
+
+ if (hd < 0) {
+ printk(KERN_NOTICE "ide-cs: ide_register() at 0x%3lx & 0x%3lx"
+ ", irq %u failed\n", io_base, ctl_base,
+ link->irq.AssignedIRQ);
+ goto failed;
+ }
+
+ info->ndev = 1;
+ sprintf(info->node.dev_name, "hd%c", 'a' + (hd * 2));
+ info->node.major = ide_major[hd];
+ info->node.minor = 0;
+ info->hd = hd;
+ link->dev = &info->node;
+ printk(KERN_INFO "ide-cs: %s: Vcc = %d.%d, Vpp = %d.%d\n",
+ info->node.dev_name, link->conf.Vcc / 10, link->conf.Vcc % 10,
+ link->conf.Vpp1 / 10, link->conf.Vpp1 % 10);
+
+ link->state &= ~DEV_CONFIG_PENDING;
+ kfree(stk);
+ return;
+
+err_mem:
+ printk(KERN_NOTICE "ide-cs: ide_config failed memory allocation\n");
+ goto failed;
+
+cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+failed:
+ kfree(stk);
+ ide_release(link);
+ link->state &= ~DEV_CONFIG_PENDING;
+} /* ide_config */
+
+/*======================================================================
+
+ After a card is removed, ide_release() will unregister the net
+ device, and release the PCMCIA configuration. If the device is
+ still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+void ide_release(dev_link_t *link)
+{
+ ide_info_t *info = link->priv;
+
+ DEBUG(0, "ide_release(0x%p)\n", link);
+
+ if (info->ndev) {
+ /* FIXME: if this fails we need to queue the cleanup somehow
+ -- need to investigate the required PCMCIA magic */
+ ide_unregister(info->hd);
+ }
+ info->ndev = 0;
+ link->dev = NULL;
+
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+ pcmcia_release_irq(link->handle, &link->irq);
+
+ link->state &= ~DEV_CONFIG;
+
+} /* ide_release */
+
+/*======================================================================
+
+ The card status event handler. Mostly, this schedules other
+ stuff to run after an event is received. A CARD_REMOVAL event
+ also sets some flags to discourage the ide drivers from
+ talking to the ports.
+
+======================================================================*/
+
+int ide_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+
+ DEBUG(1, "ide_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG)
+ ide_release(link);
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ ide_config(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ pcmcia_release_configuration(link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (DEV_OK(link))
+ pcmcia_request_configuration(link->handle, &link->conf);
+ break;
+ }
+ return 0;
+} /* ide_event */
+
+static struct pcmcia_driver ide_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "ide-cs",
+ },
+ .attach = ide_attach,
+ .detach = ide_detach,
+};
+
+static int __init init_ide_cs(void)
+{
+ return pcmcia_register_driver(&ide_cs_driver);
+}
+
+static void __exit exit_ide_cs(void)
+{
+ pcmcia_unregister_driver(&ide_cs_driver);
+ BUG_ON(dev_list != NULL);
+}
+
+module_init(init_ide_cs);
+module_exit(exit_ide_cs);
diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c
new file mode 100644
index 00000000000..90cac609d9c
--- /dev/null
+++ b/drivers/ide/legacy/macide.c
@@ -0,0 +1,155 @@
+/*
+ * linux/drivers/ide/legacy/macide.c -- Macintosh IDE Driver
+ *
+ * Copyright (C) 1998 by Michael Schmitz
+ *
+ * This driver was written based on information obtained from the MacOS IDE
+ * driver binary by Mikael Forselius
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/machw.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_baboon.h>
+
+#define IDE_BASE 0x50F1A000 /* Base address of IDE controller */
+
+/*
+ * Generic IDE registers as offsets from the base
+ * These match MkLinux so they should be correct.
+ */
+
+#define IDE_DATA 0x00
+#define IDE_ERROR 0x04 /* see err-bits */
+#define IDE_NSECTOR 0x08 /* nr of sectors to read/write */
+#define IDE_SECTOR 0x0c /* starting sector */
+#define IDE_LCYL 0x10 /* starting cylinder */
+#define IDE_HCYL 0x14 /* high byte of starting cyl */
+#define IDE_SELECT 0x18 /* 101dhhhh , d=drive, hhhh=head */
+#define IDE_STATUS 0x1c /* see status-bits */
+#define IDE_CONTROL 0x38 /* control/altstatus */
+
+/*
+ * Mac-specific registers
+ */
+
+/*
+ * this register is odd; it doesn't seem to do much and it's
+ * not word-aligned like virtually every other hardware register
+ * on the Mac...
+ */
+
+#define IDE_IFR 0x101 /* (0x101) IDE interrupt flags on Quadra:
+ *
+ * Bit 0+1: some interrupt flags
+ * Bit 2+3: some interrupt enable
+ * Bit 4: ??
+ * Bit 5: IDE interrupt flag (any hwif)
+ * Bit 6: maybe IDE interrupt enable (any hwif) ??
+ * Bit 7: Any interrupt condition
+ */
+
+volatile unsigned char *ide_ifr = (unsigned char *) (IDE_BASE + IDE_IFR);
+
+static int macide_offsets[IDE_NR_PORTS] = {
+ IDE_DATA, IDE_ERROR, IDE_NSECTOR, IDE_SECTOR, IDE_LCYL,
+ IDE_HCYL, IDE_SELECT, IDE_STATUS, IDE_CONTROL
+};
+
+int macide_ack_intr(ide_hwif_t* hwif)
+{
+ if (*ide_ifr & 0x20) {
+ *ide_ifr &= ~0x20;
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_BLK_DEV_MAC_MEDIABAY
+static void macide_mediabay_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int state = baboon->mb_status & 0x04;
+
+ printk(KERN_INFO "macide: media bay %s detected\n", state? "removal":"insertion");
+}
+#endif
+
+/*
+ * Probe for a Macintosh IDE interface
+ */
+
+void macide_init(void)
+{
+ hw_regs_t hw;
+ ide_hwif_t *hwif;
+ int index = -1;
+
+ switch (macintosh_config->ide_type) {
+ case MAC_IDE_QUADRA:
+ ide_setup_ports(&hw, IDE_BASE, macide_offsets,
+ 0, 0, macide_ack_intr,
+// quadra_ide_iops,
+ IRQ_NUBUS_F);
+ index = ide_register_hw(&hw, &hwif);
+ break;
+ case MAC_IDE_PB:
+ ide_setup_ports(&hw, IDE_BASE, macide_offsets,
+ 0, 0, macide_ack_intr,
+// macide_pb_iops,
+ IRQ_NUBUS_C);
+ index = ide_register_hw(&hw, &hwif);
+ break;
+ case MAC_IDE_BABOON:
+ ide_setup_ports(&hw, BABOON_BASE, macide_offsets,
+ 0, 0, NULL,
+// macide_baboon_iops,
+ IRQ_BABOON_1);
+ index = ide_register_hw(&hw, &hwif);
+ if (index == -1) break;
+ if (macintosh_config->ident == MAC_MODEL_PB190) {
+
+ /* Fix breakage in ide-disk.c: drive capacity */
+ /* is not initialized for drives without a */
+ /* hardware ID, and we can't get that without */
+ /* probing the drive which freezes a 190. */
+
+ ide_drive_t *drive = &ide_hwifs[index].drives[0];
+ drive->capacity64 = drive->cyl*drive->head*drive->sect;
+
+#ifdef CONFIG_BLK_DEV_MAC_MEDIABAY
+ request_irq(IRQ_BABOON_2, macide_mediabay_interrupt,
+ IRQ_FLG_FAST, "mediabay",
+ macide_mediabay_interrupt);
+#endif
+ }
+ break;
+
+ default:
+ return;
+ }
+
+ if (index != -1) {
+ hwif->mmio = 2;
+ if (macintosh_config->ide_type == MAC_IDE_QUADRA)
+ printk(KERN_INFO "ide%d: Macintosh Quadra IDE interface\n", index);
+ else if (macintosh_config->ide_type == MAC_IDE_PB)
+ printk(KERN_INFO "ide%d: Macintosh Powerbook IDE interface\n", index);
+ else if (macintosh_config->ide_type == MAC_IDE_BABOON)
+ printk(KERN_INFO "ide%d: Macintosh Powerbook Baboon IDE interface\n", index);
+ else
+ printk(KERN_INFO "ide%d: Unknown Macintosh IDE interface\n", index);
+ }
+}
diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c
new file mode 100644
index 00000000000..2a78b792f7f
--- /dev/null
+++ b/drivers/ide/legacy/q40ide.c
@@ -0,0 +1,150 @@
+/*
+ * linux/drivers/ide/legacy/q40ide.c -- Q40 I/O port IDE Driver
+ *
+ * (c) Richard Zidlicky
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <linux/ide.h>
+
+ /*
+ * Bases of the IDE interfaces
+ */
+
+#define Q40IDE_NUM_HWIFS 2
+
+#define PCIDE_BASE1 0x1f0
+#define PCIDE_BASE2 0x170
+#define PCIDE_BASE3 0x1e8
+#define PCIDE_BASE4 0x168
+#define PCIDE_BASE5 0x1e0
+#define PCIDE_BASE6 0x160
+
+static const unsigned long pcide_bases[Q40IDE_NUM_HWIFS] = {
+ PCIDE_BASE1, PCIDE_BASE2, /* PCIDE_BASE3, PCIDE_BASE4 , PCIDE_BASE5,
+ PCIDE_BASE6 */
+};
+
+
+ /*
+ * Offsets from one of the above bases
+ */
+
+/* used to do addr translation here but it is easier to do in setup ports */
+/*#define IDE_OFF_B(x) ((unsigned long)Q40_ISA_IO_B((IDE_##x##_OFFSET)))*/
+
+#define IDE_OFF_B(x) ((unsigned long)((IDE_##x##_OFFSET)))
+#define IDE_OFF_W(x) ((unsigned long)((IDE_##x##_OFFSET)))
+
+static const int pcide_offsets[IDE_NR_PORTS] = {
+ IDE_OFF_W(DATA), IDE_OFF_B(ERROR), IDE_OFF_B(NSECTOR), IDE_OFF_B(SECTOR),
+ IDE_OFF_B(LCYL), IDE_OFF_B(HCYL), 6 /*IDE_OFF_B(CURRENT)*/, IDE_OFF_B(STATUS),
+ 518/*IDE_OFF(CMD)*/
+};
+
+static int q40ide_default_irq(unsigned long base)
+{
+ switch (base) {
+ case 0x1f0: return 14;
+ case 0x170: return 15;
+ case 0x1e8: return 11;
+ default:
+ return 0;
+ }
+}
+
+
+/*
+ * This is very similar to ide_setup_ports except that addresses
+ * are pretranslated for q40 ISA access
+ */
+void q40_ide_setup_ports ( hw_regs_t *hw,
+ unsigned long base, int *offsets,
+ unsigned long ctrl, unsigned long intr,
+ ide_ack_intr_t *ack_intr,
+/*
+ * ide_io_ops_t *iops,
+ */
+ int irq)
+{
+ int i;
+
+ for (i = 0; i < IDE_NR_PORTS; i++) {
+ /* BIG FAT WARNING:
+ assumption: only DATA port is ever used in 16 bit mode */
+ if ( i==0 )
+ hw->io_ports[i] = Q40_ISA_IO_W(base + offsets[i]);
+ else
+ hw->io_ports[i] = Q40_ISA_IO_B(base + offsets[i]);
+ }
+
+ hw->irq = irq;
+ hw->dma = NO_DMA;
+ hw->ack_intr = ack_intr;
+/*
+ * hw->iops = iops;
+ */
+}
+
+
+
+/*
+ * the static array is needed to have the name reported in /proc/ioports,
+ * hwif->name unfortunately isnīt available yet
+ */
+static const char *q40_ide_names[Q40IDE_NUM_HWIFS]={
+ "ide0", "ide1"
+};
+
+/*
+ * Probe for Q40 IDE interfaces
+ */
+
+void q40ide_init(void)
+{
+ int i;
+ ide_hwif_t *hwif;
+ int index;
+ const char *name;
+
+ if (!MACH_IS_Q40)
+ return ;
+
+ for (i = 0; i < Q40IDE_NUM_HWIFS; i++) {
+ hw_regs_t hw;
+
+ name = q40_ide_names[i];
+ if (!request_region(pcide_bases[i], 8, name)) {
+ printk("could not reserve ports %lx-%lx for %s\n",
+ pcide_bases[i],pcide_bases[i]+8,name);
+ continue;
+ }
+ if (!request_region(pcide_bases[i]+0x206, 1, name)) {
+ printk("could not reserve port %lx for %s\n",
+ pcide_bases[i]+0x206,name);
+ release_region(pcide_bases[i], 8);
+ continue;
+ }
+ q40_ide_setup_ports(&hw,(unsigned long) pcide_bases[i], (int *)pcide_offsets,
+ pcide_bases[i]+0x206,
+ 0, NULL,
+// m68kide_iops,
+ q40ide_default_irq(pcide_bases[i]));
+ index = ide_register_hw(&hw, &hwif);
+ // **FIXME**
+ if (index != -1)
+ hwif->mmio = 2;
+ }
+}
+
diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c
new file mode 100644
index 00000000000..563fab0098b
--- /dev/null
+++ b/drivers/ide/legacy/qd65xx.c
@@ -0,0 +1,511 @@
+/*
+ * linux/drivers/ide/legacy/qd65xx.c Version 0.07 Sep 30, 2001
+ *
+ * Copyright (C) 1996-2001 Linus Torvalds & author (see below)
+ */
+
+/*
+ * Version 0.03 Cleaned auto-tune, added probe
+ * Version 0.04 Added second channel tuning
+ * Version 0.05 Enhanced tuning ; added qd6500 support
+ * Version 0.06 Added dos driver's list
+ * Version 0.07 Second channel bug fix
+ *
+ * QDI QD6500/QD6580 EIDE controller fast support
+ *
+ * Please set local bus speed using kernel parameter idebus
+ * for example, "idebus=33" stands for 33Mhz VLbus
+ * To activate controller support, use "ide0=qd65xx"
+ * To enable tuning, use "ide0=autotune"
+ * To enable second channel tuning (qd6580 only), use "ide1=autotune"
+ */
+
+/*
+ * Rewritten from the work of Colten Edwards <pje120@cs.usask.ca> by
+ * Samuel Thibault <samuel.thibault@fnac.net>
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "qd65xx.h"
+
+/*
+ * I/O ports are 0x30-0x31 (and 0x32-0x33 for qd6580)
+ * or 0xb0-0xb1 (and 0xb2-0xb3 for qd6580)
+ * -- qd6500 is a single IDE interface
+ * -- qd6580 is a dual IDE interface
+ *
+ * More research on qd6580 being done by willmore@cig.mot.com (David)
+ * More Information given by Petr Soucek (petr@ryston.cz)
+ * http://www.ryston.cz/petr/vlb
+ */
+
+/*
+ * base: Timer1
+ *
+ *
+ * base+0x01: Config (R/O)
+ *
+ * bit 0: ide baseport: 1 = 0x1f0 ; 0 = 0x170 (only useful for qd6500)
+ * bit 1: qd65xx baseport: 1 = 0xb0 ; 0 = 0x30
+ * bit 2: ID3: bus speed: 1 = <=33MHz ; 0 = >33MHz
+ * bit 3: qd6500: 1 = disabled, 0 = enabled
+ * qd6580: 1
+ * upper nibble:
+ * qd6500: 1100
+ * qd6580: either 1010 or 0101
+ *
+ *
+ * base+0x02: Timer2 (qd6580 only)
+ *
+ *
+ * base+0x03: Control (qd6580 only)
+ *
+ * bits 0-3 must always be set 1
+ * bit 4 must be set 1, but is set 0 by dos driver while measuring vlb clock
+ * bit 0 : 1 = Only primary port enabled : channel 0 for hda, channel 1 for hdb
+ * 0 = Primary and Secondary ports enabled : channel 0 for hda & hdb
+ * channel 1 for hdc & hdd
+ * bit 1 : 1 = only disks on primary port
+ * 0 = disks & ATAPI devices on primary port
+ * bit 2-4 : always 0
+ * bit 5 : status, but of what ?
+ * bit 6 : always set 1 by dos driver
+ * bit 7 : set 1 for non-ATAPI devices on primary port
+ * (maybe read-ahead and post-write buffer ?)
+ */
+
+static int timings[4]={-1,-1,-1,-1}; /* stores current timing for each timer */
+
+static void qd_write_reg (u8 content, unsigned long reg)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ide_lock, flags);
+ outb(content,reg);
+ spin_unlock_irqrestore(&ide_lock, flags);
+}
+
+static u8 __init qd_read_reg (unsigned long reg)
+{
+ unsigned long flags;
+ u8 read;
+
+ spin_lock_irqsave(&ide_lock, flags);
+ read = inb(reg);
+ spin_unlock_irqrestore(&ide_lock, flags);
+ return read;
+}
+
+/*
+ * qd_select:
+ *
+ * This routine is invoked from ide.c to prepare for access to a given drive.
+ */
+
+static void qd_select (ide_drive_t *drive)
+{
+ u8 index = (( (QD_TIMREG(drive)) & 0x80 ) >> 7) |
+ (QD_TIMREG(drive) & 0x02);
+
+ if (timings[index] != QD_TIMING(drive))
+ qd_write_reg(timings[index] = QD_TIMING(drive), QD_TIMREG(drive));
+}
+
+/*
+ * qd6500_compute_timing
+ *
+ * computes the timing value where
+ * lower nibble represents active time, in count of VLB clocks
+ * upper nibble represents recovery time, in count of VLB clocks
+ */
+
+static u8 qd6500_compute_timing (ide_hwif_t *hwif, int active_time, int recovery_time)
+{
+ u8 active_cycle,recovery_cycle;
+
+ if (system_bus_clock()<=33) {
+ active_cycle = 9 - IDE_IN(active_time * system_bus_clock() / 1000 + 1, 2, 9);
+ recovery_cycle = 15 - IDE_IN(recovery_time * system_bus_clock() / 1000 + 1, 0, 15);
+ } else {
+ active_cycle = 8 - IDE_IN(active_time * system_bus_clock() / 1000 + 1, 1, 8);
+ recovery_cycle = 18 - IDE_IN(recovery_time * system_bus_clock() / 1000 + 1, 3, 18);
+ }
+
+ return((recovery_cycle<<4) | 0x08 | active_cycle);
+}
+
+/*
+ * qd6580_compute_timing
+ *
+ * idem for qd6580
+ */
+
+static u8 qd6580_compute_timing (int active_time, int recovery_time)
+{
+ u8 active_cycle = 17 - IDE_IN(active_time * system_bus_clock() / 1000 + 1, 2, 17);
+ u8 recovery_cycle = 15 - IDE_IN(recovery_time * system_bus_clock() / 1000 + 1, 2, 15);
+
+ return((recovery_cycle<<4) | active_cycle);
+}
+
+/*
+ * qd_find_disk_type
+ *
+ * tries to find timing from dos driver's table
+ */
+
+static int qd_find_disk_type (ide_drive_t *drive,
+ int *active_time, int *recovery_time)
+{
+ struct qd65xx_timing_s *p;
+ char model[40];
+
+ if (!*drive->id->model) return 0;
+
+ strncpy(model,drive->id->model,40);
+ ide_fixstring(model,40,1); /* byte-swap */
+
+ for (p = qd65xx_timing ; p->offset != -1 ; p++) {
+ if (!strncmp(p->model, model+p->offset, 4)) {
+ printk(KERN_DEBUG "%s: listed !\n", drive->name);
+ *active_time = p->active;
+ *recovery_time = p->recovery;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * qd_timing_ok:
+ *
+ * check whether timings don't conflict
+ */
+
+static int qd_timing_ok (ide_drive_t drives[])
+{
+ return (IDE_IMPLY(drives[0].present && drives[1].present,
+ IDE_IMPLY(QD_TIMREG(drives) == QD_TIMREG(drives+1),
+ QD_TIMING(drives) == QD_TIMING(drives+1))));
+ /* if same timing register, must be same timing */
+}
+
+/*
+ * qd_set_timing:
+ *
+ * records the timing, and enables selectproc as needed
+ */
+
+static void qd_set_timing (ide_drive_t *drive, u8 timing)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+
+ drive->drive_data &= 0xff00;
+ drive->drive_data |= timing;
+ if (qd_timing_ok(hwif->drives)) {
+ qd_select(drive); /* selects once */
+ hwif->selectproc = NULL;
+ } else
+ hwif->selectproc = &qd_select;
+
+ printk(KERN_DEBUG "%s: %#x\n", drive->name, timing);
+}
+
+/*
+ * qd6500_tune_drive
+ */
+
+static void qd6500_tune_drive (ide_drive_t *drive, u8 pio)
+{
+ int active_time = 175;
+ int recovery_time = 415; /* worst case values from the dos driver */
+
+ if (drive->id && !qd_find_disk_type(drive, &active_time, &recovery_time)
+ && drive->id->tPIO && (drive->id->field_valid & 0x02)
+ && drive->id->eide_pio >= 240) {
+
+ printk(KERN_INFO "%s: PIO mode%d\n", drive->name,
+ drive->id->tPIO);
+ active_time = 110;
+ recovery_time = drive->id->eide_pio - 120;
+ }
+
+ qd_set_timing(drive, qd6500_compute_timing(HWIF(drive), active_time, recovery_time));
+}
+
+/*
+ * qd6580_tune_drive
+ */
+
+static void qd6580_tune_drive (ide_drive_t *drive, u8 pio)
+{
+ ide_pio_data_t d;
+ int base = HWIF(drive)->select_data;
+ int active_time = 175;
+ int recovery_time = 415; /* worst case values from the dos driver */
+
+ if (drive->id && !qd_find_disk_type(drive, &active_time, &recovery_time)) {
+ pio = ide_get_best_pio_mode(drive, pio, 255, &d);
+ pio = min_t(u8, pio, 4);
+
+ switch (pio) {
+ case 0: break;
+ case 3:
+ if (d.cycle_time >= 110) {
+ active_time = 86;
+ recovery_time = d.cycle_time - 102;
+ } else
+ printk(KERN_WARNING "%s: Strange recovery time !\n",drive->name);
+ break;
+ case 4:
+ if (d.cycle_time >= 69) {
+ active_time = 70;
+ recovery_time = d.cycle_time - 61;
+ } else
+ printk(KERN_WARNING "%s: Strange recovery time !\n",drive->name);
+ break;
+ default:
+ if (d.cycle_time >= 180) {
+ active_time = 110;
+ recovery_time = d.cycle_time - 120;
+ } else {
+ active_time = ide_pio_timings[pio].active_time;
+ recovery_time = d.cycle_time
+ -active_time;
+ }
+ }
+ printk(KERN_INFO "%s: PIO mode%d\n", drive->name,pio);
+ }
+
+ if (!HWIF(drive)->channel && drive->media != ide_disk) {
+ qd_write_reg(0x5f, QD_CONTROL_PORT);
+ printk(KERN_WARNING "%s: ATAPI: disabled read-ahead FIFO "
+ "and post-write buffer on %s.\n",
+ drive->name, HWIF(drive)->name);
+ }
+
+ qd_set_timing(drive, qd6580_compute_timing(active_time, recovery_time));
+}
+
+/*
+ * qd_testreg
+ *
+ * tests if the given port is a register
+ */
+
+static int __init qd_testreg(int port)
+{
+ u8 savereg;
+ u8 readreg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ide_lock, flags);
+ savereg = inb_p(port);
+ outb_p(QD_TESTVAL, port); /* safe value */
+ readreg = inb_p(port);
+ outb(savereg, port);
+ spin_unlock_irqrestore(&ide_lock, flags);
+
+ if (savereg == QD_TESTVAL) {
+ printk(KERN_ERR "Outch ! the probe for qd65xx isn't reliable !\n");
+ printk(KERN_ERR "Please contact maintainers to tell about your hardware\n");
+ printk(KERN_ERR "Assuming qd65xx is not present.\n");
+ return 1;
+ }
+
+ return (readreg != QD_TESTVAL);
+}
+
+/*
+ * qd_setup:
+ *
+ * called to setup an ata channel : adjusts attributes & links for tuning
+ */
+
+static void __init qd_setup(ide_hwif_t *hwif, int base, int config,
+ unsigned int data0, unsigned int data1,
+ void (*tuneproc) (ide_drive_t *, u8 pio))
+{
+ hwif->chipset = ide_qd65xx;
+ hwif->channel = hwif->index;
+ hwif->select_data = base;
+ hwif->config_data = config;
+ hwif->drives[0].drive_data = data0;
+ hwif->drives[1].drive_data = data1;
+ hwif->drives[0].io_32bit =
+ hwif->drives[1].io_32bit = 1;
+ hwif->tuneproc = tuneproc;
+ probe_hwif_init(hwif);
+}
+
+/*
+ * qd_unsetup:
+ *
+ * called to unsetup an ata channel : back to default values, unlinks tuning
+ */
+/*
+static void __exit qd_unsetup(ide_hwif_t *hwif)
+{
+ u8 config = hwif->config_data;
+ int base = hwif->select_data;
+ void *tuneproc = (void *) hwif->tuneproc;
+
+ if (hwif->chipset != ide_qd65xx)
+ return;
+
+ printk(KERN_NOTICE "%s: back to defaults\n", hwif->name);
+
+ hwif->selectproc = NULL;
+ hwif->tuneproc = NULL;
+
+ if (tuneproc == (void *) qd6500_tune_drive) {
+ // will do it for both
+ qd_write_reg(QD6500_DEF_DATA, QD_TIMREG(&hwif->drives[0]));
+ } else if (tuneproc == (void *) qd6580_tune_drive) {
+ if (QD_CONTROL(hwif) & QD_CONTR_SEC_DISABLED) {
+ qd_write_reg(QD6580_DEF_DATA, QD_TIMREG(&hwif->drives[0]));
+ qd_write_reg(QD6580_DEF_DATA2, QD_TIMREG(&hwif->drives[1]));
+ } else {
+ qd_write_reg(hwif->channel ? QD6580_DEF_DATA2 : QD6580_DEF_DATA, QD_TIMREG(&hwif->drives[0]));
+ }
+ } else {
+ printk(KERN_WARNING "Unknown qd65xx tuning fonction !\n");
+ printk(KERN_WARNING "keeping settings !\n");
+ }
+}
+*/
+
+/*
+ * qd_probe:
+ *
+ * looks at the specified baseport, and if qd found, registers & initialises it
+ * return 1 if another qd may be probed
+ */
+
+static int __init qd_probe(int base)
+{
+ ide_hwif_t *hwif;
+ u8 config;
+ u8 unit;
+
+ config = qd_read_reg(QD_CONFIG_PORT);
+
+ if (! ((config & QD_CONFIG_BASEPORT) >> 1 == (base == 0xb0)) )
+ return 1;
+
+ unit = ! (config & QD_CONFIG_IDE_BASEPORT);
+
+ if ((config & 0xf0) == QD_CONFIG_QD6500) {
+
+ if (qd_testreg(base)) return 1; /* bad register */
+
+ /* qd6500 found */
+
+ hwif = &ide_hwifs[unit];
+ printk(KERN_NOTICE "%s: qd6500 at %#x\n", hwif->name, base);
+ printk(KERN_DEBUG "qd6500: config=%#x, ID3=%u\n",
+ config, QD_ID3);
+
+ if (config & QD_CONFIG_DISABLED) {
+ printk(KERN_WARNING "qd6500 is disabled !\n");
+ return 1;
+ }
+
+ qd_setup(hwif, base, config, QD6500_DEF_DATA, QD6500_DEF_DATA,
+ &qd6500_tune_drive);
+
+ create_proc_ide_interfaces();
+
+ return 1;
+ }
+
+ if (((config & 0xf0) == QD_CONFIG_QD6580_A) ||
+ ((config & 0xf0) == QD_CONFIG_QD6580_B)) {
+
+ u8 control;
+
+ if (qd_testreg(base) || qd_testreg(base+0x02)) return 1;
+ /* bad registers */
+
+ /* qd6580 found */
+
+ control = qd_read_reg(QD_CONTROL_PORT);
+
+ printk(KERN_NOTICE "qd6580 at %#x\n", base);
+ printk(KERN_DEBUG "qd6580: config=%#x, control=%#x, ID3=%u\n",
+ config, control, QD_ID3);
+
+ if (control & QD_CONTR_SEC_DISABLED) {
+ /* secondary disabled */
+
+ hwif = &ide_hwifs[unit];
+ printk(KERN_INFO "%s: qd6580: single IDE board\n",
+ hwif->name);
+ qd_setup(hwif, base, config | (control << 8),
+ QD6580_DEF_DATA, QD6580_DEF_DATA2,
+ &qd6580_tune_drive);
+ qd_write_reg(QD_DEF_CONTR,QD_CONTROL_PORT);
+
+ create_proc_ide_interfaces();
+
+ return 1;
+ } else {
+ ide_hwif_t *mate;
+
+ hwif = &ide_hwifs[0];
+ mate = &ide_hwifs[1];
+ /* secondary enabled */
+ printk(KERN_INFO "%s&%s: qd6580: dual IDE board\n",
+ hwif->name, mate->name);
+
+ qd_setup(hwif, base, config | (control << 8),
+ QD6580_DEF_DATA, QD6580_DEF_DATA,
+ &qd6580_tune_drive);
+ qd_setup(mate, base, config | (control << 8),
+ QD6580_DEF_DATA2, QD6580_DEF_DATA2,
+ &qd6580_tune_drive);
+ qd_write_reg(QD_DEF_CONTR,QD_CONTROL_PORT);
+
+ create_proc_ide_interfaces();
+
+ return 0; /* no other qd65xx possible */
+ }
+ }
+ /* no qd65xx found */
+ return 1;
+}
+
+/* Can be called directly from ide.c. */
+int __init qd65xx_init(void)
+{
+ if (qd_probe(0x30))
+ qd_probe(0xb0);
+ if (ide_hwifs[0].chipset != ide_qd65xx &&
+ ide_hwifs[1].chipset != ide_qd65xx)
+ return -ENODEV;
+ return 0;
+}
+
+#ifdef MODULE
+module_init(qd65xx_init);
+#endif
+
+MODULE_AUTHOR("Samuel Thibault");
+MODULE_DESCRIPTION("support of qd65xx vlb ide chipset");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ide/legacy/qd65xx.h b/drivers/ide/legacy/qd65xx.h
new file mode 100644
index 00000000000..633a42456ef
--- /dev/null
+++ b/drivers/ide/legacy/qd65xx.h
@@ -0,0 +1,140 @@
+/*
+ * linux/drivers/ide/legacy/qd65xx.h
+ *
+ * Copyright (c) 2000 Linus Torvalds & authors
+ */
+
+/*
+ * Authors: Petr Soucek <petr@ryston.cz>
+ * Samuel Thibault <samuel.thibault@fnac.net>
+ */
+
+/* truncates a in [b,c] */
+#define IDE_IN(a,b,c) ( ((a)<(b)) ? (b) : ( (a)>(c) ? (c) : (a)) )
+
+#define IDE_IMPLY(a,b) ((!(a)) || (b))
+
+#define QD_TIM1_PORT (base)
+#define QD_CONFIG_PORT (base+0x01)
+#define QD_TIM2_PORT (base+0x02)
+#define QD_CONTROL_PORT (base+0x03)
+
+#define QD_CONFIG_IDE_BASEPORT 0x01
+#define QD_CONFIG_BASEPORT 0x02
+#define QD_CONFIG_ID3 0x04
+#define QD_CONFIG_DISABLED 0x08
+#define QD_CONFIG_QD6500 0xc0
+#define QD_CONFIG_QD6580_A 0xa0
+#define QD_CONFIG_QD6580_B 0x50
+
+#define QD_CONTR_SEC_DISABLED 0x01
+
+#define QD_ID3 ((config & QD_CONFIG_ID3)!=0)
+
+#define QD_CONFIG(hwif) ((hwif)->config_data & 0x00ff)
+#define QD_CONTROL(hwif) (((hwif)->config_data & 0xff00) >> 8)
+
+#define QD_TIMING(drive) (byte)(((drive)->drive_data) & 0x00ff)
+#define QD_TIMREG(drive) (byte)((((drive)->drive_data) & 0xff00) >> 8)
+
+#define QD6500_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0c : 0x08))
+#define QD6580_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0a : 0x00))
+#define QD6580_DEF_DATA2 ((QD_TIM2_PORT<<8) | (QD_ID3 ? 0x0a : 0x00))
+#define QD_DEF_CONTR (0x40 | ((control & 0x02) ? 0x9f : 0x1f))
+
+#define QD_TESTVAL 0x19 /* safe value */
+
+/* Drive specific timing taken from DOS driver v3.7 */
+
+static struct qd65xx_timing_s {
+ s8 offset; /* ofset from the beginning of Model Number" */
+ char model[4]; /* 4 chars from Model number, no conversion */
+ s16 active; /* active time */
+ s16 recovery; /* recovery time */
+} qd65xx_timing [] = {
+ { 30, "2040", 110, 225 }, /* Conner CP30204 */
+ { 30, "2045", 135, 225 }, /* Conner CP30254 */
+ { 30, "1040", 155, 325 }, /* Conner CP30104 */
+ { 30, "1047", 135, 265 }, /* Conner CP30174 */
+ { 30, "5344", 135, 225 }, /* Conner CP3544 */
+ { 30, "01 4", 175, 405 }, /* Conner CP-3104 */
+ { 27, "C030", 175, 375 }, /* Conner CP3000 */
+ { 8, "PL42", 110, 295 }, /* Quantum LP240 */
+ { 8, "PL21", 110, 315 }, /* Quantum LP120 */
+ { 8, "PL25", 175, 385 }, /* Quantum LP52 */
+ { 4, "PA24", 110, 285 }, /* WD Piranha SP4200 */
+ { 6, "2200", 110, 260 }, /* WD Caviar AC2200 */
+ { 6, "3204", 110, 235 }, /* WD Caviar AC2340 */
+ { 6, "1202", 110, 265 }, /* WD Caviar AC2120 */
+ { 0, "DS3-", 135, 315 }, /* Teac SD340 */
+ { 8, "KM32", 175, 355 }, /* Toshiba MK234 */
+ { 2, "53A1", 175, 355 }, /* Seagate ST351A */
+ { 2, "4108", 175, 295 }, /* Seagate ST1480A */
+ { 2, "1344", 175, 335 }, /* Seagate ST3144A */
+ { 6, "7 12", 110, 225 }, /* Maxtor 7213A */
+ { 30, "02F4", 145, 295 }, /* Conner 3204F */
+ { 2, "1302", 175, 335 }, /* Seagate ST3120A */
+ { 2, "2334", 145, 265 }, /* Seagate ST3243A */
+ { 2, "2338", 145, 275 }, /* Seagate ST3283A */
+ { 2, "3309", 145, 275 }, /* Seagate ST3390A */
+ { 2, "5305", 145, 275 }, /* Seagate ST3550A */
+ { 2, "4100", 175, 295 }, /* Seagate ST1400A */
+ { 2, "4110", 175, 295 }, /* Seagate ST1401A */
+ { 2, "6300", 135, 265 }, /* Seagate ST3600A */
+ { 2, "5300", 135, 265 }, /* Seagate ST3500A */
+ { 6, "7 31", 135, 225 }, /* Maxtor 7131 AT */
+ { 6, "7 43", 115, 265 }, /* Maxtor 7345 AT */
+ { 6, "7 42", 110, 255 }, /* Maxtor 7245 AT */
+ { 6, "3 04", 135, 265 }, /* Maxtor 340 AT */
+ { 6, "61 0", 135, 285 }, /* WD AC160 */
+ { 6, "1107", 135, 235 }, /* WD AC1170 */
+ { 6, "2101", 110, 220 }, /* WD AC1210 */
+ { 6, "4202", 135, 245 }, /* WD AC2420 */
+ { 6, "41 0", 175, 355 }, /* WD Caviar 140 */
+ { 6, "82 0", 175, 355 }, /* WD Caviar 280 */
+ { 8, "PL01", 175, 375 }, /* Quantum LP105 */
+ { 8, "PL25", 110, 295 }, /* Quantum LP525 */
+ { 10, "4S 2", 175, 385 }, /* Quantum ELS42 */
+ { 10, "8S 5", 175, 385 }, /* Quantum ELS85 */
+ { 10, "1S72", 175, 385 }, /* Quantum ELS127 */
+ { 10, "1S07", 175, 385 }, /* Quantum ELS170 */
+ { 8, "ZE42", 135, 295 }, /* Quantum EZ240 */
+ { 8, "ZE21", 175, 385 }, /* Quantum EZ127 */
+ { 8, "ZE58", 175, 385 }, /* Quantum EZ85 */
+ { 8, "ZE24", 175, 385 }, /* Quantum EZ42 */
+ { 27, "C036", 155, 325 }, /* Conner CP30064 */
+ { 27, "C038", 155, 325 }, /* Conner CP30084 */
+ { 6, "2205", 110, 255 }, /* WDC AC2250 */
+ { 2, " CHA", 140, 415 }, /* WDC AH series; WDC AH260, WDC */
+ { 2, " CLA", 140, 415 }, /* WDC AL series: WDC AL2120, 2170, */
+ { 4, "UC41", 140, 415 }, /* WDC CU140 */
+ { 6, "1207", 130, 275 }, /* WDC AC2170 */
+ { 6, "2107", 130, 275 }, /* WDC AC1270 */
+ { 6, "5204", 130, 275 }, /* WDC AC2540 */
+ { 30, "3004", 110, 235 }, /* Conner CP30340 */
+ { 30, "0345", 135, 255 }, /* Conner CP30544 */
+ { 12, "12A3", 175, 320 }, /* MAXTOR LXT-213A */
+ { 12, "43A0", 145, 240 }, /* MAXTOR LXT-340A */
+ { 6, "7 21", 180, 290 }, /* Maxtor 7120 AT */
+ { 6, "7 71", 135, 240 }, /* Maxtor 7170 AT */
+ { 12, "45\0000", 110, 205 }, /* MAXTOR MXT-540 */
+ { 8, "PL11", 180, 290 }, /* QUANTUM LP110A */
+ { 8, "OG21", 150, 275 }, /* QUANTUM GO120 */
+ { 12, "42A5", 175, 320 }, /* MAXTOR LXT-245A */
+ { 2, "2309", 175, 295 }, /* ST3290A */
+ { 2, "3358", 180, 310 }, /* ST3385A */
+ { 2, "6355", 180, 310 }, /* ST3655A */
+ { 2, "1900", 175, 270 }, /* ST9100A */
+ { 2, "1954", 175, 270 }, /* ST9145A */
+ { 2, "1909", 175, 270 }, /* ST9190AG */
+ { 2, "2953", 175, 270 }, /* ST9235A */
+ { 2, "1359", 175, 270 }, /* ST3195A */
+ { 24, "3R11", 175, 290 }, /* ALPS ELECTRIC Co.,LTD, DR311C */
+ { 0, "2M26", 175, 215 }, /* M262XT-0Ah */
+ { 4, "2253", 175, 300 }, /* HP C2235A */
+ { 4, "-32A", 145, 245 }, /* H3133-A2 */
+ { 30, "0326", 150, 270 }, /* Samsung Electronics 120MB */
+ { 30, "3044", 110, 195 }, /* Conner CFA340A */
+ { 30, "43A0", 110, 195 }, /* Conner CFA340A */
+ { -1, " ", 175, 415 } /* unknown disk name */
+};
diff --git a/drivers/ide/legacy/umc8672.c b/drivers/ide/legacy/umc8672.c
new file mode 100644
index 00000000000..cdbdb2ff9f1
--- /dev/null
+++ b/drivers/ide/legacy/umc8672.c
@@ -0,0 +1,183 @@
+/*
+ * linux/drivers/ide/legacy/umc8672.c Version 0.05 Jul 31, 1996
+ *
+ * Copyright (C) 1995-1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * Principal Author/Maintainer: PODIEN@hml2.atlas.de (Wolfram Podien)
+ *
+ * This file provides support for the advanced features
+ * of the UMC 8672 IDE interface.
+ *
+ * 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 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
+ */
+
+/*
+ * VLB Controller Support from
+ * Wolfram Podien
+ * Rohoefe 3
+ * D28832 Achim
+ * Germany
+ *
+ * To enable UMC8672 support there must a lilo line like
+ * append="ide0=umc8672"...
+ * To set the speed according to the abilities of the hardware there must be a
+ * line like
+ * #define UMC_DRIVE0 11
+ * in the beginning of the driver, which sets the speed of drive 0 to 11 (there
+ * are some lines present). 0 - 11 are allowed speed values. These values are
+ * the results from the DOS speed test program supplied from UMC. 11 is the
+ * highest speed (about PIO mode 3)
+ */
+#define REALLY_SLOW_IO /* some systems can safely undef this */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+
+/*
+ * Default speeds. These can be changed with "auto-tune" and/or hdparm.
+ */
+#define UMC_DRIVE0 1 /* DOS measured drive speeds */
+#define UMC_DRIVE1 1 /* 0 to 11 allowed */
+#define UMC_DRIVE2 1 /* 11 = Fastest Speed */
+#define UMC_DRIVE3 1 /* In case of crash reduce speed */
+
+static u8 current_speeds[4] = {UMC_DRIVE0, UMC_DRIVE1, UMC_DRIVE2, UMC_DRIVE3};
+static const u8 pio_to_umc [5] = {0,3,7,10,11}; /* rough guesses */
+
+/* 0 1 2 3 4 5 6 7 8 9 10 11 */
+static const u8 speedtab [3][12] = {
+ {0xf, 0xb, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
+ {0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
+ {0xff,0xcb,0xc0,0x58,0x36,0x33,0x23,0x22,0x21,0x11,0x10,0x0}};
+
+static void out_umc (char port,char wert)
+{
+ outb_p(port,0x108);
+ outb_p(wert,0x109);
+}
+
+static inline u8 in_umc (char port)
+{
+ outb_p(port,0x108);
+ return inb_p(0x109);
+}
+
+static void umc_set_speeds (u8 speeds[])
+{
+ int i, tmp;
+
+ outb_p(0x5A,0x108); /* enable umc */
+
+ out_umc (0xd7,(speedtab[0][speeds[2]] | (speedtab[0][speeds[3]]<<4)));
+ out_umc (0xd6,(speedtab[0][speeds[0]] | (speedtab[0][speeds[1]]<<4)));
+ tmp = 0;
+ for (i = 3; i >= 0; i--) {
+ tmp = (tmp << 2) | speedtab[1][speeds[i]];
+ }
+ out_umc (0xdc,tmp);
+ for (i = 0;i < 4; i++) {
+ out_umc (0xd0+i,speedtab[2][speeds[i]]);
+ out_umc (0xd8+i,speedtab[2][speeds[i]]);
+ }
+ outb_p(0xa5,0x108); /* disable umc */
+
+ printk ("umc8672: drive speeds [0 to 11]: %d %d %d %d\n",
+ speeds[0], speeds[1], speeds[2], speeds[3]);
+}
+
+static void tune_umc (ide_drive_t *drive, u8 pio)
+{
+ unsigned long flags;
+ ide_hwgroup_t *hwgroup = ide_hwifs[HWIF(drive)->index^1].hwgroup;
+
+ 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]);
+ spin_lock_irqsave(&ide_lock, flags);
+ if (hwgroup && hwgroup->handler != NULL) {
+ printk(KERN_ERR "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);
+ }
+ spin_unlock_irqrestore(&ide_lock, flags);
+}
+
+static int __init umc8672_probe(void)
+{
+ unsigned long flags;
+ ide_hwif_t *hwif, *mate;
+
+ if (!request_region(0x108, 2, "umc8672")) {
+ printk(KERN_ERR "umc8672: ports 0x108-0x109 already in use.\n");
+ return 1;
+ }
+ local_irq_save(flags);
+ outb_p(0x5A,0x108); /* enable umc */
+ if (in_umc (0xd5) != 0xa0) {
+ local_irq_restore(flags);
+ printk(KERN_ERR "umc8672: not found\n");
+ release_region(0x108, 2);
+ return 1;
+ }
+ outb_p(0xa5,0x108); /* disable umc */
+
+ umc_set_speeds (current_speeds);
+ local_irq_restore(flags);
+
+ hwif = &ide_hwifs[0];
+ mate = &ide_hwifs[1];
+
+ hwif->chipset = ide_umc8672;
+ hwif->tuneproc = &tune_umc;
+ hwif->mate = mate;
+
+ mate->chipset = ide_umc8672;
+ mate->tuneproc = &tune_umc;
+ mate->mate = hwif;
+ mate->channel = 1;
+
+ probe_hwif_init(hwif);
+ probe_hwif_init(mate);
+
+ create_proc_ide_interfaces();
+
+ return 0;
+}
+
+/* Can be called directly from ide.c. */
+int __init umc8672_init(void)
+{
+ if (umc8672_probe())
+ return -ENODEV;
+ return 0;
+}
+
+#ifdef MODULE
+module_init(umc8672_init);
+#endif
+
+MODULE_AUTHOR("Wolfram Podien");
+MODULE_DESCRIPTION("Support for UMC 8672 IDE chipset");
+MODULE_LICENSE("GPL");