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!
Этот коммит содержится в:
13
drivers/ide/legacy/Makefile
Обычный файл
13
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
|
253
drivers/ide/legacy/ali14xx.c
Обычный файл
253
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");
|
235
drivers/ide/legacy/buddha.c
Обычный файл
235
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
165
drivers/ide/legacy/dtc2278.c
Обычный файл
165
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");
|
78
drivers/ide/legacy/falconide.c
Обычный файл
78
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);
|
||||
}
|
||||
}
|
186
drivers/ide/legacy/gayle.c
Обычный файл
186
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);
|
||||
}
|
||||
}
|
864
drivers/ide/legacy/hd.c
Обычный файл
864
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);
|
370
drivers/ide/legacy/ht6560b.c
Обычный файл
370
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");
|
481
drivers/ide/legacy/ide-cs.c
Обычный файл
481
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);
|
155
drivers/ide/legacy/macide.c
Обычный файл
155
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);
|
||||
}
|
||||
}
|
150
drivers/ide/legacy/q40ide.c
Обычный файл
150
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<73>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;
|
||||
}
|
||||
}
|
||||
|
511
drivers/ide/legacy/qd65xx.c
Обычный файл
511
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");
|
140
drivers/ide/legacy/qd65xx.h
Обычный файл
140
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 */
|
||||
};
|
183
drivers/ide/legacy/umc8672.c
Обычный файл
183
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");
|
Ссылка в новой задаче
Block a user