Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
1124
drivers/ide/Kconfig
Normal file
1124
drivers/ide/Kconfig
Normal file
File diff suppressed because it is too large
Load Diff
54
drivers/ide/Makefile
Normal file
54
drivers/ide/Makefile
Normal file
@@ -0,0 +1,54 @@
|
||||
#
|
||||
# Makefile for the kernel ata, atapi, and ide block device drivers.
|
||||
#
|
||||
# 12 September 2000, Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
|
||||
# Rewritten to use lists instead of if-statements.
|
||||
#
|
||||
# Note : at this point, these files are compiled on all systems.
|
||||
# In the future, some of these should be built conditionally.
|
||||
#
|
||||
# First come modules that register themselves with the core
|
||||
|
||||
EXTRA_CFLAGS += -Idrivers/ide
|
||||
|
||||
obj-$(CONFIG_BLK_DEV_IDE) += pci/
|
||||
|
||||
ide-core-y += ide.o ide-io.o ide-iops.o ide-lib.o ide-probe.o ide-taskfile.o
|
||||
|
||||
ide-core-$(CONFIG_BLK_DEV_CMD640) += pci/cmd640.o
|
||||
|
||||
# Core IDE code - must come before legacy
|
||||
ide-core-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o
|
||||
ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o
|
||||
ide-core-$(CONFIG_PROC_FS) += ide-proc.o
|
||||
ide-core-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o
|
||||
ide-core-$(CONFIG_BLK_DEV_IDEACPI) += ide-acpi.o
|
||||
|
||||
# built-in only drivers from arm/
|
||||
ide-core-$(CONFIG_IDE_ARM) += arm/ide_arm.o
|
||||
|
||||
# built-in only drivers from legacy/
|
||||
ide-core-$(CONFIG_BLK_DEV_BUDDHA) += legacy/buddha.o
|
||||
ide-core-$(CONFIG_BLK_DEV_FALCON_IDE) += legacy/falconide.o
|
||||
ide-core-$(CONFIG_BLK_DEV_GAYLE) += legacy/gayle.o
|
||||
ide-core-$(CONFIG_BLK_DEV_MAC_IDE) += legacy/macide.o
|
||||
ide-core-$(CONFIG_BLK_DEV_Q40IDE) += legacy/q40ide.o
|
||||
|
||||
# built-in only drivers from ppc/
|
||||
ide-core-$(CONFIG_BLK_DEV_MPC8xx_IDE) += ppc/mpc8xx.o
|
||||
ide-core-$(CONFIG_BLK_DEV_IDE_PMAC) += ppc/pmac.o
|
||||
|
||||
# built-in only drivers from h8300/
|
||||
ide-core-$(CONFIG_H8300) += h8300/ide-h8300.o
|
||||
|
||||
obj-$(CONFIG_BLK_DEV_IDE) += ide-core.o
|
||||
obj-$(CONFIG_IDE_GENERIC) += ide-generic.o
|
||||
|
||||
obj-$(CONFIG_BLK_DEV_IDEDISK) += ide-disk.o
|
||||
obj-$(CONFIG_BLK_DEV_IDECD) += ide-cd.o
|
||||
obj-$(CONFIG_BLK_DEV_IDETAPE) += ide-tape.o
|
||||
obj-$(CONFIG_BLK_DEV_IDEFLOPPY) += ide-floppy.o
|
||||
|
||||
obj-$(CONFIG_BLK_DEV_IDE) += legacy/ arm/ mips/
|
||||
obj-$(CONFIG_BLK_DEV_HD) += legacy/
|
||||
obj-$(CONFIG_ETRAX_IDE) += cris/
|
||||
7
drivers/ide/arm/Makefile
Normal file
7
drivers/ide/arm/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o
|
||||
obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o
|
||||
obj-$(CONFIG_BLK_DEV_IDE_BAST) += bast-ide.o
|
||||
obj-$(CONFIG_BLK_DEV_IDE_S3C) += s3c-ide.o
|
||||
|
||||
EXTRA_CFLAGS := -Idrivers/ide
|
||||
71
drivers/ide/arm/bast-ide.c
Normal file
71
drivers/ide/arm/bast-ide.c
Normal file
@@ -0,0 +1,71 @@
|
||||
/* linux/drivers/ide/arm/bast-ide.c
|
||||
*
|
||||
* Copyright (c) 2003-2004 Simtec Electronics
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/arch/map.h>
|
||||
#include <asm/arch/bast-map.h>
|
||||
#include <asm/arch/bast-irq.h>
|
||||
|
||||
/* list of registered interfaces */
|
||||
static ide_hwif_t *ifs[2];
|
||||
|
||||
static int __init
|
||||
bastide_register(unsigned int base, unsigned int aux, int irq,
|
||||
ide_hwif_t **hwif)
|
||||
{
|
||||
hw_regs_t hw;
|
||||
int i;
|
||||
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
|
||||
base += BAST_IDE_CS;
|
||||
aux += BAST_IDE_CS;
|
||||
|
||||
for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
|
||||
hw.io_ports[i] = (unsigned long)base;
|
||||
base += 0x20;
|
||||
}
|
||||
|
||||
hw.io_ports[IDE_CONTROL_OFFSET] = aux + (6 * 0x20);
|
||||
hw.irq = irq;
|
||||
|
||||
ide_register_hw(&hw, hwif);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init bastide_init(void)
|
||||
{
|
||||
/* we can treat the VR1000 and the BAST the same */
|
||||
|
||||
if (!(machine_is_bast() || machine_is_vr1000()))
|
||||
return 0;
|
||||
|
||||
printk("BAST: IDE driver, (c) 2003-2004 Simtec Electronics\n");
|
||||
|
||||
bastide_register(BAST_VA_IDEPRI, BAST_VA_IDEPRIAUX, IRQ_IDE0, &ifs[0]);
|
||||
bastide_register(BAST_VA_IDESEC, BAST_VA_IDESECAUX, IRQ_IDE1, &ifs[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(bastide_init);
|
||||
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Simtec BAST / Thorcom VR1000 IDE driver");
|
||||
853
drivers/ide/arm/icside.c
Normal file
853
drivers/ide/arm/icside.c
Normal file
@@ -0,0 +1,853 @@
|
||||
/*
|
||||
* linux/drivers/ide/arm/icside.c
|
||||
*
|
||||
* Copyright (c) 1996-2004 Russell King.
|
||||
*
|
||||
* Please note that this platform does not support 32-bit IDE IO.
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
#include <asm/ecard.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define ICS_IDENT_OFFSET 0x2280
|
||||
|
||||
#define ICS_ARCIN_V5_INTRSTAT 0x0000
|
||||
#define ICS_ARCIN_V5_INTROFFSET 0x0004
|
||||
#define ICS_ARCIN_V5_IDEOFFSET 0x2800
|
||||
#define ICS_ARCIN_V5_IDEALTOFFSET 0x2b80
|
||||
#define ICS_ARCIN_V5_IDESTEPPING 6
|
||||
|
||||
#define ICS_ARCIN_V6_IDEOFFSET_1 0x2000
|
||||
#define ICS_ARCIN_V6_INTROFFSET_1 0x2200
|
||||
#define ICS_ARCIN_V6_INTRSTAT_1 0x2290
|
||||
#define ICS_ARCIN_V6_IDEALTOFFSET_1 0x2380
|
||||
#define ICS_ARCIN_V6_IDEOFFSET_2 0x3000
|
||||
#define ICS_ARCIN_V6_INTROFFSET_2 0x3200
|
||||
#define ICS_ARCIN_V6_INTRSTAT_2 0x3290
|
||||
#define ICS_ARCIN_V6_IDEALTOFFSET_2 0x3380
|
||||
#define ICS_ARCIN_V6_IDESTEPPING 6
|
||||
|
||||
struct cardinfo {
|
||||
unsigned int dataoffset;
|
||||
unsigned int ctrloffset;
|
||||
unsigned int stepping;
|
||||
};
|
||||
|
||||
static struct cardinfo icside_cardinfo_v5 = {
|
||||
.dataoffset = ICS_ARCIN_V5_IDEOFFSET,
|
||||
.ctrloffset = ICS_ARCIN_V5_IDEALTOFFSET,
|
||||
.stepping = ICS_ARCIN_V5_IDESTEPPING,
|
||||
};
|
||||
|
||||
static struct cardinfo icside_cardinfo_v6_1 = {
|
||||
.dataoffset = ICS_ARCIN_V6_IDEOFFSET_1,
|
||||
.ctrloffset = ICS_ARCIN_V6_IDEALTOFFSET_1,
|
||||
.stepping = ICS_ARCIN_V6_IDESTEPPING,
|
||||
};
|
||||
|
||||
static struct cardinfo icside_cardinfo_v6_2 = {
|
||||
.dataoffset = ICS_ARCIN_V6_IDEOFFSET_2,
|
||||
.ctrloffset = ICS_ARCIN_V6_IDEALTOFFSET_2,
|
||||
.stepping = ICS_ARCIN_V6_IDESTEPPING,
|
||||
};
|
||||
|
||||
struct icside_state {
|
||||
unsigned int channel;
|
||||
unsigned int enabled;
|
||||
void __iomem *irq_port;
|
||||
void __iomem *ioc_base;
|
||||
unsigned int type;
|
||||
/* parent device... until the IDE core gets one of its own */
|
||||
struct device *dev;
|
||||
ide_hwif_t *hwif[2];
|
||||
};
|
||||
|
||||
#define ICS_TYPE_A3IN 0
|
||||
#define ICS_TYPE_A3USER 1
|
||||
#define ICS_TYPE_V6 3
|
||||
#define ICS_TYPE_V5 15
|
||||
#define ICS_TYPE_NOTYPE ((unsigned int)-1)
|
||||
|
||||
/* ---------------- Version 5 PCB Support Functions --------------------- */
|
||||
/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
|
||||
* Purpose : enable interrupts from card
|
||||
*/
|
||||
static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
|
||||
{
|
||||
struct icside_state *state = ec->irq_data;
|
||||
|
||||
writeb(0, state->irq_port + ICS_ARCIN_V5_INTROFFSET);
|
||||
}
|
||||
|
||||
/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
|
||||
* Purpose : disable interrupts from card
|
||||
*/
|
||||
static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
|
||||
{
|
||||
struct icside_state *state = ec->irq_data;
|
||||
|
||||
readb(state->irq_port + ICS_ARCIN_V5_INTROFFSET);
|
||||
}
|
||||
|
||||
static const expansioncard_ops_t icside_ops_arcin_v5 = {
|
||||
.irqenable = icside_irqenable_arcin_v5,
|
||||
.irqdisable = icside_irqdisable_arcin_v5,
|
||||
};
|
||||
|
||||
|
||||
/* ---------------- Version 6 PCB Support Functions --------------------- */
|
||||
/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
|
||||
* Purpose : enable interrupts from card
|
||||
*/
|
||||
static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
|
||||
{
|
||||
struct icside_state *state = ec->irq_data;
|
||||
void __iomem *base = state->irq_port;
|
||||
|
||||
state->enabled = 1;
|
||||
|
||||
switch (state->channel) {
|
||||
case 0:
|
||||
writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1);
|
||||
readb(base + ICS_ARCIN_V6_INTROFFSET_2);
|
||||
break;
|
||||
case 1:
|
||||
writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2);
|
||||
readb(base + ICS_ARCIN_V6_INTROFFSET_1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
|
||||
* Purpose : disable interrupts from card
|
||||
*/
|
||||
static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
|
||||
{
|
||||
struct icside_state *state = ec->irq_data;
|
||||
|
||||
state->enabled = 0;
|
||||
|
||||
readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
|
||||
readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
|
||||
}
|
||||
|
||||
/* Prototype: icside_irqprobe(struct expansion_card *ec)
|
||||
* Purpose : detect an active interrupt from card
|
||||
*/
|
||||
static int icside_irqpending_arcin_v6(struct expansion_card *ec)
|
||||
{
|
||||
struct icside_state *state = ec->irq_data;
|
||||
|
||||
return readb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
|
||||
readb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
|
||||
}
|
||||
|
||||
static const expansioncard_ops_t icside_ops_arcin_v6 = {
|
||||
.irqenable = icside_irqenable_arcin_v6,
|
||||
.irqdisable = icside_irqdisable_arcin_v6,
|
||||
.irqpending = icside_irqpending_arcin_v6,
|
||||
};
|
||||
|
||||
/*
|
||||
* Handle routing of interrupts. This is called before
|
||||
* we write the command to the drive.
|
||||
*/
|
||||
static void icside_maskproc(ide_drive_t *drive, int mask)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct icside_state *state = hwif->hwif_data;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
state->channel = hwif->channel;
|
||||
|
||||
if (state->enabled && !mask) {
|
||||
switch (hwif->channel) {
|
||||
case 0:
|
||||
writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
|
||||
readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
|
||||
break;
|
||||
case 1:
|
||||
writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
|
||||
readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
|
||||
readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
|
||||
/*
|
||||
* SG-DMA support.
|
||||
*
|
||||
* Similar to the BM-DMA, but we use the RiscPCs IOMD DMA controllers.
|
||||
* There is only one DMA controller per card, which means that only
|
||||
* one drive can be accessed at one time. NOTE! We do not enforce that
|
||||
* here, but we rely on the main IDE driver spotting that both
|
||||
* interfaces use the same IRQ, which should guarantee this.
|
||||
*/
|
||||
|
||||
static void icside_build_sglist(ide_drive_t *drive, struct request *rq)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct icside_state *state = hwif->hwif_data;
|
||||
struct scatterlist *sg = hwif->sg_table;
|
||||
|
||||
ide_map_sg(drive, rq);
|
||||
|
||||
if (rq_data_dir(rq) == READ)
|
||||
hwif->sg_dma_direction = DMA_FROM_DEVICE;
|
||||
else
|
||||
hwif->sg_dma_direction = DMA_TO_DEVICE;
|
||||
|
||||
hwif->sg_nents = dma_map_sg(state->dev, sg, hwif->sg_nents,
|
||||
hwif->sg_dma_direction);
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the IOMD to give the appropriate timings for the transfer
|
||||
* mode being requested. We take the advice of the ATA standards, and
|
||||
* calculate the cycle time based on the transfer mode, and the EIDE
|
||||
* MW DMA specs that the drive provides in the IDENTIFY command.
|
||||
*
|
||||
* We have the following IOMD DMA modes to choose from:
|
||||
*
|
||||
* Type Active Recovery Cycle
|
||||
* A 250 (250) 312 (550) 562 (800)
|
||||
* B 187 250 437
|
||||
* C 125 (125) 125 (375) 250 (500)
|
||||
* D 62 125 187
|
||||
*
|
||||
* (figures in brackets are actual measured timings)
|
||||
*
|
||||
* However, we also need to take care of the read/write active and
|
||||
* recovery timings:
|
||||
*
|
||||
* Read Write
|
||||
* Mode Active -- Recovery -- Cycle IOMD type
|
||||
* MW0 215 50 215 480 A
|
||||
* MW1 80 50 50 150 C
|
||||
* MW2 70 25 25 120 C
|
||||
*/
|
||||
static int icside_set_speed(ide_drive_t *drive, u8 xfer_mode)
|
||||
{
|
||||
int on = 0, cycle_time = 0, use_dma_info = 0;
|
||||
|
||||
/*
|
||||
* Limit the transfer speed to MW_DMA_2.
|
||||
*/
|
||||
if (xfer_mode > XFER_MW_DMA_2)
|
||||
xfer_mode = XFER_MW_DMA_2;
|
||||
|
||||
switch (xfer_mode) {
|
||||
case XFER_MW_DMA_2:
|
||||
cycle_time = 250;
|
||||
use_dma_info = 1;
|
||||
break;
|
||||
|
||||
case XFER_MW_DMA_1:
|
||||
cycle_time = 250;
|
||||
use_dma_info = 1;
|
||||
break;
|
||||
|
||||
case XFER_MW_DMA_0:
|
||||
cycle_time = 480;
|
||||
break;
|
||||
|
||||
case XFER_SW_DMA_2:
|
||||
case XFER_SW_DMA_1:
|
||||
case XFER_SW_DMA_0:
|
||||
cycle_time = 480;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're going to be doing MW_DMA_1 or MW_DMA_2, we should
|
||||
* take care to note the values in the ID...
|
||||
*/
|
||||
if (use_dma_info && drive->id->eide_dma_time > cycle_time)
|
||||
cycle_time = drive->id->eide_dma_time;
|
||||
|
||||
drive->drive_data = cycle_time;
|
||||
|
||||
if (cycle_time && ide_config_drive_speed(drive, xfer_mode) == 0)
|
||||
on = 1;
|
||||
else
|
||||
drive->drive_data = 480;
|
||||
|
||||
printk("%s: %s selected (peak %dMB/s)\n", drive->name,
|
||||
ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data);
|
||||
|
||||
drive->current_speed = xfer_mode;
|
||||
|
||||
return on;
|
||||
}
|
||||
|
||||
static void icside_dma_host_off(ide_drive_t *drive)
|
||||
{
|
||||
}
|
||||
|
||||
static void icside_dma_off_quietly(ide_drive_t *drive)
|
||||
{
|
||||
drive->using_dma = 0;
|
||||
}
|
||||
|
||||
static void icside_dma_host_on(ide_drive_t *drive)
|
||||
{
|
||||
}
|
||||
|
||||
static int icside_dma_on(ide_drive_t *drive)
|
||||
{
|
||||
drive->using_dma = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icside_dma_check(ide_drive_t *drive)
|
||||
{
|
||||
struct hd_driveid *id = drive->id;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
int xfer_mode = XFER_PIO_2;
|
||||
int on;
|
||||
|
||||
if (!(id->capability & 1) || !hwif->autodma)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Consult the list of known "bad" drives
|
||||
*/
|
||||
if (__ide_dma_bad_drive(drive))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Enable DMA on any drive that has multiword DMA
|
||||
*/
|
||||
if (id->field_valid & 2) {
|
||||
xfer_mode = ide_dma_speed(drive, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Consult the list of known "good" drives
|
||||
*/
|
||||
if (__ide_dma_good_drive(drive)) {
|
||||
if (id->eide_dma_time > 150)
|
||||
goto out;
|
||||
xfer_mode = XFER_MW_DMA_1;
|
||||
}
|
||||
|
||||
out:
|
||||
on = icside_set_speed(drive, xfer_mode);
|
||||
|
||||
return on ? 0 : -1;
|
||||
}
|
||||
|
||||
static int icside_dma_end(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct icside_state *state = hwif->hwif_data;
|
||||
|
||||
drive->waiting_for_dma = 0;
|
||||
|
||||
disable_dma(hwif->hw.dma);
|
||||
|
||||
/* Teardown mappings after DMA has completed. */
|
||||
dma_unmap_sg(state->dev, hwif->sg_table, hwif->sg_nents,
|
||||
hwif->sg_dma_direction);
|
||||
|
||||
return get_dma_residue(hwif->hw.dma) != 0;
|
||||
}
|
||||
|
||||
static void icside_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
|
||||
/* We can not enable DMA on both channels simultaneously. */
|
||||
BUG_ON(dma_channel_active(hwif->hw.dma));
|
||||
enable_dma(hwif->hw.dma);
|
||||
}
|
||||
|
||||
static int icside_dma_setup(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct request *rq = hwif->hwgroup->rq;
|
||||
unsigned int dma_mode;
|
||||
|
||||
if (rq_data_dir(rq))
|
||||
dma_mode = DMA_MODE_WRITE;
|
||||
else
|
||||
dma_mode = DMA_MODE_READ;
|
||||
|
||||
/*
|
||||
* We can not enable DMA on both channels.
|
||||
*/
|
||||
BUG_ON(dma_channel_active(hwif->hw.dma));
|
||||
|
||||
icside_build_sglist(drive, rq);
|
||||
|
||||
/*
|
||||
* Ensure that we have the right interrupt routed.
|
||||
*/
|
||||
icside_maskproc(drive, 0);
|
||||
|
||||
/*
|
||||
* Route the DMA signals to the correct interface.
|
||||
*/
|
||||
writeb(hwif->select_data, hwif->config_data);
|
||||
|
||||
/*
|
||||
* Select the correct timing for this drive.
|
||||
*/
|
||||
set_dma_speed(hwif->hw.dma, drive->drive_data);
|
||||
|
||||
/*
|
||||
* Tell the DMA engine about the SG table and
|
||||
* data direction.
|
||||
*/
|
||||
set_dma_sg(hwif->hw.dma, hwif->sg_table, hwif->sg_nents);
|
||||
set_dma_mode(hwif->hw.dma, dma_mode);
|
||||
|
||||
drive->waiting_for_dma = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void icside_dma_exec_cmd(ide_drive_t *drive, u8 cmd)
|
||||
{
|
||||
/* issue cmd to drive */
|
||||
ide_execute_command(drive, cmd, ide_dma_intr, 2 * WAIT_CMD, NULL);
|
||||
}
|
||||
|
||||
static int icside_dma_test_irq(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct icside_state *state = hwif->hwif_data;
|
||||
|
||||
return readb(state->irq_port +
|
||||
(hwif->channel ?
|
||||
ICS_ARCIN_V6_INTRSTAT_2 :
|
||||
ICS_ARCIN_V6_INTRSTAT_1)) & 1;
|
||||
}
|
||||
|
||||
static int icside_dma_timeout(ide_drive_t *drive)
|
||||
{
|
||||
printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name);
|
||||
|
||||
if (icside_dma_test_irq(drive))
|
||||
return 0;
|
||||
|
||||
ide_dump_status(drive, "DMA timeout",
|
||||
HWIF(drive)->INB(IDE_STATUS_REG));
|
||||
|
||||
return icside_dma_end(drive);
|
||||
}
|
||||
|
||||
static int icside_dma_lostirq(ide_drive_t *drive)
|
||||
{
|
||||
printk(KERN_ERR "%s: IRQ lost\n", drive->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void icside_dma_init(ide_hwif_t *hwif)
|
||||
{
|
||||
printk(" %s: SG-DMA", hwif->name);
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->mwdma_mask = 7; /* MW0..2 */
|
||||
hwif->swdma_mask = 7; /* SW0..2 */
|
||||
|
||||
hwif->dmatable_cpu = NULL;
|
||||
hwif->dmatable_dma = 0;
|
||||
hwif->speedproc = icside_set_speed;
|
||||
hwif->autodma = 1;
|
||||
|
||||
hwif->ide_dma_check = icside_dma_check;
|
||||
hwif->dma_host_off = icside_dma_host_off;
|
||||
hwif->dma_off_quietly = icside_dma_off_quietly;
|
||||
hwif->dma_host_on = icside_dma_host_on;
|
||||
hwif->ide_dma_on = icside_dma_on;
|
||||
hwif->dma_setup = icside_dma_setup;
|
||||
hwif->dma_exec_cmd = icside_dma_exec_cmd;
|
||||
hwif->dma_start = icside_dma_start;
|
||||
hwif->ide_dma_end = icside_dma_end;
|
||||
hwif->ide_dma_test_irq = icside_dma_test_irq;
|
||||
hwif->ide_dma_timeout = icside_dma_timeout;
|
||||
hwif->ide_dma_lostirq = icside_dma_lostirq;
|
||||
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
|
||||
printk(" capable%s\n", hwif->autodma ? ", auto-enable" : "");
|
||||
}
|
||||
#else
|
||||
#define icside_dma_init(hwif) (0)
|
||||
#endif
|
||||
|
||||
static ide_hwif_t *icside_find_hwif(unsigned long dataport)
|
||||
{
|
||||
ide_hwif_t *hwif;
|
||||
int index;
|
||||
|
||||
for (index = 0; index < MAX_HWIFS; ++index) {
|
||||
hwif = &ide_hwifs[index];
|
||||
if (hwif->io_ports[IDE_DATA_OFFSET] == dataport)
|
||||
goto found;
|
||||
}
|
||||
|
||||
for (index = 0; index < MAX_HWIFS; ++index) {
|
||||
hwif = &ide_hwifs[index];
|
||||
if (!hwif->io_ports[IDE_DATA_OFFSET])
|
||||
goto found;
|
||||
}
|
||||
|
||||
hwif = NULL;
|
||||
found:
|
||||
return hwif;
|
||||
}
|
||||
|
||||
static ide_hwif_t *
|
||||
icside_setup(void __iomem *base, struct cardinfo *info, struct expansion_card *ec)
|
||||
{
|
||||
unsigned long port = (unsigned long)base + info->dataoffset;
|
||||
ide_hwif_t *hwif;
|
||||
|
||||
hwif = icside_find_hwif(port);
|
||||
if (hwif) {
|
||||
int i;
|
||||
|
||||
memset(&hwif->hw, 0, sizeof(hw_regs_t));
|
||||
|
||||
/*
|
||||
* Ensure we're using MMIO
|
||||
*/
|
||||
default_hwif_mmiops(hwif);
|
||||
hwif->mmio = 1;
|
||||
|
||||
for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
|
||||
hwif->hw.io_ports[i] = port;
|
||||
hwif->io_ports[i] = port;
|
||||
port += 1 << info->stepping;
|
||||
}
|
||||
hwif->hw.io_ports[IDE_CONTROL_OFFSET] = (unsigned long)base + info->ctrloffset;
|
||||
hwif->io_ports[IDE_CONTROL_OFFSET] = (unsigned long)base + info->ctrloffset;
|
||||
hwif->hw.irq = ec->irq;
|
||||
hwif->irq = ec->irq;
|
||||
hwif->noprobe = 0;
|
||||
hwif->chipset = ide_acorn;
|
||||
hwif->gendev.parent = &ec->dev;
|
||||
}
|
||||
|
||||
return hwif;
|
||||
}
|
||||
|
||||
static int __init
|
||||
icside_register_v5(struct icside_state *state, struct expansion_card *ec)
|
||||
{
|
||||
ide_hwif_t *hwif;
|
||||
void __iomem *base;
|
||||
|
||||
base = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
|
||||
ecard_resource_len(ec, ECARD_RES_MEMC));
|
||||
if (!base)
|
||||
return -ENOMEM;
|
||||
|
||||
state->irq_port = base;
|
||||
|
||||
ec->irqaddr = base + ICS_ARCIN_V5_INTRSTAT;
|
||||
ec->irqmask = 1;
|
||||
ec->irq_data = state;
|
||||
ec->ops = &icside_ops_arcin_v5;
|
||||
|
||||
/*
|
||||
* Be on the safe side - disable interrupts
|
||||
*/
|
||||
icside_irqdisable_arcin_v5(ec, 0);
|
||||
|
||||
hwif = icside_setup(base, &icside_cardinfo_v5, ec);
|
||||
if (!hwif) {
|
||||
iounmap(base);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
state->hwif[0] = hwif;
|
||||
|
||||
probe_hwif_init(hwif);
|
||||
create_proc_ide_interfaces();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init
|
||||
icside_register_v6(struct icside_state *state, struct expansion_card *ec)
|
||||
{
|
||||
ide_hwif_t *hwif, *mate;
|
||||
void __iomem *ioc_base, *easi_base;
|
||||
unsigned int sel = 0;
|
||||
int ret;
|
||||
|
||||
ioc_base = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST),
|
||||
ecard_resource_len(ec, ECARD_RES_IOCFAST));
|
||||
if (!ioc_base) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
easi_base = ioc_base;
|
||||
|
||||
if (ecard_resource_flags(ec, ECARD_RES_EASI)) {
|
||||
easi_base = ioremap(ecard_resource_start(ec, ECARD_RES_EASI),
|
||||
ecard_resource_len(ec, ECARD_RES_EASI));
|
||||
if (!easi_base) {
|
||||
ret = -ENOMEM;
|
||||
goto unmap_slot;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable access to the EASI region.
|
||||
*/
|
||||
sel = 1 << 5;
|
||||
}
|
||||
|
||||
writeb(sel, ioc_base);
|
||||
|
||||
ec->irq_data = state;
|
||||
ec->ops = &icside_ops_arcin_v6;
|
||||
|
||||
state->irq_port = easi_base;
|
||||
state->ioc_base = ioc_base;
|
||||
|
||||
/*
|
||||
* Be on the safe side - disable interrupts
|
||||
*/
|
||||
icside_irqdisable_arcin_v6(ec, 0);
|
||||
|
||||
/*
|
||||
* Find and register the interfaces.
|
||||
*/
|
||||
hwif = icside_setup(easi_base, &icside_cardinfo_v6_1, ec);
|
||||
mate = icside_setup(easi_base, &icside_cardinfo_v6_2, ec);
|
||||
|
||||
if (!hwif || !mate) {
|
||||
ret = -ENODEV;
|
||||
goto unmap_port;
|
||||
}
|
||||
|
||||
state->hwif[0] = hwif;
|
||||
state->hwif[1] = mate;
|
||||
|
||||
hwif->maskproc = icside_maskproc;
|
||||
hwif->channel = 0;
|
||||
hwif->hwif_data = state;
|
||||
hwif->mate = mate;
|
||||
hwif->serialized = 1;
|
||||
hwif->config_data = (unsigned long)ioc_base;
|
||||
hwif->select_data = sel;
|
||||
hwif->hw.dma = ec->dma;
|
||||
|
||||
mate->maskproc = icside_maskproc;
|
||||
mate->channel = 1;
|
||||
mate->hwif_data = state;
|
||||
mate->mate = hwif;
|
||||
mate->serialized = 1;
|
||||
mate->config_data = (unsigned long)ioc_base;
|
||||
mate->select_data = sel | 1;
|
||||
mate->hw.dma = ec->dma;
|
||||
|
||||
if (ec->dma != NO_DMA && !request_dma(ec->dma, hwif->name)) {
|
||||
icside_dma_init(hwif);
|
||||
icside_dma_init(mate);
|
||||
}
|
||||
|
||||
probe_hwif_init(hwif);
|
||||
probe_hwif_init(mate);
|
||||
create_proc_ide_interfaces();
|
||||
|
||||
return 0;
|
||||
|
||||
unmap_port:
|
||||
if (easi_base != ioc_base)
|
||||
iounmap(easi_base);
|
||||
unmap_slot:
|
||||
iounmap(ioc_base);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit
|
||||
icside_probe(struct expansion_card *ec, const struct ecard_id *id)
|
||||
{
|
||||
struct icside_state *state;
|
||||
void __iomem *idmem;
|
||||
int ret;
|
||||
|
||||
ret = ecard_request_resources(ec);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
state = kmalloc(sizeof(struct icside_state), GFP_KERNEL);
|
||||
if (!state) {
|
||||
ret = -ENOMEM;
|
||||
goto release;
|
||||
}
|
||||
|
||||
memset(state, 0, sizeof(state));
|
||||
state->type = ICS_TYPE_NOTYPE;
|
||||
state->dev = &ec->dev;
|
||||
|
||||
idmem = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST),
|
||||
ecard_resource_len(ec, ECARD_RES_IOCFAST));
|
||||
if (idmem) {
|
||||
unsigned int type;
|
||||
|
||||
type = readb(idmem + ICS_IDENT_OFFSET) & 1;
|
||||
type |= (readb(idmem + ICS_IDENT_OFFSET + 4) & 1) << 1;
|
||||
type |= (readb(idmem + ICS_IDENT_OFFSET + 8) & 1) << 2;
|
||||
type |= (readb(idmem + ICS_IDENT_OFFSET + 12) & 1) << 3;
|
||||
iounmap(idmem);
|
||||
|
||||
state->type = type;
|
||||
}
|
||||
|
||||
switch (state->type) {
|
||||
case ICS_TYPE_A3IN:
|
||||
dev_warn(&ec->dev, "A3IN unsupported\n");
|
||||
ret = -ENODEV;
|
||||
break;
|
||||
|
||||
case ICS_TYPE_A3USER:
|
||||
dev_warn(&ec->dev, "A3USER unsupported\n");
|
||||
ret = -ENODEV;
|
||||
break;
|
||||
|
||||
case ICS_TYPE_V5:
|
||||
ret = icside_register_v5(state, ec);
|
||||
break;
|
||||
|
||||
case ICS_TYPE_V6:
|
||||
ret = icside_register_v6(state, ec);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_warn(&ec->dev, "unknown interface type\n");
|
||||
ret = -ENODEV;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
ecard_set_drvdata(ec, state);
|
||||
goto out;
|
||||
}
|
||||
|
||||
kfree(state);
|
||||
release:
|
||||
ecard_release_resources(ec);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __devexit icside_remove(struct expansion_card *ec)
|
||||
{
|
||||
struct icside_state *state = ecard_get_drvdata(ec);
|
||||
|
||||
switch (state->type) {
|
||||
case ICS_TYPE_V5:
|
||||
/* FIXME: tell IDE to stop using the interface */
|
||||
|
||||
/* Disable interrupts */
|
||||
icside_irqdisable_arcin_v5(ec, 0);
|
||||
break;
|
||||
|
||||
case ICS_TYPE_V6:
|
||||
/* FIXME: tell IDE to stop using the interface */
|
||||
if (ec->dma != NO_DMA)
|
||||
free_dma(ec->dma);
|
||||
|
||||
/* Disable interrupts */
|
||||
icside_irqdisable_arcin_v6(ec, 0);
|
||||
|
||||
/* Reset the ROM pointer/EASI selection */
|
||||
writeb(0, state->ioc_base);
|
||||
break;
|
||||
}
|
||||
|
||||
ecard_set_drvdata(ec, NULL);
|
||||
ec->ops = NULL;
|
||||
ec->irq_data = NULL;
|
||||
|
||||
if (state->ioc_base)
|
||||
iounmap(state->ioc_base);
|
||||
if (state->ioc_base != state->irq_port)
|
||||
iounmap(state->irq_port);
|
||||
|
||||
kfree(state);
|
||||
ecard_release_resources(ec);
|
||||
}
|
||||
|
||||
static void icside_shutdown(struct expansion_card *ec)
|
||||
{
|
||||
struct icside_state *state = ecard_get_drvdata(ec);
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Disable interrupts from this card. We need to do
|
||||
* this before disabling EASI since we may be accessing
|
||||
* this register via that region.
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
ec->ops->irqdisable(ec, 0);
|
||||
local_irq_restore(flags);
|
||||
|
||||
/*
|
||||
* Reset the ROM pointer so that we can read the ROM
|
||||
* after a soft reboot. This also disables access to
|
||||
* the IDE taskfile via the EASI region.
|
||||
*/
|
||||
if (state->ioc_base)
|
||||
writeb(0, state->ioc_base);
|
||||
}
|
||||
|
||||
static const struct ecard_id icside_ids[] = {
|
||||
{ MANU_ICS, PROD_ICS_IDE },
|
||||
{ MANU_ICS2, PROD_ICS2_IDE },
|
||||
{ 0xffff, 0xffff }
|
||||
};
|
||||
|
||||
static struct ecard_driver icside_driver = {
|
||||
.probe = icside_probe,
|
||||
.remove = __devexit_p(icside_remove),
|
||||
.shutdown = icside_shutdown,
|
||||
.id_table = icside_ids,
|
||||
.drv = {
|
||||
.name = "icside",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init icside_init(void)
|
||||
{
|
||||
return ecard_register_driver(&icside_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("ICS IDE driver");
|
||||
|
||||
module_init(icside_init);
|
||||
43
drivers/ide/arm/ide_arm.c
Normal file
43
drivers/ide/arm/ide_arm.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* ARM/ARM26 default IDE host driver
|
||||
*
|
||||
* Copyright (C) 2004 Bartlomiej Zolnierkiewicz
|
||||
* Based on code by: Russell King, Ian Molton and Alexander Schulz.
|
||||
*
|
||||
* May be copied or modified under the terms of the GNU General Public License.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#ifdef CONFIG_ARM26
|
||||
# define IDE_ARM_HOST (machine_is_a5k())
|
||||
#else
|
||||
# define IDE_ARM_HOST (1)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_CLPS7500
|
||||
# include <asm/arch/hardware.h>
|
||||
#
|
||||
# define IDE_ARM_IO (ISASLOT_IO + 0x1f0)
|
||||
# define IDE_ARM_IRQ IRQ_ISA_14
|
||||
#else
|
||||
# define IDE_ARM_IO 0x1f0
|
||||
# define IDE_ARM_IRQ IRQ_HARDDISK
|
||||
#endif
|
||||
|
||||
void __init ide_arm_init(void)
|
||||
{
|
||||
if (IDE_ARM_HOST) {
|
||||
hw_regs_t hw;
|
||||
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
ide_std_init_ports(&hw, IDE_ARM_IO, IDE_ARM_IO + 0x206);
|
||||
hw.irq = IDE_ARM_IRQ;
|
||||
ide_register_hw(&hw, NULL);
|
||||
}
|
||||
}
|
||||
125
drivers/ide/arm/rapide.c
Normal file
125
drivers/ide/arm/rapide.c
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* linux/drivers/ide/arm/rapide.c
|
||||
*
|
||||
* Copyright (c) 1996-2002 Russell King.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/ecard.h>
|
||||
|
||||
/*
|
||||
* Something like this really should be in generic code, but isn't.
|
||||
*/
|
||||
static ide_hwif_t *
|
||||
rapide_locate_hwif(void __iomem *base, void __iomem *ctrl, unsigned int sz, int irq)
|
||||
{
|
||||
unsigned long port = (unsigned long)base;
|
||||
ide_hwif_t *hwif;
|
||||
int index, i;
|
||||
|
||||
for (index = 0; index < MAX_HWIFS; ++index) {
|
||||
hwif = ide_hwifs + index;
|
||||
if (hwif->io_ports[IDE_DATA_OFFSET] == port)
|
||||
goto found;
|
||||
}
|
||||
|
||||
for (index = 0; index < MAX_HWIFS; ++index) {
|
||||
hwif = ide_hwifs + index;
|
||||
if (hwif->io_ports[IDE_DATA_OFFSET] == 0)
|
||||
goto found;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
found:
|
||||
for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
|
||||
hwif->hw.io_ports[i] = port;
|
||||
hwif->io_ports[i] = port;
|
||||
port += sz;
|
||||
}
|
||||
hwif->hw.io_ports[IDE_CONTROL_OFFSET] = (unsigned long)ctrl;
|
||||
hwif->io_ports[IDE_CONTROL_OFFSET] = (unsigned long)ctrl;
|
||||
hwif->hw.irq = hwif->irq = irq;
|
||||
hwif->mmio = 1;
|
||||
default_hwif_mmiops(hwif);
|
||||
|
||||
return hwif;
|
||||
}
|
||||
|
||||
static int __devinit
|
||||
rapide_probe(struct expansion_card *ec, const struct ecard_id *id)
|
||||
{
|
||||
ide_hwif_t *hwif;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
|
||||
ret = ecard_request_resources(ec);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
base = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
|
||||
ecard_resource_len(ec, ECARD_RES_MEMC));
|
||||
if (!base) {
|
||||
ret = -ENOMEM;
|
||||
goto release;
|
||||
}
|
||||
|
||||
hwif = rapide_locate_hwif(base, base + 0x818, 1 << 6, ec->irq);
|
||||
if (hwif) {
|
||||
hwif->hwif_data = base;
|
||||
hwif->gendev.parent = &ec->dev;
|
||||
hwif->noprobe = 0;
|
||||
probe_hwif_init(hwif);
|
||||
create_proc_ide_interfaces();
|
||||
ecard_set_drvdata(ec, hwif);
|
||||
goto out;
|
||||
}
|
||||
|
||||
iounmap(base);
|
||||
release:
|
||||
ecard_release_resources(ec);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __devexit rapide_remove(struct expansion_card *ec)
|
||||
{
|
||||
ide_hwif_t *hwif = ecard_get_drvdata(ec);
|
||||
|
||||
ecard_set_drvdata(ec, NULL);
|
||||
|
||||
/* there must be a better way */
|
||||
ide_unregister(hwif - ide_hwifs);
|
||||
iounmap(hwif->hwif_data);
|
||||
ecard_release_resources(ec);
|
||||
}
|
||||
|
||||
static struct ecard_id rapide_ids[] = {
|
||||
{ MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 },
|
||||
{ 0xffff, 0xffff }
|
||||
};
|
||||
|
||||
static struct ecard_driver rapide_driver = {
|
||||
.probe = rapide_probe,
|
||||
.remove = __devexit_p(rapide_remove),
|
||||
.id_table = rapide_ids,
|
||||
.drv = {
|
||||
.name = "rapide",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init rapide_init(void)
|
||||
{
|
||||
return ecard_register_driver(&rapide_driver);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Yellowstone RAPIDE driver");
|
||||
|
||||
module_init(rapide_init);
|
||||
1231
drivers/ide/arm/s3c-ide.c
Normal file
1231
drivers/ide/arm/s3c-ide.c
Normal file
File diff suppressed because it is too large
Load Diff
113
drivers/ide/arm/s3c-ide.h
Normal file
113
drivers/ide/arm/s3c-ide.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* $Id: s3c-ide.h,v 1.2 2008/06/11 00:56:02 scsuh Exp $
|
||||
*
|
||||
* drivers/ide/arm/s3c-ide.h
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
PIO0, PIO1, PIO2, PIO3, PIO4
|
||||
} PIOMODE;
|
||||
|
||||
typedef enum {
|
||||
UDMA0, UDMA1, UDMA2, UDMA3, UDMA4
|
||||
} UDMAMODE;
|
||||
|
||||
typedef enum {
|
||||
ATA_CMD_STOP, ATA_CMD_START, ATA_CMD_ABORT, ATA_CMD_CONTINUE
|
||||
} ATA_TRANSFER_CMD;
|
||||
|
||||
typedef enum {
|
||||
ATA_INT_XFER_DONE, ATA_INT_UDMA_HOLD, ATA_INT_IRQ,
|
||||
ATA_INT_TBUF_FULL, ATA_INT_SBUF_EMPTY
|
||||
} ATA_INT_SRC;
|
||||
|
||||
typedef enum {
|
||||
PIO_CPU, PIO_DMA, UDMA
|
||||
} ATA_MODE;
|
||||
|
||||
typedef enum {
|
||||
IDLE, BUSYW, PREP, BUSYR, PAUSER, PAUSEW, PAUSER2
|
||||
} BUS_STATE;
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDE_S3C_UDMA
|
||||
#define DMA_WAIT_TIMEOUT 100
|
||||
#define NUM_DESCRIPTORS PRD_ENTRIES
|
||||
#else
|
||||
#define NUM_DESCRIPTORS 2
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/*
|
||||
* This will enable the device to be powered up when write() or read()
|
||||
* is called. If this is not defined, the driver will return -EBUSY.
|
||||
*/
|
||||
#define WAKE_ON_ACCESS 1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
spinlock_t lock; /* Used to block on state transitions */
|
||||
unsigned stopped; /* USed to signaling device is stopped */
|
||||
} pm_state;
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ulong addr; /* Used to block on state transitions */
|
||||
ulong len; /* Power Managers device structure */
|
||||
} dma_queue_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 tx_dev_id, rx_dev_id, target_dev_id;
|
||||
ide_hwif_t *hwif;
|
||||
#ifdef CONFIG_BLK_DEV_IDE_S3C_UDMA
|
||||
ide_drive_t *drive;
|
||||
u8 white_list, black_list;
|
||||
|
||||
uint index; /* current queue index */
|
||||
uint queue_size; /* total queue size requested */
|
||||
dma_queue_t table[NUM_DESCRIPTORS];
|
||||
uint irq_sta;
|
||||
uint pseudo_dma; /* in DMA session */
|
||||
#endif
|
||||
struct platform_device *dev;
|
||||
int irq;
|
||||
void __iomem *regbase;
|
||||
ulong piotime[5];
|
||||
ulong udmatime[5];
|
||||
struct clk *clk;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
pm_state pm;
|
||||
#endif
|
||||
} s3c_ide_hwif_t;
|
||||
|
||||
/* s3c2443 has some faults in bus sharing.
|
||||
* so must use lock and unlock in UDMA. by scsuh.
|
||||
*/
|
||||
#ifdef CONFIG_ARCH_S3C2443
|
||||
void lock_rom_bus(void);
|
||||
void unlock_rom_bus(void);
|
||||
#endif
|
||||
|
||||
3
drivers/ide/cris/Makefile
Normal file
3
drivers/ide/cris/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
EXTRA_CFLAGS += -Idrivers/ide
|
||||
|
||||
obj-y += ide-cris.o
|
||||
1098
drivers/ide/cris/ide-cris.c
Normal file
1098
drivers/ide/cris/ide-cris.c
Normal file
File diff suppressed because it is too large
Load Diff
116
drivers/ide/h8300/ide-h8300.c
Normal file
116
drivers/ide/h8300/ide-h8300.c
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* drivers/ide/h8300/ide-h8300.c
|
||||
* H8/300 generic IDE interface
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#define bswap(d) \
|
||||
({ \
|
||||
u16 r; \
|
||||
__asm__("mov.b %w1,r1h\n\t" \
|
||||
"mov.b %x1,r1l\n\t" \
|
||||
"mov.w r1,%0" \
|
||||
:"=r"(r) \
|
||||
:"r"(d) \
|
||||
:"er1"); \
|
||||
(r); \
|
||||
})
|
||||
|
||||
static void mm_outw(u16 d, unsigned long a)
|
||||
{
|
||||
__asm__("mov.b %w0,r2h\n\t"
|
||||
"mov.b %x0,r2l\n\t"
|
||||
"mov.w r2,@%1"
|
||||
:
|
||||
:"r"(d),"r"(a)
|
||||
:"er2");
|
||||
}
|
||||
|
||||
static u16 mm_inw(unsigned long a)
|
||||
{
|
||||
register u16 r __asm__("er0");
|
||||
__asm__("mov.w @%1,r2\n\t"
|
||||
"mov.b r2l,%x0\n\t"
|
||||
"mov.b r2h,%w0"
|
||||
:"=r"(r)
|
||||
:"r"(a)
|
||||
:"er2");
|
||||
return r;
|
||||
}
|
||||
|
||||
static void mm_outsw(unsigned long addr, void *buf, u32 len)
|
||||
{
|
||||
unsigned short *bp = (unsigned short *)buf;
|
||||
for (; len > 0; len--, bp++)
|
||||
*(volatile u16 *)addr = bswap(*bp);
|
||||
}
|
||||
|
||||
static void mm_insw(unsigned long addr, void *buf, u32 len)
|
||||
{
|
||||
unsigned short *bp = (unsigned short *)buf;
|
||||
for (; len > 0; len--, bp++)
|
||||
*bp = bswap(*(volatile u16 *)addr);
|
||||
}
|
||||
|
||||
#define H8300_IDE_GAP (2)
|
||||
|
||||
static inline void hw_setup(hw_regs_t *hw)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(hw, 0, sizeof(hw_regs_t));
|
||||
for (i = 0; i <= IDE_STATUS_OFFSET; i++)
|
||||
hw->io_ports[i] = CONFIG_H8300_IDE_BASE + H8300_IDE_GAP*i;
|
||||
hw->io_ports[IDE_CONTROL_OFFSET] = CONFIG_H8300_IDE_ALT;
|
||||
hw->irq = EXT_IRQ0 + CONFIG_H8300_IDE_IRQ;
|
||||
hw->dma = NO_DMA;
|
||||
hw->chipset = ide_generic;
|
||||
}
|
||||
|
||||
static inline void hwif_setup(ide_hwif_t *hwif)
|
||||
{
|
||||
default_hwif_iops(hwif);
|
||||
|
||||
hwif->mmio = 1;
|
||||
hwif->OUTW = mm_outw;
|
||||
hwif->OUTSW = mm_outsw;
|
||||
hwif->INW = mm_inw;
|
||||
hwif->INSW = mm_insw;
|
||||
hwif->OUTSL = NULL;
|
||||
hwif->INSL = NULL;
|
||||
}
|
||||
|
||||
void __init h8300_ide_init(void)
|
||||
{
|
||||
hw_regs_t hw;
|
||||
ide_hwif_t *hwif;
|
||||
int idx;
|
||||
|
||||
if (!request_region(CONFIG_H8300_IDE_BASE, H8300_IDE_GAP*8, "ide-h8300"))
|
||||
goto out_busy;
|
||||
if (!request_region(CONFIG_H8300_IDE_ALT, H8300_IDE_GAP, "ide-h8300")) {
|
||||
release_region(CONFIG_H8300_IDE_BASE, H8300_IDE_GAP*8);
|
||||
goto out_busy;
|
||||
}
|
||||
|
||||
hw_setup(&hw);
|
||||
|
||||
/* register if */
|
||||
idx = ide_register_hw(&hw, &hwif);
|
||||
if (idx == -1) {
|
||||
printk(KERN_ERR "ide-h8300: IDE I/F register failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hwif_setup(hwif);
|
||||
printk(KERN_INFO "ide%d: H8/300 generic IDE interface\n", idx);
|
||||
return;
|
||||
|
||||
out_busy:
|
||||
printk(KERN_ERR "ide-h8300: IDE I/F resource already used.\n");
|
||||
}
|
||||
697
drivers/ide/ide-acpi.c
Normal file
697
drivers/ide/ide-acpi.c
Normal file
@@ -0,0 +1,697 @@
|
||||
/*
|
||||
* ide-acpi.c
|
||||
* Provides ACPI support for IDE drives.
|
||||
*
|
||||
* Copyright (C) 2005 Intel Corp.
|
||||
* Copyright (C) 2005 Randy Dunlap
|
||||
* Copyright (C) 2006 SUSE Linux Products GmbH
|
||||
* Copyright (C) 2006 Hannes Reinecke
|
||||
*/
|
||||
|
||||
#include <linux/ata.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <acpi/acpi.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <acpi/acpi_bus.h>
|
||||
#include <acpi/acnames.h>
|
||||
#include <acpi/acnamesp.h>
|
||||
#include <acpi/acparser.h>
|
||||
#include <acpi/acexcep.h>
|
||||
#include <acpi/acmacros.h>
|
||||
#include <acpi/actypes.h>
|
||||
|
||||
#define REGS_PER_GTF 7
|
||||
struct taskfile_array {
|
||||
u8 tfa[REGS_PER_GTF]; /* regs. 0x1f1 - 0x1f7 */
|
||||
};
|
||||
|
||||
struct GTM_buffer {
|
||||
u32 PIO_speed0;
|
||||
u32 DMA_speed0;
|
||||
u32 PIO_speed1;
|
||||
u32 DMA_speed1;
|
||||
u32 GTM_flags;
|
||||
};
|
||||
|
||||
struct ide_acpi_drive_link {
|
||||
ide_drive_t *drive;
|
||||
acpi_handle obj_handle;
|
||||
u8 idbuff[512];
|
||||
};
|
||||
|
||||
struct ide_acpi_hwif_link {
|
||||
ide_hwif_t *hwif;
|
||||
acpi_handle obj_handle;
|
||||
struct GTM_buffer gtm;
|
||||
struct ide_acpi_drive_link master;
|
||||
struct ide_acpi_drive_link slave;
|
||||
};
|
||||
|
||||
#undef DEBUGGING
|
||||
/* note: adds function name and KERN_DEBUG */
|
||||
#ifdef DEBUGGING
|
||||
#define DEBPRINT(fmt, args...) \
|
||||
printk(KERN_DEBUG "%s: " fmt, __FUNCTION__, ## args)
|
||||
#else
|
||||
#define DEBPRINT(fmt, args...) do {} while (0)
|
||||
#endif /* DEBUGGING */
|
||||
|
||||
extern int ide_noacpi;
|
||||
extern int ide_noacpitfs;
|
||||
extern int ide_noacpionboot;
|
||||
|
||||
/**
|
||||
* ide_get_dev_handle - finds acpi_handle and PCI device.function
|
||||
* @dev: device to locate
|
||||
* @handle: returned acpi_handle for @dev
|
||||
* @pcidevfn: return PCI device.func for @dev
|
||||
*
|
||||
* Returns the ACPI object handle to the corresponding PCI device.
|
||||
*
|
||||
* Returns 0 on success, <0 on error.
|
||||
*/
|
||||
static int ide_get_dev_handle(struct device *dev, acpi_handle *handle,
|
||||
acpi_integer *pcidevfn)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
unsigned int bus, devnum, func;
|
||||
acpi_integer addr;
|
||||
acpi_handle dev_handle;
|
||||
struct acpi_buffer buffer = {.length = ACPI_ALLOCATE_BUFFER,
|
||||
.pointer = NULL};
|
||||
acpi_status status;
|
||||
struct acpi_device_info *dinfo = NULL;
|
||||
int ret = -ENODEV;
|
||||
|
||||
bus = pdev->bus->number;
|
||||
devnum = PCI_SLOT(pdev->devfn);
|
||||
func = PCI_FUNC(pdev->devfn);
|
||||
/* ACPI _ADR encoding for PCI bus: */
|
||||
addr = (acpi_integer)(devnum << 16 | func);
|
||||
|
||||
DEBPRINT("ENTER: pci %02x:%02x.%01x\n", bus, devnum, func);
|
||||
|
||||
dev_handle = DEVICE_ACPI_HANDLE(dev);
|
||||
if (!dev_handle) {
|
||||
DEBPRINT("no acpi handle for device\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
status = acpi_get_object_info(dev_handle, &buffer);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
DEBPRINT("get_object_info for device failed\n");
|
||||
goto err;
|
||||
}
|
||||
dinfo = buffer.pointer;
|
||||
if (dinfo && (dinfo->valid & ACPI_VALID_ADR) &&
|
||||
dinfo->address == addr) {
|
||||
*pcidevfn = addr;
|
||||
*handle = dev_handle;
|
||||
} else {
|
||||
DEBPRINT("get_object_info for device has wrong "
|
||||
" address: %llu, should be %u\n",
|
||||
dinfo ? (unsigned long long)dinfo->address : -1ULL,
|
||||
(unsigned int)addr);
|
||||
goto err;
|
||||
}
|
||||
|
||||
DEBPRINT("for dev=0x%x.%x, addr=0x%llx, *handle=0x%p\n",
|
||||
devnum, func, (unsigned long long)addr, *handle);
|
||||
ret = 0;
|
||||
err:
|
||||
kfree(dinfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_acpi_hwif_get_handle - Get ACPI object handle for a given hwif
|
||||
* @hwif: device to locate
|
||||
*
|
||||
* Retrieves the object handle for a given hwif.
|
||||
*
|
||||
* Returns handle on success, 0 on error.
|
||||
*/
|
||||
static acpi_handle ide_acpi_hwif_get_handle(ide_hwif_t *hwif)
|
||||
{
|
||||
struct device *dev = hwif->gendev.parent;
|
||||
acpi_handle dev_handle;
|
||||
acpi_integer pcidevfn;
|
||||
acpi_handle chan_handle;
|
||||
int err;
|
||||
|
||||
DEBPRINT("ENTER: device %s\n", hwif->name);
|
||||
|
||||
if (!dev) {
|
||||
DEBPRINT("no PCI device for %s\n", hwif->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = ide_get_dev_handle(dev, &dev_handle, &pcidevfn);
|
||||
if (err < 0) {
|
||||
DEBPRINT("ide_get_dev_handle failed (%d)\n", err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* get child objects of dev_handle == channel objects,
|
||||
* + _their_ children == drive objects */
|
||||
/* channel is hwif->channel */
|
||||
chan_handle = acpi_get_child(dev_handle, hwif->channel);
|
||||
DEBPRINT("chan adr=%d: handle=0x%p\n",
|
||||
hwif->channel, chan_handle);
|
||||
|
||||
return chan_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_acpi_drive_get_handle - Get ACPI object handle for a given drive
|
||||
* @drive: device to locate
|
||||
*
|
||||
* Retrieves the object handle of a given drive. According to the ACPI
|
||||
* spec the drive is a child of the hwif.
|
||||
*
|
||||
* Returns handle on success, 0 on error.
|
||||
*/
|
||||
static acpi_handle ide_acpi_drive_get_handle(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
int port;
|
||||
acpi_handle drive_handle;
|
||||
|
||||
if (!hwif->acpidata)
|
||||
return NULL;
|
||||
|
||||
if (!hwif->acpidata->obj_handle)
|
||||
return NULL;
|
||||
|
||||
port = hwif->channel ? drive->dn - 2: drive->dn;
|
||||
|
||||
DEBPRINT("ENTER: %s at channel#: %d port#: %d\n",
|
||||
drive->name, hwif->channel, port);
|
||||
|
||||
|
||||
/* TBD: could also check ACPI object VALID bits */
|
||||
drive_handle = acpi_get_child(hwif->acpidata->obj_handle, port);
|
||||
DEBPRINT("drive %s handle 0x%p\n", drive->name, drive_handle);
|
||||
|
||||
return drive_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* do_drive_get_GTF - get the drive bootup default taskfile settings
|
||||
* @drive: the drive for which the taskfile settings should be retrieved
|
||||
* @gtf_length: number of bytes of _GTF data returned at @gtf_address
|
||||
* @gtf_address: buffer containing _GTF taskfile arrays
|
||||
*
|
||||
* The _GTF method has no input parameters.
|
||||
* It returns a variable number of register set values (registers
|
||||
* hex 1F1..1F7, taskfiles).
|
||||
* The <variable number> is not known in advance, so have ACPI-CA
|
||||
* allocate the buffer as needed and return it, then free it later.
|
||||
*
|
||||
* The returned @gtf_length and @gtf_address are only valid if the
|
||||
* function return value is 0.
|
||||
*/
|
||||
static int do_drive_get_GTF(ide_drive_t *drive,
|
||||
unsigned int *gtf_length, unsigned long *gtf_address,
|
||||
unsigned long *obj_loc)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer output;
|
||||
union acpi_object *out_obj;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct device *dev = hwif->gendev.parent;
|
||||
int err = -ENODEV;
|
||||
int port;
|
||||
|
||||
*gtf_length = 0;
|
||||
*gtf_address = 0UL;
|
||||
*obj_loc = 0UL;
|
||||
|
||||
if (ide_noacpi)
|
||||
return 0;
|
||||
|
||||
if (!dev) {
|
||||
DEBPRINT("no PCI device for %s\n", hwif->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!hwif->acpidata) {
|
||||
DEBPRINT("no ACPI data for %s\n", hwif->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
port = hwif->channel ? drive->dn - 2: drive->dn;
|
||||
|
||||
if (!drive->acpidata) {
|
||||
if (port == 0) {
|
||||
drive->acpidata = &hwif->acpidata->master;
|
||||
hwif->acpidata->master.drive = drive;
|
||||
} else {
|
||||
drive->acpidata = &hwif->acpidata->slave;
|
||||
hwif->acpidata->slave.drive = drive;
|
||||
}
|
||||
}
|
||||
|
||||
DEBPRINT("ENTER: %s at %s, port#: %d, hard_port#: %d\n",
|
||||
hwif->name, dev->bus_id, port, hwif->channel);
|
||||
|
||||
if (!drive->present) {
|
||||
DEBPRINT("%s drive %d:%d not present\n",
|
||||
hwif->name, hwif->channel, port);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get this drive's _ADR info. if not already known. */
|
||||
if (!drive->acpidata->obj_handle) {
|
||||
drive->acpidata->obj_handle = ide_acpi_drive_get_handle(drive);
|
||||
if (!drive->acpidata->obj_handle) {
|
||||
DEBPRINT("No ACPI object found for %s\n",
|
||||
drive->name);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setting up output buffer */
|
||||
output.length = ACPI_ALLOCATE_BUFFER;
|
||||
output.pointer = NULL; /* ACPI-CA sets this; save/free it later */
|
||||
|
||||
/* _GTF has no input parameters */
|
||||
err = -EIO;
|
||||
status = acpi_evaluate_object(drive->acpidata->obj_handle, "_GTF",
|
||||
NULL, &output);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_DEBUG
|
||||
"%s: Run _GTF error: status = 0x%x\n",
|
||||
__FUNCTION__, status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!output.length || !output.pointer) {
|
||||
DEBPRINT("Run _GTF: "
|
||||
"length or ptr is NULL (0x%llx, 0x%p)\n",
|
||||
(unsigned long long)output.length,
|
||||
output.pointer);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out_obj = output.pointer;
|
||||
if (out_obj->type != ACPI_TYPE_BUFFER) {
|
||||
DEBPRINT("Run _GTF: error: "
|
||||
"expected object type of ACPI_TYPE_BUFFER, "
|
||||
"got 0x%x\n", out_obj->type);
|
||||
err = -ENOENT;
|
||||
kfree(output.pointer);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!out_obj->buffer.length || !out_obj->buffer.pointer ||
|
||||
out_obj->buffer.length % REGS_PER_GTF) {
|
||||
printk(KERN_ERR
|
||||
"%s: unexpected GTF length (%d) or addr (0x%p)\n",
|
||||
__FUNCTION__, out_obj->buffer.length,
|
||||
out_obj->buffer.pointer);
|
||||
err = -ENOENT;
|
||||
kfree(output.pointer);
|
||||
goto out;
|
||||
}
|
||||
|
||||
*gtf_length = out_obj->buffer.length;
|
||||
*gtf_address = (unsigned long)out_obj->buffer.pointer;
|
||||
*obj_loc = (unsigned long)out_obj;
|
||||
DEBPRINT("returning gtf_length=%d, gtf_address=0x%lx, obj_loc=0x%lx\n",
|
||||
*gtf_length, *gtf_address, *obj_loc);
|
||||
err = 0;
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* taskfile_load_raw - send taskfile registers to drive
|
||||
* @drive: drive to which output is sent
|
||||
* @gtf: raw ATA taskfile register set (0x1f1 - 0x1f7)
|
||||
*
|
||||
* Outputs IDE taskfile to the drive.
|
||||
*/
|
||||
static int taskfile_load_raw(ide_drive_t *drive,
|
||||
const struct taskfile_array *gtf)
|
||||
{
|
||||
ide_task_t args;
|
||||
int err = 0;
|
||||
|
||||
DEBPRINT("(0x1f1-1f7): hex: "
|
||||
"%02x %02x %02x %02x %02x %02x %02x\n",
|
||||
gtf->tfa[0], gtf->tfa[1], gtf->tfa[2],
|
||||
gtf->tfa[3], gtf->tfa[4], gtf->tfa[5], gtf->tfa[6]);
|
||||
|
||||
memset(&args, 0, sizeof(ide_task_t));
|
||||
args.command_type = IDE_DRIVE_TASK_NO_DATA;
|
||||
args.data_phase = TASKFILE_IN;
|
||||
args.handler = &task_no_data_intr;
|
||||
|
||||
/* convert gtf to IDE Taskfile */
|
||||
args.tfRegister[1] = gtf->tfa[0]; /* 0x1f1 */
|
||||
args.tfRegister[2] = gtf->tfa[1]; /* 0x1f2 */
|
||||
args.tfRegister[3] = gtf->tfa[2]; /* 0x1f3 */
|
||||
args.tfRegister[4] = gtf->tfa[3]; /* 0x1f4 */
|
||||
args.tfRegister[5] = gtf->tfa[4]; /* 0x1f5 */
|
||||
args.tfRegister[6] = gtf->tfa[5]; /* 0x1f6 */
|
||||
args.tfRegister[7] = gtf->tfa[6]; /* 0x1f7 */
|
||||
|
||||
if (ide_noacpitfs) {
|
||||
DEBPRINT("_GTF execution disabled\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ide_raw_taskfile(drive, &args, NULL);
|
||||
if (err)
|
||||
printk(KERN_ERR "%s: ide_raw_taskfile failed: %u\n",
|
||||
__FUNCTION__, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* do_drive_set_taskfiles - write the drive taskfile settings from _GTF
|
||||
* @drive: the drive to which the taskfile command should be sent
|
||||
* @gtf_length: total number of bytes of _GTF taskfiles
|
||||
* @gtf_address: location of _GTF taskfile arrays
|
||||
*
|
||||
* Write {gtf_address, length gtf_length} in groups of
|
||||
* REGS_PER_GTF bytes.
|
||||
*/
|
||||
static int do_drive_set_taskfiles(ide_drive_t *drive,
|
||||
unsigned int gtf_length,
|
||||
unsigned long gtf_address)
|
||||
{
|
||||
int rc = -ENODEV, err;
|
||||
int gtf_count = gtf_length / REGS_PER_GTF;
|
||||
int ix;
|
||||
struct taskfile_array *gtf;
|
||||
|
||||
if (ide_noacpi)
|
||||
return 0;
|
||||
|
||||
DEBPRINT("ENTER: %s, hard_port#: %d\n", drive->name, drive->dn);
|
||||
|
||||
if (!drive->present)
|
||||
goto out;
|
||||
if (!gtf_count) /* shouldn't be here */
|
||||
goto out;
|
||||
|
||||
DEBPRINT("total GTF bytes=%u (0x%x), gtf_count=%d, addr=0x%lx\n",
|
||||
gtf_length, gtf_length, gtf_count, gtf_address);
|
||||
|
||||
if (gtf_length % REGS_PER_GTF) {
|
||||
printk(KERN_ERR "%s: unexpected GTF length (%d)\n",
|
||||
__FUNCTION__, gtf_length);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
for (ix = 0; ix < gtf_count; ix++) {
|
||||
gtf = (struct taskfile_array *)
|
||||
(gtf_address + ix * REGS_PER_GTF);
|
||||
|
||||
/* send all TaskFile registers (0x1f1-0x1f7) *in*that*order* */
|
||||
err = taskfile_load_raw(drive, gtf);
|
||||
if (err)
|
||||
rc = err;
|
||||
}
|
||||
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_acpi_exec_tfs - get then write drive taskfile settings
|
||||
* @drive: the drive for which the taskfile settings should be
|
||||
* written.
|
||||
*
|
||||
* According to the ACPI spec this should be called after _STM
|
||||
* has been evaluated for the interface. Some ACPI vendors interpret
|
||||
* that as a hard requirement and modify the taskfile according
|
||||
* to the Identify Drive information passed down with _STM.
|
||||
* So one should really make sure to call this only after _STM has
|
||||
* been executed.
|
||||
*/
|
||||
int ide_acpi_exec_tfs(ide_drive_t *drive)
|
||||
{
|
||||
int ret;
|
||||
unsigned int gtf_length;
|
||||
unsigned long gtf_address;
|
||||
unsigned long obj_loc;
|
||||
|
||||
if (ide_noacpi)
|
||||
return 0;
|
||||
|
||||
DEBPRINT("call get_GTF, drive=%s port=%d\n", drive->name, drive->dn);
|
||||
|
||||
ret = do_drive_get_GTF(drive, >f_length, >f_address, &obj_loc);
|
||||
if (ret < 0) {
|
||||
DEBPRINT("get_GTF error (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEBPRINT("call set_taskfiles, drive=%s\n", drive->name);
|
||||
|
||||
ret = do_drive_set_taskfiles(drive, gtf_length, gtf_address);
|
||||
kfree((void *)obj_loc);
|
||||
if (ret < 0) {
|
||||
DEBPRINT("set_taskfiles error (%d)\n", ret);
|
||||
}
|
||||
|
||||
DEBPRINT("ret=%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_acpi_exec_tfs);
|
||||
|
||||
/**
|
||||
* ide_acpi_get_timing - get the channel (controller) timings
|
||||
* @hwif: target IDE interface (channel)
|
||||
*
|
||||
* This function executes the _GTM ACPI method for the target channel.
|
||||
*
|
||||
*/
|
||||
void ide_acpi_get_timing(ide_hwif_t *hwif)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer output;
|
||||
union acpi_object *out_obj;
|
||||
|
||||
if (ide_noacpi)
|
||||
return;
|
||||
|
||||
DEBPRINT("ENTER:\n");
|
||||
|
||||
if (!hwif->acpidata) {
|
||||
DEBPRINT("no ACPI data for %s\n", hwif->name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Setting up output buffer for _GTM */
|
||||
output.length = ACPI_ALLOCATE_BUFFER;
|
||||
output.pointer = NULL; /* ACPI-CA sets this; save/free it later */
|
||||
|
||||
/* _GTM has no input parameters */
|
||||
status = acpi_evaluate_object(hwif->acpidata->obj_handle, "_GTM",
|
||||
NULL, &output);
|
||||
|
||||
DEBPRINT("_GTM status: %d, outptr: 0x%p, outlen: 0x%llx\n",
|
||||
status, output.pointer,
|
||||
(unsigned long long)output.length);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
DEBPRINT("Run _GTM error: status = 0x%x\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!output.length || !output.pointer) {
|
||||
DEBPRINT("Run _GTM: length or ptr is NULL (0x%llx, 0x%p)\n",
|
||||
(unsigned long long)output.length,
|
||||
output.pointer);
|
||||
kfree(output.pointer);
|
||||
return;
|
||||
}
|
||||
|
||||
out_obj = output.pointer;
|
||||
if (out_obj->type != ACPI_TYPE_BUFFER) {
|
||||
kfree(output.pointer);
|
||||
DEBPRINT("Run _GTM: error: "
|
||||
"expected object type of ACPI_TYPE_BUFFER, "
|
||||
"got 0x%x\n", out_obj->type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!out_obj->buffer.length || !out_obj->buffer.pointer ||
|
||||
out_obj->buffer.length != sizeof(struct GTM_buffer)) {
|
||||
kfree(output.pointer);
|
||||
printk(KERN_ERR
|
||||
"%s: unexpected _GTM length (0x%x)[should be 0x%zx] or "
|
||||
"addr (0x%p)\n",
|
||||
__FUNCTION__, out_obj->buffer.length,
|
||||
sizeof(struct GTM_buffer), out_obj->buffer.pointer);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&hwif->acpidata->gtm, out_obj->buffer.pointer,
|
||||
sizeof(struct GTM_buffer));
|
||||
|
||||
DEBPRINT("_GTM info: ptr: 0x%p, len: 0x%x, exp.len: 0x%Zx\n",
|
||||
out_obj->buffer.pointer, out_obj->buffer.length,
|
||||
sizeof(struct GTM_buffer));
|
||||
|
||||
DEBPRINT("_GTM fields: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
|
||||
hwif->acpidata->gtm.PIO_speed0,
|
||||
hwif->acpidata->gtm.DMA_speed0,
|
||||
hwif->acpidata->gtm.PIO_speed1,
|
||||
hwif->acpidata->gtm.DMA_speed1,
|
||||
hwif->acpidata->gtm.GTM_flags);
|
||||
|
||||
kfree(output.pointer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_acpi_get_timing);
|
||||
|
||||
/**
|
||||
* ide_acpi_push_timing - set the channel (controller) timings
|
||||
* @hwif: target IDE interface (channel)
|
||||
*
|
||||
* This function executes the _STM ACPI method for the target channel.
|
||||
*
|
||||
* _STM requires Identify Drive data, which has to passed as an argument.
|
||||
* Unfortunately hd_driveid is a mangled version which we can't readily
|
||||
* use; hence we'll get the information afresh.
|
||||
*/
|
||||
void ide_acpi_push_timing(ide_hwif_t *hwif)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
union acpi_object in_params[3];
|
||||
struct ide_acpi_drive_link *master = &hwif->acpidata->master;
|
||||
struct ide_acpi_drive_link *slave = &hwif->acpidata->slave;
|
||||
|
||||
if (ide_noacpi)
|
||||
return;
|
||||
|
||||
DEBPRINT("ENTER:\n");
|
||||
|
||||
if (!hwif->acpidata) {
|
||||
DEBPRINT("no ACPI data for %s\n", hwif->name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Give the GTM buffer + drive Identify data to the channel via the
|
||||
* _STM method: */
|
||||
/* setup input parameters buffer for _STM */
|
||||
input.count = 3;
|
||||
input.pointer = in_params;
|
||||
in_params[0].type = ACPI_TYPE_BUFFER;
|
||||
in_params[0].buffer.length = sizeof(struct GTM_buffer);
|
||||
in_params[0].buffer.pointer = (u8 *)&hwif->acpidata->gtm;
|
||||
in_params[1].type = ACPI_TYPE_BUFFER;
|
||||
in_params[1].buffer.length = sizeof(struct hd_driveid);
|
||||
in_params[1].buffer.pointer = (u8 *)&master->idbuff;
|
||||
in_params[2].type = ACPI_TYPE_BUFFER;
|
||||
in_params[2].buffer.length = sizeof(struct hd_driveid);
|
||||
in_params[2].buffer.pointer = (u8 *)&slave->idbuff;
|
||||
/* Output buffer: _STM has no output */
|
||||
|
||||
status = acpi_evaluate_object(hwif->acpidata->obj_handle, "_STM",
|
||||
&input, NULL);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
DEBPRINT("Run _STM error: status = 0x%x\n", status);
|
||||
}
|
||||
DEBPRINT("_STM status: %d\n", status);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_acpi_push_timing);
|
||||
|
||||
/**
|
||||
* ide_acpi_init - initialize the ACPI link for an IDE interface
|
||||
* @hwif: target IDE interface (channel)
|
||||
*
|
||||
* The ACPI spec is not quite clear when the drive identify buffer
|
||||
* should be obtained. Calling IDENTIFY DEVICE during shutdown
|
||||
* is not the best of ideas as the drive might already being put to
|
||||
* sleep. And obviously we can't call it during resume.
|
||||
* So we get the information during startup; but this means that
|
||||
* any changes during run-time will be lost after resume.
|
||||
*/
|
||||
void ide_acpi_init(ide_hwif_t *hwif)
|
||||
{
|
||||
int unit;
|
||||
int err;
|
||||
struct ide_acpi_drive_link *master;
|
||||
struct ide_acpi_drive_link *slave;
|
||||
|
||||
hwif->acpidata = kzalloc(sizeof(struct ide_acpi_hwif_link), GFP_KERNEL);
|
||||
if (!hwif->acpidata)
|
||||
return;
|
||||
|
||||
hwif->acpidata->obj_handle = ide_acpi_hwif_get_handle(hwif);
|
||||
if (!hwif->acpidata->obj_handle) {
|
||||
DEBPRINT("no ACPI object for %s found\n", hwif->name);
|
||||
kfree(hwif->acpidata);
|
||||
hwif->acpidata = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The ACPI spec mandates that we send information
|
||||
* for both drives, regardless whether they are connected
|
||||
* or not.
|
||||
*/
|
||||
hwif->acpidata->master.drive = &hwif->drives[0];
|
||||
hwif->drives[0].acpidata = &hwif->acpidata->master;
|
||||
master = &hwif->acpidata->master;
|
||||
|
||||
hwif->acpidata->slave.drive = &hwif->drives[1];
|
||||
hwif->drives[1].acpidata = &hwif->acpidata->slave;
|
||||
slave = &hwif->acpidata->slave;
|
||||
|
||||
|
||||
/*
|
||||
* Send IDENTIFY for each drive
|
||||
*/
|
||||
if (master->drive->present) {
|
||||
err = taskfile_lib_get_identify(master->drive, master->idbuff);
|
||||
if (err) {
|
||||
DEBPRINT("identify device %s failed (%d)\n",
|
||||
master->drive->name, err);
|
||||
}
|
||||
}
|
||||
|
||||
if (slave->drive->present) {
|
||||
err = taskfile_lib_get_identify(slave->drive, slave->idbuff);
|
||||
if (err) {
|
||||
DEBPRINT("identify device %s failed (%d)\n",
|
||||
slave->drive->name, err);
|
||||
}
|
||||
}
|
||||
|
||||
if (ide_noacpionboot) {
|
||||
DEBPRINT("ACPI methods disabled on boot\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* ACPI requires us to call _STM on startup
|
||||
*/
|
||||
ide_acpi_get_timing(hwif);
|
||||
ide_acpi_push_timing(hwif);
|
||||
|
||||
for (unit = 0; unit < MAX_DRIVES; ++unit) {
|
||||
ide_drive_t *drive = &hwif->drives[unit];
|
||||
|
||||
if (drive->present) {
|
||||
/* Execute ACPI startup code */
|
||||
ide_acpi_exec_tfs(drive);
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_acpi_init);
|
||||
3573
drivers/ide/ide-cd.c
Normal file
3573
drivers/ide/ide-cd.c
Normal file
File diff suppressed because it is too large
Load Diff
745
drivers/ide/ide-cd.h
Normal file
745
drivers/ide/ide-cd.h
Normal file
@@ -0,0 +1,745 @@
|
||||
/*
|
||||
* linux/drivers/ide/ide_cd.h
|
||||
*
|
||||
* Copyright (C) 1996-98 Erik Andersen
|
||||
* Copyright (C) 1998-2000 Jens Axboe
|
||||
*/
|
||||
#ifndef _IDE_CD_H
|
||||
#define _IDE_CD_H
|
||||
|
||||
#include <linux/cdrom.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
/* Turn this on to have the driver print out the meanings of the
|
||||
ATAPI error codes. This will use up additional kernel-space
|
||||
memory, though. */
|
||||
|
||||
#ifndef VERBOSE_IDE_CD_ERRORS
|
||||
#define VERBOSE_IDE_CD_ERRORS 1
|
||||
#endif
|
||||
|
||||
|
||||
/* Turning this on will remove code to work around various nonstandard
|
||||
ATAPI implementations. If you know your drive follows the standard,
|
||||
this will give you a slightly smaller kernel. */
|
||||
|
||||
#ifndef STANDARD_ATAPI
|
||||
#define STANDARD_ATAPI 0
|
||||
#endif
|
||||
|
||||
|
||||
/* Turning this on will disable the door-locking functionality.
|
||||
This is apparently needed for supermount. */
|
||||
|
||||
#ifndef NO_DOOR_LOCKING
|
||||
#define NO_DOOR_LOCKING 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* typical timeout for packet command
|
||||
*/
|
||||
#define ATAPI_WAIT_PC (60 * HZ)
|
||||
#define ATAPI_WAIT_WRITE_BUSY (10 * HZ)
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
#define SECTOR_BITS 9
|
||||
#ifndef SECTOR_SIZE
|
||||
#define SECTOR_SIZE (1 << SECTOR_BITS)
|
||||
#endif
|
||||
#define SECTORS_PER_FRAME (CD_FRAMESIZE >> SECTOR_BITS)
|
||||
#define SECTOR_BUFFER_SIZE (CD_FRAMESIZE * 32)
|
||||
#define SECTORS_BUFFER (SECTOR_BUFFER_SIZE >> SECTOR_BITS)
|
||||
#define SECTORS_MAX (131072 >> SECTOR_BITS)
|
||||
|
||||
#define BLOCKS_PER_FRAME (CD_FRAMESIZE / BLOCK_SIZE)
|
||||
|
||||
/* special command codes for strategy routine. */
|
||||
#define PACKET_COMMAND 4315
|
||||
#define REQUEST_SENSE_COMMAND 4316
|
||||
#define RESET_DRIVE_COMMAND 4317
|
||||
|
||||
|
||||
/* Configuration flags. These describe the capabilities of the drive.
|
||||
They generally do not change after initialization, unless we learn
|
||||
more about the drive from stuff failing. */
|
||||
struct ide_cd_config_flags {
|
||||
__u8 drq_interrupt : 1; /* Device sends an interrupt when ready
|
||||
for a packet command. */
|
||||
__u8 no_doorlock : 1; /* Drive cannot lock the door. */
|
||||
__u8 no_eject : 1; /* Drive cannot eject the disc. */
|
||||
__u8 nec260 : 1; /* Drive is a pre-1.2 NEC 260 drive. */
|
||||
__u8 playmsf_as_bcd : 1; /* PLAYMSF command takes BCD args. */
|
||||
__u8 tocaddr_as_bcd : 1; /* TOC addresses are in BCD. */
|
||||
__u8 toctracks_as_bcd : 1; /* TOC track numbers are in BCD. */
|
||||
__u8 subchan_as_bcd : 1; /* Subchannel info is in BCD. */
|
||||
__u8 is_changer : 1; /* Drive is a changer. */
|
||||
__u8 cd_r : 1; /* Drive can write to CD-R media . */
|
||||
__u8 cd_rw : 1; /* Drive can write to CD-R/W media . */
|
||||
__u8 dvd : 1; /* Drive is a DVD-ROM */
|
||||
__u8 dvd_r : 1; /* Drive can write DVD-R */
|
||||
__u8 dvd_ram : 1; /* Drive can write DVD-RAM */
|
||||
__u8 ram : 1; /* generic WRITE (dvd-ram/mrw) */
|
||||
__u8 test_write : 1; /* Drive can fake writes */
|
||||
__u8 supp_disc_present : 1; /* Changer can report exact contents
|
||||
of slots. */
|
||||
__u8 limit_nframes : 1; /* Drive does not provide data in
|
||||
multiples of SECTOR_SIZE when more
|
||||
than one interrupt is needed. */
|
||||
__u8 seeking : 1; /* Seeking in progress */
|
||||
__u8 audio_play : 1; /* can do audio related commands */
|
||||
__u8 close_tray : 1; /* can close the tray */
|
||||
__u8 writing : 1; /* pseudo write in progress */
|
||||
__u8 mo_drive : 1; /* drive is an MO device */
|
||||
__u8 reserved : 2;
|
||||
byte max_speed; /* Max speed of the drive */
|
||||
};
|
||||
#define CDROM_CONFIG_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->config_flags))
|
||||
|
||||
|
||||
/* State flags. These give information about the current state of the
|
||||
drive, and will change during normal operation. */
|
||||
struct ide_cd_state_flags {
|
||||
__u8 media_changed : 1; /* Driver has noticed a media change. */
|
||||
__u8 toc_valid : 1; /* Saved TOC information is current. */
|
||||
__u8 door_locked : 1; /* We think that the drive door is locked. */
|
||||
__u8 writing : 1; /* the drive is currently writing */
|
||||
__u8 reserved : 4;
|
||||
byte current_speed; /* Current speed of the drive */
|
||||
};
|
||||
|
||||
#define CDROM_STATE_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->state_flags))
|
||||
|
||||
/* Structure of a MSF cdrom address. */
|
||||
struct atapi_msf {
|
||||
byte reserved;
|
||||
byte minute;
|
||||
byte second;
|
||||
byte frame;
|
||||
};
|
||||
|
||||
/* Space to hold the disk TOC. */
|
||||
#define MAX_TRACKS 99
|
||||
struct atapi_toc_header {
|
||||
unsigned short toc_length;
|
||||
byte first_track;
|
||||
byte last_track;
|
||||
};
|
||||
|
||||
struct atapi_toc_entry {
|
||||
byte reserved1;
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
__u8 adr : 4;
|
||||
__u8 control : 4;
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
__u8 control : 4;
|
||||
__u8 adr : 4;
|
||||
#else
|
||||
#error "Please fix <asm/byteorder.h>"
|
||||
#endif
|
||||
byte track;
|
||||
byte reserved2;
|
||||
union {
|
||||
unsigned lba;
|
||||
struct atapi_msf msf;
|
||||
} addr;
|
||||
};
|
||||
|
||||
struct atapi_toc {
|
||||
int last_session_lba;
|
||||
int xa_flag;
|
||||
unsigned long capacity;
|
||||
struct atapi_toc_header hdr;
|
||||
struct atapi_toc_entry ent[MAX_TRACKS+1];
|
||||
/* One extra for the leadout. */
|
||||
};
|
||||
|
||||
|
||||
/* This structure is annoyingly close to, but not identical with,
|
||||
the cdrom_subchnl structure from cdrom.h. */
|
||||
struct atapi_cdrom_subchnl {
|
||||
u_char acdsc_reserved;
|
||||
u_char acdsc_audiostatus;
|
||||
u_short acdsc_length;
|
||||
u_char acdsc_format;
|
||||
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
u_char acdsc_ctrl: 4;
|
||||
u_char acdsc_adr: 4;
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
u_char acdsc_adr: 4;
|
||||
u_char acdsc_ctrl: 4;
|
||||
#else
|
||||
#error "Please fix <asm/byteorder.h>"
|
||||
#endif
|
||||
u_char acdsc_trk;
|
||||
u_char acdsc_ind;
|
||||
union {
|
||||
struct atapi_msf msf;
|
||||
int lba;
|
||||
} acdsc_absaddr;
|
||||
union {
|
||||
struct atapi_msf msf;
|
||||
int lba;
|
||||
} acdsc_reladdr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* This should probably go into cdrom.h along with the other
|
||||
* generic stuff now in the Mt. Fuji spec.
|
||||
*/
|
||||
struct atapi_capabilities_page {
|
||||
struct mode_page_header header;
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
__u8 parameters_saveable : 1;
|
||||
__u8 reserved1 : 1;
|
||||
__u8 page_code : 6;
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
__u8 page_code : 6;
|
||||
__u8 reserved1 : 1;
|
||||
__u8 parameters_saveable : 1;
|
||||
#else
|
||||
#error "Please fix <asm/byteorder.h>"
|
||||
#endif
|
||||
|
||||
byte page_length;
|
||||
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
__u8 reserved2 : 2;
|
||||
/* Drive supports reading of DVD-RAM discs */
|
||||
__u8 dvd_ram_read : 1;
|
||||
/* Drive supports reading of DVD-R discs */
|
||||
__u8 dvd_r_read : 1;
|
||||
/* Drive supports reading of DVD-ROM discs */
|
||||
__u8 dvd_rom : 1;
|
||||
/* Drive supports reading CD-R discs with addressing method 2 */
|
||||
__u8 method2 : 1; /* reserved in 1.2 */
|
||||
/* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */
|
||||
__u8 cd_rw_read : 1; /* reserved in 1.2 */
|
||||
/* Drive supports read from CD-R discs (orange book, part II) */
|
||||
__u8 cd_r_read : 1; /* reserved in 1.2 */
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
/* Drive supports read from CD-R discs (orange book, part II) */
|
||||
__u8 cd_r_read : 1; /* reserved in 1.2 */
|
||||
/* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */
|
||||
__u8 cd_rw_read : 1; /* reserved in 1.2 */
|
||||
/* Drive supports reading CD-R discs with addressing method 2 */
|
||||
__u8 method2 : 1;
|
||||
/* Drive supports reading of DVD-ROM discs */
|
||||
__u8 dvd_rom : 1;
|
||||
/* Drive supports reading of DVD-R discs */
|
||||
__u8 dvd_r_read : 1;
|
||||
/* Drive supports reading of DVD-RAM discs */
|
||||
__u8 dvd_ram_read : 1;
|
||||
__u8 reserved2 : 2;
|
||||
#else
|
||||
#error "Please fix <asm/byteorder.h>"
|
||||
#endif
|
||||
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
__u8 reserved3 : 2;
|
||||
/* Drive can write DVD-RAM discs */
|
||||
__u8 dvd_ram_write : 1;
|
||||
/* Drive can write DVD-R discs */
|
||||
__u8 dvd_r_write : 1;
|
||||
__u8 reserved3a : 1;
|
||||
/* Drive can fake writes */
|
||||
__u8 test_write : 1;
|
||||
/* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
|
||||
__u8 cd_rw_write : 1; /* reserved in 1.2 */
|
||||
/* Drive supports write to CD-R discs (orange book, part II) */
|
||||
__u8 cd_r_write : 1; /* reserved in 1.2 */
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
/* Drive can write to CD-R discs (orange book, part II) */
|
||||
__u8 cd_r_write : 1; /* reserved in 1.2 */
|
||||
/* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
|
||||
__u8 cd_rw_write : 1; /* reserved in 1.2 */
|
||||
/* Drive can fake writes */
|
||||
__u8 test_write : 1;
|
||||
__u8 reserved3a : 1;
|
||||
/* Drive can write DVD-R discs */
|
||||
__u8 dvd_r_write : 1;
|
||||
/* Drive can write DVD-RAM discs */
|
||||
__u8 dvd_ram_write : 1;
|
||||
__u8 reserved3 : 2;
|
||||
#else
|
||||
#error "Please fix <asm/byteorder.h>"
|
||||
#endif
|
||||
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
__u8 reserved4 : 1;
|
||||
/* Drive can read multisession discs. */
|
||||
__u8 multisession : 1;
|
||||
/* Drive can read mode 2, form 2 data. */
|
||||
__u8 mode2_form2 : 1;
|
||||
/* Drive can read mode 2, form 1 (XA) data. */
|
||||
__u8 mode2_form1 : 1;
|
||||
/* Drive supports digital output on port 2. */
|
||||
__u8 digport2 : 1;
|
||||
/* Drive supports digital output on port 1. */
|
||||
__u8 digport1 : 1;
|
||||
/* Drive can deliver a composite audio/video data stream. */
|
||||
__u8 composite : 1;
|
||||
/* Drive supports audio play operations. */
|
||||
__u8 audio_play : 1;
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
/* Drive supports audio play operations. */
|
||||
__u8 audio_play : 1;
|
||||
/* Drive can deliver a composite audio/video data stream. */
|
||||
__u8 composite : 1;
|
||||
/* Drive supports digital output on port 1. */
|
||||
__u8 digport1 : 1;
|
||||
/* Drive supports digital output on port 2. */
|
||||
__u8 digport2 : 1;
|
||||
/* Drive can read mode 2, form 1 (XA) data. */
|
||||
__u8 mode2_form1 : 1;
|
||||
/* Drive can read mode 2, form 2 data. */
|
||||
__u8 mode2_form2 : 1;
|
||||
/* Drive can read multisession discs. */
|
||||
__u8 multisession : 1;
|
||||
__u8 reserved4 : 1;
|
||||
#else
|
||||
#error "Please fix <asm/byteorder.h>"
|
||||
#endif
|
||||
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
__u8 reserved5 : 1;
|
||||
/* Drive can return Media Catalog Number (UPC) info. */
|
||||
__u8 upc : 1;
|
||||
/* Drive can return International Standard Recording Code info. */
|
||||
__u8 isrc : 1;
|
||||
/* Drive supports C2 error pointers. */
|
||||
__u8 c2_pointers : 1;
|
||||
/* R-W data will be returned deinterleaved and error corrected. */
|
||||
__u8 rw_corr : 1;
|
||||
/* Subchannel reads can return combined R-W information. */
|
||||
__u8 rw_supported : 1;
|
||||
/* Drive can continue a read cdda operation from a loss of streaming.*/
|
||||
__u8 cdda_accurate : 1;
|
||||
/* Drive can read Red Book audio data. */
|
||||
__u8 cdda : 1;
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
/* Drive can read Red Book audio data. */
|
||||
__u8 cdda : 1;
|
||||
/* Drive can continue a read cdda operation from a loss of streaming.*/
|
||||
__u8 cdda_accurate : 1;
|
||||
/* Subchannel reads can return combined R-W information. */
|
||||
__u8 rw_supported : 1;
|
||||
/* R-W data will be returned deinterleaved and error corrected. */
|
||||
__u8 rw_corr : 1;
|
||||
/* Drive supports C2 error pointers. */
|
||||
__u8 c2_pointers : 1;
|
||||
/* Drive can return International Standard Recording Code info. */
|
||||
__u8 isrc : 1;
|
||||
/* Drive can return Media Catalog Number (UPC) info. */
|
||||
__u8 upc : 1;
|
||||
__u8 reserved5 : 1;
|
||||
#else
|
||||
#error "Please fix <asm/byteorder.h>"
|
||||
#endif
|
||||
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
/* Drive mechanism types. */
|
||||
mechtype_t mechtype : 3;
|
||||
__u8 reserved6 : 1;
|
||||
/* Drive can eject a disc or changer cartridge. */
|
||||
__u8 eject : 1;
|
||||
/* State of prevent/allow jumper. */
|
||||
__u8 prevent_jumper : 1;
|
||||
/* Present state of door lock. */
|
||||
__u8 lock_state : 1;
|
||||
/* Drive can lock the door. */
|
||||
__u8 lock : 1;
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
|
||||
/* Drive can lock the door. */
|
||||
__u8 lock : 1;
|
||||
/* Present state of door lock. */
|
||||
__u8 lock_state : 1;
|
||||
/* State of prevent/allow jumper. */
|
||||
__u8 prevent_jumper : 1;
|
||||
/* Drive can eject a disc or changer cartridge. */
|
||||
__u8 eject : 1;
|
||||
__u8 reserved6 : 1;
|
||||
/* Drive mechanism types. */
|
||||
mechtype_t mechtype : 3;
|
||||
#else
|
||||
#error "Please fix <asm/byteorder.h>"
|
||||
#endif
|
||||
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
__u8 reserved7 : 4;
|
||||
/* Drive supports software slot selection. */
|
||||
__u8 sss : 1; /* reserved in 1.2 */
|
||||
/* Changer can report exact contents of slots. */
|
||||
__u8 disc_present : 1; /* reserved in 1.2 */
|
||||
/* Audio for each channel can be muted independently. */
|
||||
__u8 separate_mute : 1;
|
||||
/* Audio level for each channel can be controlled independently. */
|
||||
__u8 separate_volume : 1;
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
|
||||
/* Audio level for each channel can be controlled independently. */
|
||||
__u8 separate_volume : 1;
|
||||
/* Audio for each channel can be muted independently. */
|
||||
__u8 separate_mute : 1;
|
||||
/* Changer can report exact contents of slots. */
|
||||
__u8 disc_present : 1; /* reserved in 1.2 */
|
||||
/* Drive supports software slot selection. */
|
||||
__u8 sss : 1; /* reserved in 1.2 */
|
||||
__u8 reserved7 : 4;
|
||||
#else
|
||||
#error "Please fix <asm/byteorder.h>"
|
||||
#endif
|
||||
|
||||
/* Note: the following four fields are returned in big-endian form. */
|
||||
/* Maximum speed (in kB/s). */
|
||||
unsigned short maxspeed;
|
||||
/* Number of discrete volume levels. */
|
||||
unsigned short n_vol_levels;
|
||||
/* Size of cache in drive, in kB. */
|
||||
unsigned short buffer_size;
|
||||
/* Current speed (in kB/s). */
|
||||
unsigned short curspeed;
|
||||
char pad[4];
|
||||
};
|
||||
|
||||
|
||||
struct atapi_mechstat_header {
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
__u8 fault : 1;
|
||||
__u8 changer_state : 2;
|
||||
__u8 curslot : 5;
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
__u8 curslot : 5;
|
||||
__u8 changer_state : 2;
|
||||
__u8 fault : 1;
|
||||
#else
|
||||
#error "Please fix <asm/byteorder.h>"
|
||||
#endif
|
||||
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
__u8 mech_state : 3;
|
||||
__u8 door_open : 1;
|
||||
__u8 reserved1 : 4;
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
__u8 reserved1 : 4;
|
||||
__u8 door_open : 1;
|
||||
__u8 mech_state : 3;
|
||||
#else
|
||||
#error "Please fix <asm/byteorder.h>"
|
||||
#endif
|
||||
|
||||
byte curlba[3];
|
||||
byte nslots;
|
||||
__u16 slot_tablelen;
|
||||
};
|
||||
|
||||
|
||||
struct atapi_slot {
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
__u8 disc_present : 1;
|
||||
__u8 reserved1 : 6;
|
||||
__u8 change : 1;
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
__u8 change : 1;
|
||||
__u8 reserved1 : 6;
|
||||
__u8 disc_present : 1;
|
||||
#else
|
||||
#error "Please fix <asm/byteorder.h>"
|
||||
#endif
|
||||
|
||||
byte reserved2[3];
|
||||
};
|
||||
|
||||
struct atapi_changer_info {
|
||||
struct atapi_mechstat_header hdr;
|
||||
struct atapi_slot slots[0];
|
||||
};
|
||||
|
||||
/* Extra per-device info for cdrom drives. */
|
||||
struct cdrom_info {
|
||||
ide_drive_t *drive;
|
||||
ide_driver_t *driver;
|
||||
struct gendisk *disk;
|
||||
struct kref kref;
|
||||
|
||||
/* Buffer for table of contents. NULL if we haven't allocated
|
||||
a TOC buffer for this device yet. */
|
||||
|
||||
struct atapi_toc *toc;
|
||||
|
||||
unsigned long sector_buffered;
|
||||
unsigned long nsectors_buffered;
|
||||
unsigned char *buffer;
|
||||
|
||||
/* The result of the last successful request sense command
|
||||
on this device. */
|
||||
struct request_sense sense_data;
|
||||
|
||||
struct request request_sense_request;
|
||||
int dma;
|
||||
unsigned long last_block;
|
||||
unsigned long start_seek;
|
||||
/* Buffer to hold mechanism status and changer slot table. */
|
||||
struct atapi_changer_info *changer_info;
|
||||
|
||||
struct ide_cd_config_flags config_flags;
|
||||
struct ide_cd_state_flags state_flags;
|
||||
|
||||
/* Per-device info needed by cdrom.c generic driver. */
|
||||
struct cdrom_device_info devinfo;
|
||||
|
||||
unsigned long write_timeout;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Descriptions of ATAPI error codes.
|
||||
*/
|
||||
|
||||
#define ARY_LEN(a) ((sizeof(a) / sizeof(a[0])))
|
||||
|
||||
/* This stuff should be in cdrom.h, since it is now generic... */
|
||||
|
||||
/* ATAPI sense keys (from table 140 of ATAPI 2.6) */
|
||||
#define NO_SENSE 0x00
|
||||
#define RECOVERED_ERROR 0x01
|
||||
#define NOT_READY 0x02
|
||||
#define MEDIUM_ERROR 0x03
|
||||
#define HARDWARE_ERROR 0x04
|
||||
#define ILLEGAL_REQUEST 0x05
|
||||
#define UNIT_ATTENTION 0x06
|
||||
#define DATA_PROTECT 0x07
|
||||
#define BLANK_CHECK 0x08
|
||||
#define ABORTED_COMMAND 0x0b
|
||||
#define MISCOMPARE 0x0e
|
||||
|
||||
|
||||
|
||||
/* This stuff should be in cdrom.h, since it is now generic... */
|
||||
#if VERBOSE_IDE_CD_ERRORS
|
||||
|
||||
/* The generic packet command opcodes for CD/DVD Logical Units,
|
||||
* From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
|
||||
static const struct {
|
||||
unsigned short packet_command;
|
||||
const char * const text;
|
||||
} packet_command_texts[] = {
|
||||
{ GPCMD_TEST_UNIT_READY, "Test Unit Ready" },
|
||||
{ GPCMD_REQUEST_SENSE, "Request Sense" },
|
||||
{ GPCMD_FORMAT_UNIT, "Format Unit" },
|
||||
{ GPCMD_INQUIRY, "Inquiry" },
|
||||
{ GPCMD_START_STOP_UNIT, "Start/Stop Unit" },
|
||||
{ GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, "Prevent/Allow Medium Removal" },
|
||||
{ GPCMD_READ_FORMAT_CAPACITIES, "Read Format Capacities" },
|
||||
{ GPCMD_READ_CDVD_CAPACITY, "Read Cd/Dvd Capacity" },
|
||||
{ GPCMD_READ_10, "Read 10" },
|
||||
{ GPCMD_WRITE_10, "Write 10" },
|
||||
{ GPCMD_SEEK, "Seek" },
|
||||
{ GPCMD_WRITE_AND_VERIFY_10, "Write and Verify 10" },
|
||||
{ GPCMD_VERIFY_10, "Verify 10" },
|
||||
{ GPCMD_FLUSH_CACHE, "Flush Cache" },
|
||||
{ GPCMD_READ_SUBCHANNEL, "Read Subchannel" },
|
||||
{ GPCMD_READ_TOC_PMA_ATIP, "Read Table of Contents" },
|
||||
{ GPCMD_READ_HEADER, "Read Header" },
|
||||
{ GPCMD_PLAY_AUDIO_10, "Play Audio 10" },
|
||||
{ GPCMD_GET_CONFIGURATION, "Get Configuration" },
|
||||
{ GPCMD_PLAY_AUDIO_MSF, "Play Audio MSF" },
|
||||
{ GPCMD_PLAYAUDIO_TI, "Play Audio TrackIndex" },
|
||||
{ GPCMD_GET_EVENT_STATUS_NOTIFICATION, "Get Event Status Notification" },
|
||||
{ GPCMD_PAUSE_RESUME, "Pause/Resume" },
|
||||
{ GPCMD_STOP_PLAY_SCAN, "Stop Play/Scan" },
|
||||
{ GPCMD_READ_DISC_INFO, "Read Disc Info" },
|
||||
{ GPCMD_READ_TRACK_RZONE_INFO, "Read Track Rzone Info" },
|
||||
{ GPCMD_RESERVE_RZONE_TRACK, "Reserve Rzone Track" },
|
||||
{ GPCMD_SEND_OPC, "Send OPC" },
|
||||
{ GPCMD_MODE_SELECT_10, "Mode Select 10" },
|
||||
{ GPCMD_REPAIR_RZONE_TRACK, "Repair Rzone Track" },
|
||||
{ GPCMD_MODE_SENSE_10, "Mode Sense 10" },
|
||||
{ GPCMD_CLOSE_TRACK, "Close Track" },
|
||||
{ GPCMD_BLANK, "Blank" },
|
||||
{ GPCMD_SEND_EVENT, "Send Event" },
|
||||
{ GPCMD_SEND_KEY, "Send Key" },
|
||||
{ GPCMD_REPORT_KEY, "Report Key" },
|
||||
{ GPCMD_LOAD_UNLOAD, "Load/Unload" },
|
||||
{ GPCMD_SET_READ_AHEAD, "Set Read-ahead" },
|
||||
{ GPCMD_READ_12, "Read 12" },
|
||||
{ GPCMD_GET_PERFORMANCE, "Get Performance" },
|
||||
{ GPCMD_SEND_DVD_STRUCTURE, "Send DVD Structure" },
|
||||
{ GPCMD_READ_DVD_STRUCTURE, "Read DVD Structure" },
|
||||
{ GPCMD_SET_STREAMING, "Set Streaming" },
|
||||
{ GPCMD_READ_CD_MSF, "Read CD MSF" },
|
||||
{ GPCMD_SCAN, "Scan" },
|
||||
{ GPCMD_SET_SPEED, "Set Speed" },
|
||||
{ GPCMD_PLAY_CD, "Play CD" },
|
||||
{ GPCMD_MECHANISM_STATUS, "Mechanism Status" },
|
||||
{ GPCMD_READ_CD, "Read CD" },
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* From Table 303 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
|
||||
static const char * const sense_key_texts[16] = {
|
||||
"No sense data",
|
||||
"Recovered error",
|
||||
"Not ready",
|
||||
"Medium error",
|
||||
"Hardware error",
|
||||
"Illegal request",
|
||||
"Unit attention",
|
||||
"Data protect",
|
||||
"Blank check",
|
||||
"(reserved)",
|
||||
"(reserved)",
|
||||
"Aborted command",
|
||||
"(reserved)",
|
||||
"(reserved)",
|
||||
"Miscompare",
|
||||
"(reserved)",
|
||||
};
|
||||
|
||||
/* From Table 304 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
|
||||
static const struct {
|
||||
unsigned long asc_ascq;
|
||||
const char * const text;
|
||||
} sense_data_texts[] = {
|
||||
{ 0x000000, "No additional sense information" },
|
||||
{ 0x000011, "Play operation in progress" },
|
||||
{ 0x000012, "Play operation paused" },
|
||||
{ 0x000013, "Play operation successfully completed" },
|
||||
{ 0x000014, "Play operation stopped due to error" },
|
||||
{ 0x000015, "No current audio status to return" },
|
||||
{ 0x010c0a, "Write error - padding blocks added" },
|
||||
{ 0x011700, "Recovered data with no error correction applied" },
|
||||
{ 0x011701, "Recovered data with retries" },
|
||||
{ 0x011702, "Recovered data with positive head offset" },
|
||||
{ 0x011703, "Recovered data with negative head offset" },
|
||||
{ 0x011704, "Recovered data with retries and/or CIRC applied" },
|
||||
{ 0x011705, "Recovered data using previous sector ID" },
|
||||
{ 0x011800, "Recovered data with error correction applied" },
|
||||
{ 0x011801, "Recovered data with error correction and retries applied"},
|
||||
{ 0x011802, "Recovered data - the data was auto-reallocated" },
|
||||
{ 0x011803, "Recovered data with CIRC" },
|
||||
{ 0x011804, "Recovered data with L-EC" },
|
||||
{ 0x015d00,
|
||||
"Failure prediction threshold exceeded - Predicted logical unit failure" },
|
||||
{ 0x015d01,
|
||||
"Failure prediction threshold exceeded - Predicted media failure" },
|
||||
{ 0x015dff, "Failure prediction threshold exceeded - False" },
|
||||
{ 0x017301, "Power calibration area almost full" },
|
||||
{ 0x020400, "Logical unit not ready - cause not reportable" },
|
||||
/* Following is misspelled in ATAPI 2.6, _and_ in Mt. Fuji */
|
||||
{ 0x020401,
|
||||
"Logical unit not ready - in progress [sic] of becoming ready" },
|
||||
{ 0x020402, "Logical unit not ready - initializing command required" },
|
||||
{ 0x020403, "Logical unit not ready - manual intervention required" },
|
||||
{ 0x020404, "Logical unit not ready - format in progress" },
|
||||
{ 0x020407, "Logical unit not ready - operation in progress" },
|
||||
{ 0x020408, "Logical unit not ready - long write in progress" },
|
||||
{ 0x020600, "No reference position found (media may be upside down)" },
|
||||
{ 0x023000, "Incompatible medium installed" },
|
||||
{ 0x023a00, "Medium not present" },
|
||||
{ 0x025300, "Media load or eject failed" },
|
||||
{ 0x025700, "Unable to recover table of contents" },
|
||||
{ 0x030300, "Peripheral device write fault" },
|
||||
{ 0x030301, "No write current" },
|
||||
{ 0x030302, "Excessive write errors" },
|
||||
{ 0x030c00, "Write error" },
|
||||
{ 0x030c01, "Write error - Recovered with auto reallocation" },
|
||||
{ 0x030c02, "Write error - auto reallocation failed" },
|
||||
{ 0x030c03, "Write error - recommend reassignment" },
|
||||
{ 0x030c04, "Compression check miscompare error" },
|
||||
{ 0x030c05, "Data expansion occurred during compress" },
|
||||
{ 0x030c06, "Block not compressible" },
|
||||
{ 0x030c07, "Write error - recovery needed" },
|
||||
{ 0x030c08, "Write error - recovery failed" },
|
||||
{ 0x030c09, "Write error - loss of streaming" },
|
||||
{ 0x031100, "Unrecovered read error" },
|
||||
{ 0x031106, "CIRC unrecovered error" },
|
||||
{ 0x033101, "Format command failed" },
|
||||
{ 0x033200, "No defect spare location available" },
|
||||
{ 0x033201, "Defect list update failure" },
|
||||
{ 0x035100, "Erase failure" },
|
||||
{ 0x037200, "Session fixation error" },
|
||||
{ 0x037201, "Session fixation error writin lead-in" },
|
||||
{ 0x037202, "Session fixation error writin lead-out" },
|
||||
{ 0x037300, "CD control error" },
|
||||
{ 0x037302, "Power calibration area is full" },
|
||||
{ 0x037303, "Power calibration area error" },
|
||||
{ 0x037304, "Program memory area / RMA update failure" },
|
||||
{ 0x037305, "Program memory area / RMA is full" },
|
||||
{ 0x037306, "Program memory area / RMA is (almost) full" },
|
||||
|
||||
{ 0x040200, "No seek complete" },
|
||||
{ 0x040300, "Write fault" },
|
||||
{ 0x040900, "Track following error" },
|
||||
{ 0x040901, "Tracking servo failure" },
|
||||
{ 0x040902, "Focus servo failure" },
|
||||
{ 0x040903, "Spindle servo failure" },
|
||||
{ 0x041500, "Random positioning error" },
|
||||
{ 0x041501, "Mechanical positioning or changer error" },
|
||||
{ 0x041502, "Positioning error detected by read of medium" },
|
||||
{ 0x043c00, "Mechanical positioning or changer error" },
|
||||
{ 0x044000, "Diagnostic failure on component (ASCQ)" },
|
||||
{ 0x044400, "Internal CD/DVD logical unit failure" },
|
||||
{ 0x04b600, "Media load mechanism failed" },
|
||||
{ 0x051a00, "Parameter list length error" },
|
||||
{ 0x052000, "Invalid command operation code" },
|
||||
{ 0x052100, "Logical block address out of range" },
|
||||
{ 0x052102, "Invalid address for write" },
|
||||
{ 0x052400, "Invalid field in command packet" },
|
||||
{ 0x052600, "Invalid field in parameter list" },
|
||||
{ 0x052601, "Parameter not supported" },
|
||||
{ 0x052602, "Parameter value invalid" },
|
||||
{ 0x052700, "Write protected media" },
|
||||
{ 0x052c00, "Command sequence error" },
|
||||
{ 0x052c03, "Current program area is not empty" },
|
||||
{ 0x052c04, "Current program area is empty" },
|
||||
{ 0x053001, "Cannot read medium - unknown format" },
|
||||
{ 0x053002, "Cannot read medium - incompatible format" },
|
||||
{ 0x053900, "Saving parameters not supported" },
|
||||
{ 0x054e00, "Overlapped commands attempted" },
|
||||
{ 0x055302, "Medium removal prevented" },
|
||||
{ 0x055500, "System resource failure" },
|
||||
{ 0x056300, "End of user area encountered on this track" },
|
||||
{ 0x056400, "Illegal mode for this track or incompatible medium" },
|
||||
{ 0x056f00, "Copy protection key exchange failure - Authentication failure" },
|
||||
{ 0x056f01, "Copy protection key exchange failure - Key not present" },
|
||||
{ 0x056f02, "Copy protection key exchange failure - Key not established" },
|
||||
{ 0x056f03, "Read of scrambled sector without authentication" },
|
||||
{ 0x056f04, "Media region code is mismatched to logical unit" },
|
||||
{ 0x056f05, "Drive region must be permanent / region reset count error" },
|
||||
{ 0x057203, "Session fixation error - incomplete track in session" },
|
||||
{ 0x057204, "Empty or partially written reserved track" },
|
||||
{ 0x057205, "No more RZONE reservations are allowed" },
|
||||
{ 0x05bf00, "Loss of streaming" },
|
||||
{ 0x062800, "Not ready to ready transition, medium may have changed" },
|
||||
{ 0x062900, "Power on, reset or hardware reset occurred" },
|
||||
{ 0x062a00, "Parameters changed" },
|
||||
{ 0x062a01, "Mode parameters changed" },
|
||||
{ 0x062e00, "Insufficient time for operation" },
|
||||
{ 0x063f00, "Logical unit operating conditions have changed" },
|
||||
{ 0x063f01, "Microcode has been changed" },
|
||||
{ 0x065a00, "Operator request or state change input (unspecified)" },
|
||||
{ 0x065a01, "Operator medium removal request" },
|
||||
{ 0x0bb900, "Play operation aborted" },
|
||||
|
||||
/* Here we use 0xff for the key (not a valid key) to signify
|
||||
* that these can have _any_ key value associated with them... */
|
||||
{ 0xff0401, "Logical unit is in process of becoming ready" },
|
||||
{ 0xff0400, "Logical unit not ready, cause not reportable" },
|
||||
{ 0xff0402, "Logical unit not ready, initializing command required" },
|
||||
{ 0xff0403, "Logical unit not ready, manual intervention required" },
|
||||
{ 0xff0500, "Logical unit does not respond to selection" },
|
||||
{ 0xff0800, "Logical unit communication failure" },
|
||||
{ 0xff0802, "Logical unit communication parity error" },
|
||||
{ 0xff0801, "Logical unit communication time-out" },
|
||||
{ 0xff2500, "Logical unit not supported" },
|
||||
{ 0xff4c00, "Logical unit failed self-configuration" },
|
||||
{ 0xff3e00, "Logical unit has not self-configured yet" },
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _IDE_CD_H */
|
||||
1252
drivers/ide/ide-disk.c
Normal file
1252
drivers/ide/ide-disk.c
Normal file
File diff suppressed because it is too large
Load Diff
971
drivers/ide/ide-dma.c
Normal file
971
drivers/ide/ide-dma.c
Normal file
@@ -0,0 +1,971 @@
|
||||
/*
|
||||
* linux/drivers/ide/ide-dma.c Version 4.10 June 9, 2000
|
||||
*
|
||||
* Copyright (c) 1999-2000 Andre Hedrick <andre@linux-ide.org>
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*/
|
||||
|
||||
/*
|
||||
* Special Thanks to Mark for his Six years of work.
|
||||
*
|
||||
* Copyright (c) 1995-1998 Mark Lord
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*/
|
||||
|
||||
/*
|
||||
* This module provides support for the bus-master IDE DMA functions
|
||||
* of various PCI chipsets, including the Intel PIIX (i82371FB for
|
||||
* the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and
|
||||
* 440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset)
|
||||
* ("PIIX" stands for "PCI ISA IDE Xcellerator").
|
||||
*
|
||||
* Pretty much the same code works for other IDE PCI bus-mastering chipsets.
|
||||
*
|
||||
* DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies).
|
||||
*
|
||||
* By default, DMA support is prepared for use, but is currently enabled only
|
||||
* for drives which already have DMA enabled (UltraDMA or mode 2 multi/single),
|
||||
* or which are recognized as "good" (see table below). Drives with only mode0
|
||||
* or mode1 (multi/single) DMA should also work with this chipset/driver
|
||||
* (eg. MC2112A) but are not enabled by default.
|
||||
*
|
||||
* Use "hdparm -i" to view modes supported by a given drive.
|
||||
*
|
||||
* The hdparm-3.5 (or later) utility can be used for manually enabling/disabling
|
||||
* DMA support, but must be (re-)compiled against this kernel version or later.
|
||||
*
|
||||
* To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting.
|
||||
* If problems arise, ide.c will disable DMA operation after a few retries.
|
||||
* This error recovery mechanism works and has been extremely well exercised.
|
||||
*
|
||||
* IDE drives, depending on their vintage, may support several different modes
|
||||
* of DMA operation. The boot-time modes are indicated with a "*" in
|
||||
* the "hdparm -i" listing, and can be changed with *knowledgeable* use of
|
||||
* the "hdparm -X" feature. There is seldom a need to do this, as drives
|
||||
* normally power-up with their "best" PIO/DMA modes enabled.
|
||||
*
|
||||
* Testing has been done with a rather extensive number of drives,
|
||||
* with Quantum & Western Digital models generally outperforming the pack,
|
||||
* and Fujitsu & Conner (and some Seagate which are really Conner) drives
|
||||
* showing more lackluster throughput.
|
||||
*
|
||||
* Keep an eye on /var/adm/messages for "DMA disabled" messages.
|
||||
*
|
||||
* Some people have reported trouble with Intel Zappa motherboards.
|
||||
* This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0,
|
||||
* available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
|
||||
* (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
|
||||
*
|
||||
* Thanks to "Christopher J. Reimer" <reimer@doe.carleton.ca> for
|
||||
* fixing the problem with the BIOS on some Acer motherboards.
|
||||
*
|
||||
* Thanks to "Benoit Poulot-Cazajous" <poulot@chorus.fr> for testing
|
||||
* "TX" chipset compatibility and for providing patches for the "TX" chipset.
|
||||
*
|
||||
* Thanks to Christian Brunner <chb@muc.de> for taking a good first crack
|
||||
* at generic DMA -- his patches were referred to when preparing this code.
|
||||
*
|
||||
* Most importantly, thanks to Robert Bringman <rob@mars.trion.com>
|
||||
* for supplying a Promise UDMA board & WD UDMA drive for this work!
|
||||
*
|
||||
* And, yes, Intel Zappa boards really *do* use both PIIX IDE ports.
|
||||
*
|
||||
* ATA-66/100 and recovery functions, I forgot the rest......
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
static const struct drive_list_entry drive_whitelist [] = {
|
||||
|
||||
{ "Micropolis 2112A" , "ALL" },
|
||||
{ "CONNER CTMA 4000" , "ALL" },
|
||||
{ "CONNER CTT8000-A" , "ALL" },
|
||||
{ "ST34342A" , "ALL" },
|
||||
{ NULL , NULL }
|
||||
};
|
||||
|
||||
static const struct drive_list_entry drive_blacklist [] = {
|
||||
|
||||
{ "WDC AC11000H" , "ALL" },
|
||||
{ "WDC AC22100H" , "ALL" },
|
||||
{ "WDC AC32500H" , "ALL" },
|
||||
{ "WDC AC33100H" , "ALL" },
|
||||
{ "WDC AC31600H" , "ALL" },
|
||||
{ "WDC AC32100H" , "24.09P07" },
|
||||
{ "WDC AC23200L" , "21.10N21" },
|
||||
{ "Compaq CRD-8241B" , "ALL" },
|
||||
{ "CRD-8400B" , "ALL" },
|
||||
{ "CRD-8480B", "ALL" },
|
||||
{ "CRD-8482B", "ALL" },
|
||||
{ "CRD-84" , "ALL" },
|
||||
{ "SanDisk SDP3B" , "ALL" },
|
||||
{ "SanDisk SDP3B-64" , "ALL" },
|
||||
{ "SANYO CD-ROM CRD" , "ALL" },
|
||||
{ "HITACHI CDR-8" , "ALL" },
|
||||
{ "HITACHI CDR-8335" , "ALL" },
|
||||
{ "HITACHI CDR-8435" , "ALL" },
|
||||
{ "Toshiba CD-ROM XM-6202B" , "ALL" },
|
||||
{ "CD-532E-A" , "ALL" },
|
||||
{ "E-IDE CD-ROM CR-840", "ALL" },
|
||||
{ "CD-ROM Drive/F5A", "ALL" },
|
||||
{ "WPI CDD-820", "ALL" },
|
||||
{ "SAMSUNG CD-ROM SC-148C", "ALL" },
|
||||
{ "SAMSUNG CD-ROM SC", "ALL" },
|
||||
{ "SanDisk SDP3B-64" , "ALL" },
|
||||
{ "ATAPI CD-ROM DRIVE 40X MAXIMUM", "ALL" },
|
||||
{ "_NEC DV5800A", "ALL" },
|
||||
{ NULL , NULL }
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* ide_in_drive_list - look for drive in black/white list
|
||||
* @id: drive identifier
|
||||
* @drive_table: list to inspect
|
||||
*
|
||||
* Look for a drive in the blacklist and the whitelist tables
|
||||
* Returns 1 if the drive is found in the table.
|
||||
*/
|
||||
|
||||
int ide_in_drive_list(struct hd_driveid *id, const struct drive_list_entry *drive_table)
|
||||
{
|
||||
for ( ; drive_table->id_model ; drive_table++)
|
||||
if ((!strcmp(drive_table->id_model, id->model)) &&
|
||||
((strstr(id->fw_rev, drive_table->id_firmware)) ||
|
||||
(!strcmp(drive_table->id_firmware, "ALL"))))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_dma_intr - IDE DMA interrupt handler
|
||||
* @drive: the drive the interrupt is for
|
||||
*
|
||||
* Handle an interrupt completing a read/write DMA transfer on an
|
||||
* IDE device
|
||||
*/
|
||||
|
||||
ide_startstop_t ide_dma_intr (ide_drive_t *drive)
|
||||
{
|
||||
u8 stat = 0, dma_stat = 0;
|
||||
|
||||
dma_stat = HWIF(drive)->ide_dma_end(drive);
|
||||
stat = HWIF(drive)->INB(IDE_STATUS_REG); /* get drive status */
|
||||
if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
|
||||
if (!dma_stat) {
|
||||
struct request *rq = HWGROUP(drive)->rq;
|
||||
|
||||
if (rq->rq_disk) {
|
||||
ide_driver_t *drv;
|
||||
|
||||
drv = *(ide_driver_t **)rq->rq_disk->private_data;
|
||||
drv->end_request(drive, 1, rq->nr_sectors);
|
||||
} else
|
||||
ide_end_request(drive, 1, rq->nr_sectors);
|
||||
return ide_stopped;
|
||||
}
|
||||
printk(KERN_ERR "%s: dma_intr: bad DMA status (dma_stat=%x)\n",
|
||||
drive->name, dma_stat);
|
||||
}
|
||||
return ide_error(drive, "dma_intr", stat);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_dma_intr);
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDEDMA_PCI
|
||||
/**
|
||||
* ide_build_sglist - map IDE scatter gather for DMA I/O
|
||||
* @drive: the drive to build the DMA table for
|
||||
* @rq: the request holding the sg list
|
||||
*
|
||||
* Perform the PCI mapping magic necessary to access the source or
|
||||
* target buffers of a request via PCI DMA. The lower layers of the
|
||||
* kernel provide the necessary cache management so that we can
|
||||
* operate in a portable fashion
|
||||
*/
|
||||
|
||||
int ide_build_sglist(ide_drive_t *drive, struct request *rq)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct scatterlist *sg = hwif->sg_table;
|
||||
|
||||
BUG_ON((rq->cmd_type == REQ_TYPE_ATA_TASKFILE) && rq->nr_sectors > 256);
|
||||
|
||||
ide_map_sg(drive, rq);
|
||||
|
||||
if (rq_data_dir(rq) == READ)
|
||||
hwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
|
||||
else
|
||||
hwif->sg_dma_direction = PCI_DMA_TODEVICE;
|
||||
|
||||
return pci_map_sg(hwif->pci_dev, sg, hwif->sg_nents, hwif->sg_dma_direction);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_build_sglist);
|
||||
|
||||
/**
|
||||
* ide_build_dmatable - build IDE DMA table
|
||||
*
|
||||
* ide_build_dmatable() prepares a dma request. We map the command
|
||||
* to get the pci bus addresses of the buffers and then build up
|
||||
* the PRD table that the IDE layer wants to be fed. The code
|
||||
* knows about the 64K wrap bug in the CS5530.
|
||||
*
|
||||
* Returns the number of built PRD entries if all went okay,
|
||||
* returns 0 otherwise.
|
||||
*
|
||||
* May also be invoked from trm290.c
|
||||
*/
|
||||
|
||||
int ide_build_dmatable (ide_drive_t *drive, struct request *rq)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned int *table = hwif->dmatable_cpu;
|
||||
unsigned int is_trm290 = (hwif->chipset == ide_trm290) ? 1 : 0;
|
||||
unsigned int count = 0;
|
||||
int i;
|
||||
struct scatterlist *sg;
|
||||
|
||||
hwif->sg_nents = i = ide_build_sglist(drive, rq);
|
||||
|
||||
if (!i)
|
||||
return 0;
|
||||
|
||||
sg = hwif->sg_table;
|
||||
while (i) {
|
||||
u32 cur_addr;
|
||||
u32 cur_len;
|
||||
|
||||
cur_addr = sg_dma_address(sg);
|
||||
cur_len = sg_dma_len(sg);
|
||||
|
||||
/*
|
||||
* Fill in the dma table, without crossing any 64kB boundaries.
|
||||
* Most hardware requires 16-bit alignment of all blocks,
|
||||
* but the trm290 requires 32-bit alignment.
|
||||
*/
|
||||
|
||||
while (cur_len) {
|
||||
if (count++ >= PRD_ENTRIES) {
|
||||
printk(KERN_ERR "%s: DMA table too small\n", drive->name);
|
||||
goto use_pio_instead;
|
||||
} else {
|
||||
u32 xcount, bcount = 0x10000 - (cur_addr & 0xffff);
|
||||
|
||||
if (bcount > cur_len)
|
||||
bcount = cur_len;
|
||||
*table++ = cpu_to_le32(cur_addr);
|
||||
xcount = bcount & 0xffff;
|
||||
if (is_trm290)
|
||||
xcount = ((xcount >> 2) - 1) << 16;
|
||||
if (xcount == 0x0000) {
|
||||
/*
|
||||
* Most chipsets correctly interpret a length of 0x0000 as 64KB,
|
||||
* but at least one (e.g. CS5530) misinterprets it as zero (!).
|
||||
* So here we break the 64KB entry into two 32KB entries instead.
|
||||
*/
|
||||
if (count++ >= PRD_ENTRIES) {
|
||||
printk(KERN_ERR "%s: DMA table too small\n", drive->name);
|
||||
goto use_pio_instead;
|
||||
}
|
||||
*table++ = cpu_to_le32(0x8000);
|
||||
*table++ = cpu_to_le32(cur_addr + 0x8000);
|
||||
xcount = 0x8000;
|
||||
}
|
||||
*table++ = cpu_to_le32(xcount);
|
||||
cur_addr += bcount;
|
||||
cur_len -= bcount;
|
||||
}
|
||||
}
|
||||
|
||||
sg++;
|
||||
i--;
|
||||
}
|
||||
|
||||
if (count) {
|
||||
if (!is_trm290)
|
||||
*--table |= cpu_to_le32(0x80000000);
|
||||
return count;
|
||||
}
|
||||
printk(KERN_ERR "%s: empty DMA table?\n", drive->name);
|
||||
use_pio_instead:
|
||||
pci_unmap_sg(hwif->pci_dev,
|
||||
hwif->sg_table,
|
||||
hwif->sg_nents,
|
||||
hwif->sg_dma_direction);
|
||||
return 0; /* revert to PIO for this request */
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_build_dmatable);
|
||||
|
||||
/**
|
||||
* ide_destroy_dmatable - clean up DMA mapping
|
||||
* @drive: The drive to unmap
|
||||
*
|
||||
* Teardown mappings after DMA has completed. This must be called
|
||||
* after the completion of each use of ide_build_dmatable and before
|
||||
* the next use of ide_build_dmatable. Failure to do so will cause
|
||||
* an oops as only one mapping can be live for each target at a given
|
||||
* time.
|
||||
*/
|
||||
|
||||
void ide_destroy_dmatable (ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = HWIF(drive)->pci_dev;
|
||||
struct scatterlist *sg = HWIF(drive)->sg_table;
|
||||
int nents = HWIF(drive)->sg_nents;
|
||||
|
||||
pci_unmap_sg(dev, sg, nents, HWIF(drive)->sg_dma_direction);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_destroy_dmatable);
|
||||
|
||||
/**
|
||||
* config_drive_for_dma - attempt to activate IDE DMA
|
||||
* @drive: the drive to place in DMA mode
|
||||
*
|
||||
* If the drive supports at least mode 2 DMA or UDMA of any kind
|
||||
* then attempt to place it into DMA mode. Drives that are known to
|
||||
* support DMA but predate the DMA properties or that are known
|
||||
* to have DMA handling bugs are also set up appropriately based
|
||||
* on the good/bad drive lists.
|
||||
*/
|
||||
|
||||
static int config_drive_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
struct hd_driveid *id = drive->id;
|
||||
|
||||
if ((id->capability & 1) && drive->hwif->autodma) {
|
||||
/*
|
||||
* Enable DMA on any drive that has
|
||||
* UltraDMA (mode 0/1/2/3/4/5/6) enabled
|
||||
*/
|
||||
if ((id->field_valid & 4) && ((id->dma_ultra >> 8) & 0x7f))
|
||||
return 0;
|
||||
/*
|
||||
* Enable DMA on any drive that has mode2 DMA
|
||||
* (multi or single) enabled
|
||||
*/
|
||||
if (id->field_valid & 2) /* regular DMA */
|
||||
if ((id->dma_mword & 0x404) == 0x404 ||
|
||||
(id->dma_1word & 0x404) == 0x404)
|
||||
return 0;
|
||||
|
||||
/* Consult the list of known "good" drives */
|
||||
if (__ide_dma_good_drive(drive))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_timer_expiry - handle a DMA timeout
|
||||
* @drive: Drive that timed out
|
||||
*
|
||||
* An IDE DMA transfer timed out. In the event of an error we ask
|
||||
* the driver to resolve the problem, if a DMA transfer is still
|
||||
* in progress we continue to wait (arguably we need to add a
|
||||
* secondary 'I don't care what the drive thinks' timeout here)
|
||||
* Finally if we have an interrupt we let it complete the I/O.
|
||||
* But only one time - we clear expiry and if it's still not
|
||||
* completed after WAIT_CMD, we error and retry in PIO.
|
||||
* This can occur if an interrupt is lost or due to hang or bugs.
|
||||
*/
|
||||
|
||||
static int dma_timer_expiry (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 dma_stat = hwif->INB(hwif->dma_status);
|
||||
|
||||
printk(KERN_WARNING "%s: dma_timer_expiry: dma status == 0x%02x\n",
|
||||
drive->name, dma_stat);
|
||||
|
||||
if ((dma_stat & 0x18) == 0x18) /* BUSY Stupid Early Timer !! */
|
||||
return WAIT_CMD;
|
||||
|
||||
HWGROUP(drive)->expiry = NULL; /* one free ride for now */
|
||||
|
||||
/* 1 dmaing, 2 error, 4 intr */
|
||||
if (dma_stat & 2) /* ERROR */
|
||||
return -1;
|
||||
|
||||
if (dma_stat & 1) /* DMAing */
|
||||
return WAIT_CMD;
|
||||
|
||||
if (dma_stat & 4) /* Got an Interrupt */
|
||||
return WAIT_CMD;
|
||||
|
||||
return 0; /* Status is unknown -- reset the bus */
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_dma_host_off - Generic DMA kill
|
||||
* @drive: drive to control
|
||||
*
|
||||
* Perform the generic IDE controller DMA off operation. This
|
||||
* works for most IDE bus mastering controllers
|
||||
*/
|
||||
|
||||
void ide_dma_host_off(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 unit = (drive->select.b.unit & 0x01);
|
||||
u8 dma_stat = hwif->INB(hwif->dma_status);
|
||||
|
||||
hwif->OUTB((dma_stat & ~(1<<(5+unit))), hwif->dma_status);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ide_dma_host_off);
|
||||
|
||||
/**
|
||||
* ide_dma_off_quietly - Generic DMA kill
|
||||
* @drive: drive to control
|
||||
*
|
||||
* Turn off the current DMA on this IDE controller.
|
||||
*/
|
||||
|
||||
void ide_dma_off_quietly(ide_drive_t *drive)
|
||||
{
|
||||
drive->using_dma = 0;
|
||||
ide_toggle_bounce(drive, 0);
|
||||
|
||||
drive->hwif->dma_host_off(drive);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ide_dma_off_quietly);
|
||||
#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */
|
||||
|
||||
/**
|
||||
* ide_dma_off - disable DMA on a device
|
||||
* @drive: drive to disable DMA on
|
||||
*
|
||||
* Disable IDE DMA for a device on this IDE controller.
|
||||
* Inform the user that DMA has been disabled.
|
||||
*/
|
||||
|
||||
void ide_dma_off(ide_drive_t *drive)
|
||||
{
|
||||
printk(KERN_INFO "%s: DMA disabled\n", drive->name);
|
||||
drive->hwif->dma_off_quietly(drive);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ide_dma_off);
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDEDMA_PCI
|
||||
/**
|
||||
* ide_dma_host_on - Enable DMA on a host
|
||||
* @drive: drive to enable for DMA
|
||||
*
|
||||
* Enable DMA on an IDE controller following generic bus mastering
|
||||
* IDE controller behaviour
|
||||
*/
|
||||
|
||||
void ide_dma_host_on(ide_drive_t *drive)
|
||||
{
|
||||
if (drive->using_dma) {
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 unit = (drive->select.b.unit & 0x01);
|
||||
u8 dma_stat = hwif->INB(hwif->dma_status);
|
||||
|
||||
hwif->OUTB((dma_stat|(1<<(5+unit))), hwif->dma_status);
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ide_dma_host_on);
|
||||
|
||||
/**
|
||||
* __ide_dma_on - Enable DMA on a device
|
||||
* @drive: drive to enable DMA on
|
||||
*
|
||||
* Enable IDE DMA for a device on this IDE controller.
|
||||
*/
|
||||
|
||||
int __ide_dma_on (ide_drive_t *drive)
|
||||
{
|
||||
/* consult the list of known "bad" drives */
|
||||
if (__ide_dma_bad_drive(drive))
|
||||
return 1;
|
||||
|
||||
drive->using_dma = 1;
|
||||
ide_toggle_bounce(drive, 1);
|
||||
|
||||
drive->hwif->dma_host_on(drive);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(__ide_dma_on);
|
||||
|
||||
/**
|
||||
* __ide_dma_check - check DMA setup
|
||||
* @drive: drive to check
|
||||
*
|
||||
* Don't use - due for extermination
|
||||
*/
|
||||
|
||||
int __ide_dma_check (ide_drive_t *drive)
|
||||
{
|
||||
return config_drive_for_dma(drive);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(__ide_dma_check);
|
||||
|
||||
/**
|
||||
* ide_dma_setup - begin a DMA phase
|
||||
* @drive: target device
|
||||
*
|
||||
* Build an IDE DMA PRD (IDE speak for scatter gather table)
|
||||
* and then set up the DMA transfer registers for a device
|
||||
* that follows generic IDE PCI DMA behaviour. Controllers can
|
||||
* override this function if they need to
|
||||
*
|
||||
* Returns 0 on success. If a PIO fallback is required then 1
|
||||
* is returned.
|
||||
*/
|
||||
|
||||
int ide_dma_setup(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct request *rq = HWGROUP(drive)->rq;
|
||||
unsigned int reading;
|
||||
u8 dma_stat;
|
||||
|
||||
if (rq_data_dir(rq))
|
||||
reading = 0;
|
||||
else
|
||||
reading = 1 << 3;
|
||||
|
||||
/* fall back to pio! */
|
||||
if (!ide_build_dmatable(drive, rq)) {
|
||||
ide_map_sg(drive, rq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* PRD table */
|
||||
if (hwif->mmio)
|
||||
writel(hwif->dmatable_dma, (void __iomem *)hwif->dma_prdtable);
|
||||
else
|
||||
outl(hwif->dmatable_dma, hwif->dma_prdtable);
|
||||
|
||||
/* specify r/w */
|
||||
hwif->OUTB(reading, hwif->dma_command);
|
||||
|
||||
/* read dma_status for INTR & ERROR flags */
|
||||
dma_stat = hwif->INB(hwif->dma_status);
|
||||
|
||||
/* clear INTR & ERROR flags */
|
||||
hwif->OUTB(dma_stat|6, hwif->dma_status);
|
||||
drive->waiting_for_dma = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_dma_setup);
|
||||
|
||||
static void ide_dma_exec_cmd(ide_drive_t *drive, u8 command)
|
||||
{
|
||||
/* issue cmd to drive */
|
||||
ide_execute_command(drive, command, &ide_dma_intr, 2*WAIT_CMD, dma_timer_expiry);
|
||||
}
|
||||
|
||||
void ide_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 dma_cmd = hwif->INB(hwif->dma_command);
|
||||
|
||||
/* Note that this is done *after* the cmd has
|
||||
* been issued to the drive, as per the BM-IDE spec.
|
||||
* The Promise Ultra33 doesn't work correctly when
|
||||
* we do this part before issuing the drive cmd.
|
||||
*/
|
||||
/* start DMA */
|
||||
hwif->OUTB(dma_cmd|1, hwif->dma_command);
|
||||
hwif->dma = 1;
|
||||
wmb();
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_dma_start);
|
||||
|
||||
/* returns 1 on error, 0 otherwise */
|
||||
int __ide_dma_end (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 dma_stat = 0, dma_cmd = 0;
|
||||
|
||||
drive->waiting_for_dma = 0;
|
||||
/* get dma_command mode */
|
||||
dma_cmd = hwif->INB(hwif->dma_command);
|
||||
/* stop DMA */
|
||||
hwif->OUTB(dma_cmd&~1, hwif->dma_command);
|
||||
/* get DMA status */
|
||||
dma_stat = hwif->INB(hwif->dma_status);
|
||||
/* clear the INTR & ERROR bits */
|
||||
hwif->OUTB(dma_stat|6, hwif->dma_status);
|
||||
/* purge DMA mappings */
|
||||
ide_destroy_dmatable(drive);
|
||||
/* verify good DMA status */
|
||||
hwif->dma = 0;
|
||||
wmb();
|
||||
return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(__ide_dma_end);
|
||||
|
||||
/* returns 1 if dma irq issued, 0 otherwise */
|
||||
static int __ide_dma_test_irq(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 dma_stat = hwif->INB(hwif->dma_status);
|
||||
|
||||
#if 0 /* do not set unless you know what you are doing */
|
||||
if (dma_stat & 4) {
|
||||
u8 stat = hwif->INB(IDE_STATUS_REG);
|
||||
hwif->OUTB(hwif->dma_status, dma_stat & 0xE4);
|
||||
}
|
||||
#endif
|
||||
/* return 1 if INTR asserted */
|
||||
if ((dma_stat & 4) == 4)
|
||||
return 1;
|
||||
if (!drive->waiting_for_dma)
|
||||
printk(KERN_WARNING "%s: (%s) called while not waiting\n",
|
||||
drive->name, __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */
|
||||
|
||||
int __ide_dma_bad_drive (ide_drive_t *drive)
|
||||
{
|
||||
struct hd_driveid *id = drive->id;
|
||||
|
||||
int blacklist = ide_in_drive_list(id, drive_blacklist);
|
||||
if (blacklist) {
|
||||
printk(KERN_WARNING "%s: Disabling (U)DMA for %s (blacklisted)\n",
|
||||
drive->name, id->model);
|
||||
return blacklist;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(__ide_dma_bad_drive);
|
||||
|
||||
int __ide_dma_good_drive (ide_drive_t *drive)
|
||||
{
|
||||
struct hd_driveid *id = drive->id;
|
||||
return ide_in_drive_list(id, drive_whitelist);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(__ide_dma_good_drive);
|
||||
|
||||
int ide_use_dma(ide_drive_t *drive)
|
||||
{
|
||||
struct hd_driveid *id = drive->id;
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
|
||||
if ((id->capability & 1) == 0 || drive->autodma == 0)
|
||||
return 0;
|
||||
|
||||
/* consult the list of known "bad" drives */
|
||||
if (__ide_dma_bad_drive(drive))
|
||||
return 0;
|
||||
|
||||
/* capable of UltraDMA modes */
|
||||
if (id->field_valid & 4) {
|
||||
if (hwif->ultra_mask & id->dma_ultra)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* capable of regular DMA modes */
|
||||
if (id->field_valid & 2) {
|
||||
if (hwif->mwdma_mask & id->dma_mword)
|
||||
return 1;
|
||||
if (hwif->swdma_mask & id->dma_1word)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* consult the list of known "good" drives */
|
||||
if (__ide_dma_good_drive(drive) && id->eide_dma_time < 150)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_use_dma);
|
||||
|
||||
void ide_dma_verbose(ide_drive_t *drive)
|
||||
{
|
||||
struct hd_driveid *id = drive->id;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
|
||||
if (id->field_valid & 4) {
|
||||
if ((id->dma_ultra >> 8) && (id->dma_mword >> 8))
|
||||
goto bug_dma_off;
|
||||
if (id->dma_ultra & ((id->dma_ultra >> 8) & hwif->ultra_mask)) {
|
||||
if (((id->dma_ultra >> 11) & 0x1F) &&
|
||||
eighty_ninty_three(drive)) {
|
||||
if ((id->dma_ultra >> 15) & 1) {
|
||||
printk(", UDMA(mode 7)");
|
||||
} else if ((id->dma_ultra >> 14) & 1) {
|
||||
printk(", UDMA(133)");
|
||||
} else if ((id->dma_ultra >> 13) & 1) {
|
||||
printk(", UDMA(100)");
|
||||
} else if ((id->dma_ultra >> 12) & 1) {
|
||||
printk(", UDMA(66)");
|
||||
} else if ((id->dma_ultra >> 11) & 1) {
|
||||
printk(", UDMA(44)");
|
||||
} else
|
||||
goto mode_two;
|
||||
} else {
|
||||
mode_two:
|
||||
if ((id->dma_ultra >> 10) & 1) {
|
||||
printk(", UDMA(33)");
|
||||
} else if ((id->dma_ultra >> 9) & 1) {
|
||||
printk(", UDMA(25)");
|
||||
} else if ((id->dma_ultra >> 8) & 1) {
|
||||
printk(", UDMA(16)");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printk(", (U)DMA"); /* Can be BIOS-enabled! */
|
||||
}
|
||||
} else if (id->field_valid & 2) {
|
||||
if ((id->dma_mword >> 8) && (id->dma_1word >> 8))
|
||||
goto bug_dma_off;
|
||||
printk(", DMA");
|
||||
} else if (id->field_valid & 1) {
|
||||
goto bug_dma_off;
|
||||
}
|
||||
return;
|
||||
bug_dma_off:
|
||||
printk(", BUG DMA OFF");
|
||||
hwif->dma_off_quietly(drive);
|
||||
return;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ide_dma_verbose);
|
||||
|
||||
int ide_set_dma(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
int rc;
|
||||
|
||||
rc = hwif->ide_dma_check(drive);
|
||||
|
||||
switch(rc) {
|
||||
case -1: /* DMA needs to be disabled */
|
||||
hwif->dma_off_quietly(drive);
|
||||
return -1;
|
||||
case 0: /* DMA needs to be enabled */
|
||||
return hwif->ide_dma_on(drive);
|
||||
case 1: /* DMA setting cannot be changed */
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_set_dma);
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDEDMA_PCI
|
||||
int __ide_dma_lostirq (ide_drive_t *drive)
|
||||
{
|
||||
printk("%s: DMA interrupt recovery\n", drive->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(__ide_dma_lostirq);
|
||||
|
||||
int __ide_dma_timeout (ide_drive_t *drive)
|
||||
{
|
||||
printk(KERN_ERR "%s: timeout waiting for DMA\n", drive->name);
|
||||
if (HWIF(drive)->ide_dma_test_irq(drive))
|
||||
return 0;
|
||||
|
||||
return HWIF(drive)->ide_dma_end(drive);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(__ide_dma_timeout);
|
||||
|
||||
/*
|
||||
* Needed for allowing full modular support of ide-driver
|
||||
*/
|
||||
static int ide_release_dma_engine(ide_hwif_t *hwif)
|
||||
{
|
||||
if (hwif->dmatable_cpu) {
|
||||
pci_free_consistent(hwif->pci_dev,
|
||||
PRD_ENTRIES * PRD_BYTES,
|
||||
hwif->dmatable_cpu,
|
||||
hwif->dmatable_dma);
|
||||
hwif->dmatable_cpu = NULL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ide_release_iomio_dma(ide_hwif_t *hwif)
|
||||
{
|
||||
release_region(hwif->dma_base, 8);
|
||||
if (hwif->extra_ports)
|
||||
release_region(hwif->extra_base, hwif->extra_ports);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Needed for allowing full modular support of ide-driver
|
||||
*/
|
||||
int ide_release_dma(ide_hwif_t *hwif)
|
||||
{
|
||||
ide_release_dma_engine(hwif);
|
||||
|
||||
if (hwif->mmio)
|
||||
return 1;
|
||||
else
|
||||
return ide_release_iomio_dma(hwif);
|
||||
}
|
||||
|
||||
static int ide_allocate_dma_engine(ide_hwif_t *hwif)
|
||||
{
|
||||
hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev,
|
||||
PRD_ENTRIES * PRD_BYTES,
|
||||
&hwif->dmatable_dma);
|
||||
|
||||
if (hwif->dmatable_cpu)
|
||||
return 0;
|
||||
|
||||
printk(KERN_ERR "%s: -- Error, unable to allocate DMA table.\n",
|
||||
hwif->cds->name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ide_mapped_mmio_dma(ide_hwif_t *hwif, unsigned long base, unsigned int ports)
|
||||
{
|
||||
printk(KERN_INFO " %s: MMIO-DMA ", hwif->name);
|
||||
|
||||
hwif->dma_base = base;
|
||||
|
||||
if(hwif->mate)
|
||||
hwif->dma_master = (hwif->channel) ? hwif->mate->dma_base : base;
|
||||
else
|
||||
hwif->dma_master = base;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ide_iomio_dma(ide_hwif_t *hwif, unsigned long base, unsigned int ports)
|
||||
{
|
||||
printk(KERN_INFO " %s: BM-DMA at 0x%04lx-0x%04lx",
|
||||
hwif->name, base, base + ports - 1);
|
||||
|
||||
if (!request_region(base, ports, hwif->name)) {
|
||||
printk(" -- Error, ports in use.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
hwif->dma_base = base;
|
||||
|
||||
if (hwif->cds->extra) {
|
||||
hwif->extra_base = base + (hwif->channel ? 8 : 16);
|
||||
|
||||
if (!hwif->mate || !hwif->mate->extra_ports) {
|
||||
if (!request_region(hwif->extra_base,
|
||||
hwif->cds->extra, hwif->cds->name)) {
|
||||
printk(" -- Error, extra ports in use.\n");
|
||||
release_region(base, ports);
|
||||
return 1;
|
||||
}
|
||||
hwif->extra_ports = hwif->cds->extra;
|
||||
}
|
||||
}
|
||||
|
||||
if(hwif->mate)
|
||||
hwif->dma_master = (hwif->channel) ? hwif->mate->dma_base:base;
|
||||
else
|
||||
hwif->dma_master = base;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ide_dma_iobase(ide_hwif_t *hwif, unsigned long base, unsigned int ports)
|
||||
{
|
||||
if (hwif->mmio)
|
||||
return ide_mapped_mmio_dma(hwif, base,ports);
|
||||
|
||||
return ide_iomio_dma(hwif, base, ports);
|
||||
}
|
||||
|
||||
/*
|
||||
* This can be called for a dynamically installed interface. Don't __init it
|
||||
*/
|
||||
void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_ports)
|
||||
{
|
||||
if (ide_dma_iobase(hwif, dma_base, num_ports))
|
||||
return;
|
||||
|
||||
if (ide_allocate_dma_engine(hwif)) {
|
||||
ide_release_dma(hwif);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(hwif->dma_command))
|
||||
hwif->dma_command = hwif->dma_base;
|
||||
if (!(hwif->dma_vendor1))
|
||||
hwif->dma_vendor1 = (hwif->dma_base + 1);
|
||||
if (!(hwif->dma_status))
|
||||
hwif->dma_status = (hwif->dma_base + 2);
|
||||
if (!(hwif->dma_vendor3))
|
||||
hwif->dma_vendor3 = (hwif->dma_base + 3);
|
||||
if (!(hwif->dma_prdtable))
|
||||
hwif->dma_prdtable = (hwif->dma_base + 4);
|
||||
|
||||
if (!hwif->dma_off_quietly)
|
||||
hwif->dma_off_quietly = &ide_dma_off_quietly;
|
||||
if (!hwif->dma_host_off)
|
||||
hwif->dma_host_off = &ide_dma_host_off;
|
||||
if (!hwif->ide_dma_on)
|
||||
hwif->ide_dma_on = &__ide_dma_on;
|
||||
if (!hwif->dma_host_on)
|
||||
hwif->dma_host_on = &ide_dma_host_on;
|
||||
if (!hwif->ide_dma_check)
|
||||
hwif->ide_dma_check = &__ide_dma_check;
|
||||
if (!hwif->dma_setup)
|
||||
hwif->dma_setup = &ide_dma_setup;
|
||||
if (!hwif->dma_exec_cmd)
|
||||
hwif->dma_exec_cmd = &ide_dma_exec_cmd;
|
||||
if (!hwif->dma_start)
|
||||
hwif->dma_start = &ide_dma_start;
|
||||
if (!hwif->ide_dma_end)
|
||||
hwif->ide_dma_end = &__ide_dma_end;
|
||||
if (!hwif->ide_dma_test_irq)
|
||||
hwif->ide_dma_test_irq = &__ide_dma_test_irq;
|
||||
if (!hwif->ide_dma_timeout)
|
||||
hwif->ide_dma_timeout = &__ide_dma_timeout;
|
||||
if (!hwif->ide_dma_lostirq)
|
||||
hwif->ide_dma_lostirq = &__ide_dma_lostirq;
|
||||
|
||||
if (hwif->chipset != ide_trm290) {
|
||||
u8 dma_stat = hwif->INB(hwif->dma_status);
|
||||
printk(", BIOS settings: %s:%s, %s:%s",
|
||||
hwif->drives[0].name, (dma_stat & 0x20) ? "DMA" : "pio",
|
||||
hwif->drives[1].name, (dma_stat & 0x40) ? "DMA" : "pio");
|
||||
}
|
||||
printk("\n");
|
||||
|
||||
BUG_ON(!hwif->dma_master);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_setup_dma);
|
||||
#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */
|
||||
2206
drivers/ide/ide-floppy.c
Normal file
2206
drivers/ide/ide-floppy.c
Normal file
File diff suppressed because it is too large
Load Diff
32
drivers/ide/ide-generic.c
Normal file
32
drivers/ide/ide-generic.c
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* generic/default IDE host driver
|
||||
*
|
||||
* Copyright (C) 2004 Bartlomiej Zolnierkiewicz
|
||||
* This code was split off from ide.c. See it for original copyrights.
|
||||
*
|
||||
* May be copied or modified under the terms of the GNU General Public License.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
static int __init ide_generic_init(void)
|
||||
{
|
||||
if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET])
|
||||
ide_get_lock(NULL, NULL); /* for atari only */
|
||||
|
||||
(void)ideprobe_init();
|
||||
|
||||
if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET])
|
||||
ide_release_lock(); /* for atari only */
|
||||
|
||||
create_proc_ide_interfaces();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(ide_generic_init);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
1801
drivers/ide/ide-io.c
Normal file
1801
drivers/ide/ide-io.c
Normal file
File diff suppressed because it is too large
Load Diff
1244
drivers/ide/ide-iops.c
Normal file
1244
drivers/ide/ide-iops.c
Normal file
File diff suppressed because it is too large
Load Diff
643
drivers/ide/ide-lib.c
Normal file
643
drivers/ide/ide-lib.c
Normal file
@@ -0,0 +1,643 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <linux/blkpg.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/*
|
||||
* IDE library routines. These are plug in code that most
|
||||
* drivers can use but occasionally may be weird enough
|
||||
* to want to do their own thing with
|
||||
*
|
||||
* Add common non I/O op stuff here. Make sure it has proper
|
||||
* kernel-doc function headers or your patch will be rejected
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* ide_xfer_verbose - return IDE mode names
|
||||
* @xfer_rate: rate to name
|
||||
*
|
||||
* Returns a constant string giving the name of the mode
|
||||
* requested.
|
||||
*/
|
||||
|
||||
char *ide_xfer_verbose (u8 xfer_rate)
|
||||
{
|
||||
switch(xfer_rate) {
|
||||
case XFER_UDMA_7: return("UDMA 7");
|
||||
case XFER_UDMA_6: return("UDMA 6");
|
||||
case XFER_UDMA_5: return("UDMA 5");
|
||||
case XFER_UDMA_4: return("UDMA 4");
|
||||
case XFER_UDMA_3: return("UDMA 3");
|
||||
case XFER_UDMA_2: return("UDMA 2");
|
||||
case XFER_UDMA_1: return("UDMA 1");
|
||||
case XFER_UDMA_0: return("UDMA 0");
|
||||
case XFER_MW_DMA_2: return("MW DMA 2");
|
||||
case XFER_MW_DMA_1: return("MW DMA 1");
|
||||
case XFER_MW_DMA_0: return("MW DMA 0");
|
||||
case XFER_SW_DMA_2: return("SW DMA 2");
|
||||
case XFER_SW_DMA_1: return("SW DMA 1");
|
||||
case XFER_SW_DMA_0: return("SW DMA 0");
|
||||
case XFER_PIO_4: return("PIO 4");
|
||||
case XFER_PIO_3: return("PIO 3");
|
||||
case XFER_PIO_2: return("PIO 2");
|
||||
case XFER_PIO_1: return("PIO 1");
|
||||
case XFER_PIO_0: return("PIO 0");
|
||||
case XFER_PIO_SLOW: return("PIO SLOW");
|
||||
default: return("XFER ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ide_xfer_verbose);
|
||||
|
||||
/**
|
||||
* ide_dma_speed - compute DMA speed
|
||||
* @drive: drive
|
||||
* @mode: modes available
|
||||
*
|
||||
* Checks the drive capabilities and returns the speed to use
|
||||
* for the DMA transfer. Returns 0 if the drive is incapable
|
||||
* of DMA transfers.
|
||||
*/
|
||||
|
||||
u8 ide_dma_speed(ide_drive_t *drive, u8 mode)
|
||||
{
|
||||
struct hd_driveid *id = drive->id;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 ultra_mask, mwdma_mask, swdma_mask;
|
||||
u8 speed = 0;
|
||||
|
||||
if (drive->media != ide_disk && hwif->atapi_dma == 0)
|
||||
return 0;
|
||||
|
||||
/* Capable of UltraDMA modes? */
|
||||
ultra_mask = id->dma_ultra & hwif->ultra_mask;
|
||||
|
||||
if (!(id->field_valid & 4))
|
||||
mode = 0; /* fallback to MW/SW DMA if no UltraDMA */
|
||||
|
||||
switch (mode) {
|
||||
case 4:
|
||||
if (ultra_mask & 0x40) {
|
||||
speed = XFER_UDMA_6;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
if (ultra_mask & 0x20) {
|
||||
speed = XFER_UDMA_5;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
if (ultra_mask & 0x10) {
|
||||
speed = XFER_UDMA_4;
|
||||
break;
|
||||
}
|
||||
if (ultra_mask & 0x08) {
|
||||
speed = XFER_UDMA_3;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
if (ultra_mask & 0x04) {
|
||||
speed = XFER_UDMA_2;
|
||||
break;
|
||||
}
|
||||
if (ultra_mask & 0x02) {
|
||||
speed = XFER_UDMA_1;
|
||||
break;
|
||||
}
|
||||
if (ultra_mask & 0x01) {
|
||||
speed = XFER_UDMA_0;
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
mwdma_mask = id->dma_mword & hwif->mwdma_mask;
|
||||
|
||||
if (mwdma_mask & 0x04) {
|
||||
speed = XFER_MW_DMA_2;
|
||||
break;
|
||||
}
|
||||
if (mwdma_mask & 0x02) {
|
||||
speed = XFER_MW_DMA_1;
|
||||
break;
|
||||
}
|
||||
if (mwdma_mask & 0x01) {
|
||||
speed = XFER_MW_DMA_0;
|
||||
break;
|
||||
}
|
||||
|
||||
swdma_mask = id->dma_1word & hwif->swdma_mask;
|
||||
|
||||
if (swdma_mask & 0x04) {
|
||||
speed = XFER_SW_DMA_2;
|
||||
break;
|
||||
}
|
||||
if (swdma_mask & 0x02) {
|
||||
speed = XFER_SW_DMA_1;
|
||||
break;
|
||||
}
|
||||
if (swdma_mask & 0x01) {
|
||||
speed = XFER_SW_DMA_0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return speed;
|
||||
}
|
||||
EXPORT_SYMBOL(ide_dma_speed);
|
||||
|
||||
|
||||
/**
|
||||
* ide_rate_filter - return best speed for mode
|
||||
* @mode: modes available
|
||||
* @speed: desired speed
|
||||
*
|
||||
* Given the available DMA/UDMA mode this function returns
|
||||
* the best available speed at or below the speed requested.
|
||||
*/
|
||||
|
||||
u8 ide_rate_filter (u8 mode, u8 speed)
|
||||
{
|
||||
#ifdef CONFIG_BLK_DEV_IDEDMA
|
||||
static u8 speed_max[] = {
|
||||
XFER_MW_DMA_2, XFER_UDMA_2, XFER_UDMA_4,
|
||||
XFER_UDMA_5, XFER_UDMA_6
|
||||
};
|
||||
|
||||
// printk("%s: mode 0x%02x, speed 0x%02x\n", __FUNCTION__, mode, speed);
|
||||
|
||||
/* So that we remember to update this if new modes appear */
|
||||
BUG_ON(mode > 4);
|
||||
return min(speed, speed_max[mode]);
|
||||
#else /* !CONFIG_BLK_DEV_IDEDMA */
|
||||
return min(speed, (u8)XFER_PIO_4);
|
||||
#endif /* CONFIG_BLK_DEV_IDEDMA */
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ide_rate_filter);
|
||||
|
||||
int ide_dma_enable (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct hd_driveid *id = drive->id;
|
||||
|
||||
return ((int) ((((id->dma_ultra >> 8) & hwif->ultra_mask) ||
|
||||
((id->dma_mword >> 8) & hwif->mwdma_mask) ||
|
||||
((id->dma_1word >> 8) & hwif->swdma_mask)) ? 1 : 0));
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ide_dma_enable);
|
||||
|
||||
int ide_use_fast_pio(ide_drive_t *drive)
|
||||
{
|
||||
struct hd_driveid *id = drive->id;
|
||||
|
||||
if ((id->capability & 1) && drive->autodma)
|
||||
return 1;
|
||||
|
||||
if ((id->capability & 8) || (id->field_valid & 2))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_use_fast_pio);
|
||||
|
||||
/*
|
||||
* Standard (generic) timings for PIO modes, from ATA2 specification.
|
||||
* These timings are for access to the IDE data port register *only*.
|
||||
* Some drives may specify a mode, while also specifying a different
|
||||
* value for cycle_time (from drive identification data).
|
||||
*/
|
||||
const ide_pio_timings_t ide_pio_timings[6] = {
|
||||
{ 70, 165, 600 }, /* PIO Mode 0 */
|
||||
{ 50, 125, 383 }, /* PIO Mode 1 */
|
||||
{ 30, 100, 240 }, /* PIO Mode 2 */
|
||||
{ 30, 80, 180 }, /* PIO Mode 3 with IORDY */
|
||||
{ 25, 70, 120 }, /* PIO Mode 4 with IORDY */
|
||||
{ 20, 50, 100 } /* PIO Mode 5 with IORDY (nonstandard) */
|
||||
};
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_pio_timings);
|
||||
|
||||
/*
|
||||
* Shared data/functions for determining best PIO mode for an IDE drive.
|
||||
* Most of this stuff originally lived in cmd640.c, and changes to the
|
||||
* ide_pio_blacklist[] table should be made with EXTREME CAUTION to avoid
|
||||
* breaking the fragile cmd640.c support.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Black list. Some drives incorrectly report their maximal PIO mode,
|
||||
* at least in respect to CMD640. Here we keep info on some known drives.
|
||||
*/
|
||||
static struct ide_pio_info {
|
||||
const char *name;
|
||||
int pio;
|
||||
} ide_pio_blacklist [] = {
|
||||
/* { "Conner Peripherals 1275MB - CFS1275A", 4 }, */
|
||||
{ "Conner Peripherals 540MB - CFS540A", 3 },
|
||||
|
||||
{ "WDC AC2700", 3 },
|
||||
{ "WDC AC2540", 3 },
|
||||
{ "WDC AC2420", 3 },
|
||||
{ "WDC AC2340", 3 },
|
||||
{ "WDC AC2250", 0 },
|
||||
{ "WDC AC2200", 0 },
|
||||
{ "WDC AC21200", 4 },
|
||||
{ "WDC AC2120", 0 },
|
||||
{ "WDC AC2850", 3 },
|
||||
{ "WDC AC1270", 3 },
|
||||
{ "WDC AC1170", 1 },
|
||||
{ "WDC AC1210", 1 },
|
||||
{ "WDC AC280", 0 },
|
||||
/* { "WDC AC21000", 4 }, */
|
||||
{ "WDC AC31000", 3 },
|
||||
{ "WDC AC31200", 3 },
|
||||
/* { "WDC AC31600", 4 }, */
|
||||
|
||||
{ "Maxtor 7131 AT", 1 },
|
||||
{ "Maxtor 7171 AT", 1 },
|
||||
{ "Maxtor 7213 AT", 1 },
|
||||
{ "Maxtor 7245 AT", 1 },
|
||||
{ "Maxtor 7345 AT", 1 },
|
||||
{ "Maxtor 7546 AT", 3 },
|
||||
{ "Maxtor 7540 AV", 3 },
|
||||
|
||||
{ "SAMSUNG SHD-3121A", 1 },
|
||||
{ "SAMSUNG SHD-3122A", 1 },
|
||||
{ "SAMSUNG SHD-3172A", 1 },
|
||||
|
||||
/* { "ST51080A", 4 },
|
||||
* { "ST51270A", 4 },
|
||||
* { "ST31220A", 4 },
|
||||
* { "ST31640A", 4 },
|
||||
* { "ST32140A", 4 },
|
||||
* { "ST3780A", 4 },
|
||||
*/
|
||||
{ "ST5660A", 3 },
|
||||
{ "ST3660A", 3 },
|
||||
{ "ST3630A", 3 },
|
||||
{ "ST3655A", 3 },
|
||||
{ "ST3391A", 3 },
|
||||
{ "ST3390A", 1 },
|
||||
{ "ST3600A", 1 },
|
||||
{ "ST3290A", 0 },
|
||||
{ "ST3144A", 0 },
|
||||
{ "ST3491A", 1 }, /* reports 3, should be 1 or 2 (depending on */
|
||||
/* drive) according to Seagates FIND-ATA program */
|
||||
|
||||
{ "QUANTUM ELS127A", 0 },
|
||||
{ "QUANTUM ELS170A", 0 },
|
||||
{ "QUANTUM LPS240A", 0 },
|
||||
{ "QUANTUM LPS210A", 3 },
|
||||
{ "QUANTUM LPS270A", 3 },
|
||||
{ "QUANTUM LPS365A", 3 },
|
||||
{ "QUANTUM LPS540A", 3 },
|
||||
{ "QUANTUM LIGHTNING 540A", 3 },
|
||||
{ "QUANTUM LIGHTNING 730A", 3 },
|
||||
|
||||
{ "QUANTUM FIREBALL_540", 3 }, /* Older Quantum Fireballs don't work */
|
||||
{ "QUANTUM FIREBALL_640", 3 },
|
||||
{ "QUANTUM FIREBALL_1080", 3 },
|
||||
{ "QUANTUM FIREBALL_1280", 3 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/**
|
||||
* ide_scan_pio_blacklist - check for a blacklisted drive
|
||||
* @model: Drive model string
|
||||
*
|
||||
* This routine searches the ide_pio_blacklist for an entry
|
||||
* matching the start/whole of the supplied model name.
|
||||
*
|
||||
* Returns -1 if no match found.
|
||||
* Otherwise returns the recommended PIO mode from ide_pio_blacklist[].
|
||||
*/
|
||||
|
||||
static int ide_scan_pio_blacklist (char *model)
|
||||
{
|
||||
struct ide_pio_info *p;
|
||||
|
||||
for (p = ide_pio_blacklist; p->name != NULL; p++) {
|
||||
if (strncmp(p->name, model, strlen(p->name)) == 0)
|
||||
return p->pio;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_get_best_pio_mode - get PIO mode from drive
|
||||
* @drive: drive to consider
|
||||
* @mode_wanted: preferred mode
|
||||
* @max_mode: highest allowed mode
|
||||
* @d: PIO data
|
||||
*
|
||||
* This routine returns the recommended PIO settings for a given drive,
|
||||
* based on the drive->id information and the ide_pio_blacklist[].
|
||||
*
|
||||
* Drive PIO mode is auto-selected if 255 is passed as mode_wanted.
|
||||
* This is used by most chipset support modules when "auto-tuning".
|
||||
*/
|
||||
|
||||
u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 max_mode, ide_pio_data_t *d)
|
||||
{
|
||||
int pio_mode;
|
||||
int cycle_time = 0;
|
||||
int use_iordy = 0;
|
||||
struct hd_driveid* id = drive->id;
|
||||
int overridden = 0;
|
||||
|
||||
if (mode_wanted != 255) {
|
||||
pio_mode = mode_wanted;
|
||||
use_iordy = (pio_mode > 2);
|
||||
} else if (!drive->id) {
|
||||
pio_mode = 0;
|
||||
} else if ((pio_mode = ide_scan_pio_blacklist(id->model)) != -1) {
|
||||
overridden = 1;
|
||||
use_iordy = (pio_mode > 2);
|
||||
} else {
|
||||
pio_mode = id->tPIO;
|
||||
if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */
|
||||
pio_mode = 2;
|
||||
overridden = 1;
|
||||
}
|
||||
if (id->field_valid & 2) { /* drive implements ATA2? */
|
||||
if (id->capability & 8) { /* drive supports use_iordy? */
|
||||
use_iordy = 1;
|
||||
cycle_time = id->eide_pio_iordy;
|
||||
if (id->eide_pio_modes & 7) {
|
||||
overridden = 0;
|
||||
if (id->eide_pio_modes & 4)
|
||||
pio_mode = 5;
|
||||
else if (id->eide_pio_modes & 2)
|
||||
pio_mode = 4;
|
||||
else
|
||||
pio_mode = 3;
|
||||
}
|
||||
} else {
|
||||
cycle_time = id->eide_pio;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Conservative "downgrade" for all pre-ATA2 drives
|
||||
*/
|
||||
if (pio_mode && pio_mode < 4) {
|
||||
pio_mode--;
|
||||
overridden = 1;
|
||||
if (cycle_time && cycle_time < ide_pio_timings[pio_mode].cycle_time)
|
||||
cycle_time = 0; /* use standard timing */
|
||||
}
|
||||
}
|
||||
if (pio_mode > max_mode) {
|
||||
pio_mode = max_mode;
|
||||
cycle_time = 0;
|
||||
}
|
||||
if (d) {
|
||||
d->pio_mode = pio_mode;
|
||||
d->cycle_time = cycle_time ? cycle_time : ide_pio_timings[pio_mode].cycle_time;
|
||||
d->use_iordy = use_iordy;
|
||||
d->overridden = overridden;
|
||||
}
|
||||
return pio_mode;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_get_best_pio_mode);
|
||||
|
||||
/**
|
||||
* ide_toggle_bounce - handle bounce buffering
|
||||
* @drive: drive to update
|
||||
* @on: on/off boolean
|
||||
*
|
||||
* Enable or disable bounce buffering for the device. Drives move
|
||||
* between PIO and DMA and that changes the rules we need.
|
||||
*/
|
||||
|
||||
void ide_toggle_bounce(ide_drive_t *drive, int on)
|
||||
{
|
||||
u64 addr = BLK_BOUNCE_HIGH; /* dma64_addr_t */
|
||||
|
||||
if (!PCI_DMA_BUS_IS_PHYS) {
|
||||
addr = BLK_BOUNCE_ANY;
|
||||
} else if (on && drive->media == ide_disk) {
|
||||
if (HWIF(drive)->pci_dev)
|
||||
addr = HWIF(drive)->pci_dev->dma_mask;
|
||||
}
|
||||
|
||||
if (drive->queue)
|
||||
blk_queue_bounce_limit(drive->queue, addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_set_xfer_rate - set transfer rate
|
||||
* @drive: drive to set
|
||||
* @speed: speed to attempt to set
|
||||
*
|
||||
* General helper for setting the speed of an IDE device. This
|
||||
* function knows about user enforced limits from the configuration
|
||||
* which speedproc() does not. High level drivers should never
|
||||
* invoke speedproc() directly.
|
||||
*/
|
||||
|
||||
int ide_set_xfer_rate(ide_drive_t *drive, u8 rate)
|
||||
{
|
||||
#ifndef CONFIG_BLK_DEV_IDEDMA
|
||||
rate = min(rate, (u8) XFER_PIO_4);
|
||||
#endif
|
||||
if(HWIF(drive)->speedproc)
|
||||
return HWIF(drive)->speedproc(drive, rate);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ide_dump_opcode(ide_drive_t *drive)
|
||||
{
|
||||
struct request *rq;
|
||||
u8 opcode = 0;
|
||||
int found = 0;
|
||||
|
||||
spin_lock(&ide_lock);
|
||||
rq = NULL;
|
||||
if (HWGROUP(drive))
|
||||
rq = HWGROUP(drive)->rq;
|
||||
spin_unlock(&ide_lock);
|
||||
if (!rq)
|
||||
return;
|
||||
if (rq->cmd_type == REQ_TYPE_ATA_CMD ||
|
||||
rq->cmd_type == REQ_TYPE_ATA_TASK) {
|
||||
char *args = rq->buffer;
|
||||
if (args) {
|
||||
opcode = args[0];
|
||||
found = 1;
|
||||
}
|
||||
} else if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
|
||||
ide_task_t *args = rq->special;
|
||||
if (args) {
|
||||
task_struct_t *tf = (task_struct_t *) args->tfRegister;
|
||||
opcode = tf->command;
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
|
||||
printk("ide: failed opcode was: ");
|
||||
if (!found)
|
||||
printk("unknown\n");
|
||||
else
|
||||
printk("0x%02x\n", opcode);
|
||||
}
|
||||
|
||||
static u8 ide_dump_ata_status(ide_drive_t *drive, const char *msg, u8 stat)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned long flags;
|
||||
u8 err = 0;
|
||||
|
||||
local_irq_save(flags);
|
||||
printk("%s: %s: status=0x%02x { ", drive->name, msg, stat);
|
||||
if (stat & BUSY_STAT)
|
||||
printk("Busy ");
|
||||
else {
|
||||
if (stat & READY_STAT) printk("DriveReady ");
|
||||
if (stat & WRERR_STAT) printk("DeviceFault ");
|
||||
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 & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
|
||||
err = hwif->INB(IDE_ERROR_REG);
|
||||
printk("%s: %s: error=0x%02x { ", drive->name, msg, err);
|
||||
if (err & ABRT_ERR) printk("DriveStatusError ");
|
||||
if (err & ICRC_ERR)
|
||||
printk((err & ABRT_ERR) ? "BadCRC " : "BadSector ");
|
||||
if (err & ECC_ERR) printk("UncorrectableError ");
|
||||
if (err & ID_ERR) printk("SectorIdNotFound ");
|
||||
if (err & TRK0_ERR) printk("TrackZeroNotFound ");
|
||||
if (err & MARK_ERR) printk("AddrMarkNotFound ");
|
||||
printk("}");
|
||||
if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR ||
|
||||
(err & (ECC_ERR|ID_ERR|MARK_ERR))) {
|
||||
if (drive->addressing == 1) {
|
||||
__u64 sectors = 0;
|
||||
u32 low = 0, high = 0;
|
||||
low = ide_read_24(drive);
|
||||
hwif->OUTB(drive->ctl|0x80, IDE_CONTROL_REG);
|
||||
high = ide_read_24(drive);
|
||||
sectors = ((__u64)high << 24) | low;
|
||||
printk(", LBAsect=%llu, high=%d, low=%d",
|
||||
(unsigned long long) sectors,
|
||||
high, low);
|
||||
} else {
|
||||
u8 cur = hwif->INB(IDE_SELECT_REG);
|
||||
if (cur & 0x40) { /* using LBA? */
|
||||
printk(", LBAsect=%ld", (unsigned long)
|
||||
((cur&0xf)<<24)
|
||||
|(hwif->INB(IDE_HCYL_REG)<<16)
|
||||
|(hwif->INB(IDE_LCYL_REG)<<8)
|
||||
| hwif->INB(IDE_SECTOR_REG));
|
||||
} else {
|
||||
printk(", CHS=%d/%d/%d",
|
||||
(hwif->INB(IDE_HCYL_REG)<<8) +
|
||||
hwif->INB(IDE_LCYL_REG),
|
||||
cur & 0xf,
|
||||
hwif->INB(IDE_SECTOR_REG));
|
||||
}
|
||||
}
|
||||
if (HWGROUP(drive) && HWGROUP(drive)->rq)
|
||||
printk(", sector=%llu",
|
||||
(unsigned long long)HWGROUP(drive)->rq->sector);
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
ide_dump_opcode(drive);
|
||||
local_irq_restore(flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_dump_atapi_status - print human readable atapi status
|
||||
* @drive: drive that status applies to
|
||||
* @msg: text message to print
|
||||
* @stat: status byte to decode
|
||||
*
|
||||
* Error reporting, in human readable form (luxurious, but a memory hog).
|
||||
*/
|
||||
|
||||
static u8 ide_dump_atapi_status(ide_drive_t *drive, const char *msg, u8 stat)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
atapi_status_t status;
|
||||
atapi_error_t error;
|
||||
|
||||
status.all = stat;
|
||||
error.all = 0;
|
||||
local_irq_save(flags);
|
||||
printk("%s: %s: status=0x%02x { ", drive->name, msg, stat);
|
||||
if (status.b.bsy)
|
||||
printk("Busy ");
|
||||
else {
|
||||
if (status.b.drdy) printk("DriveReady ");
|
||||
if (status.b.df) printk("DeviceFault ");
|
||||
if (status.b.dsc) printk("SeekComplete ");
|
||||
if (status.b.drq) printk("DataRequest ");
|
||||
if (status.b.corr) printk("CorrectedError ");
|
||||
if (status.b.idx) printk("Index ");
|
||||
if (status.b.check) printk("Error ");
|
||||
}
|
||||
printk("}\n");
|
||||
if (status.b.check && !status.b.bsy) {
|
||||
error.all = HWIF(drive)->INB(IDE_ERROR_REG);
|
||||
printk("%s: %s: error=0x%02x { ", drive->name, msg, error.all);
|
||||
if (error.b.ili) printk("IllegalLengthIndication ");
|
||||
if (error.b.eom) printk("EndOfMedia ");
|
||||
if (error.b.abrt) printk("AbortedCommand ");
|
||||
if (error.b.mcr) printk("MediaChangeRequested ");
|
||||
if (error.b.sense_key) printk("LastFailedSense=0x%02x ",
|
||||
error.b.sense_key);
|
||||
printk("}\n");
|
||||
}
|
||||
ide_dump_opcode(drive);
|
||||
local_irq_restore(flags);
|
||||
return error.all;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_dump_status - translate ATA/ATAPI error
|
||||
* @drive: drive the error occured on
|
||||
* @msg: information string
|
||||
* @stat: status byte
|
||||
*
|
||||
* Error reporting, in human readable form (luxurious, but a memory hog).
|
||||
* Combines the drive name, message and status byte to provide a
|
||||
* user understandable explanation of the device error.
|
||||
*/
|
||||
|
||||
u8 ide_dump_status(ide_drive_t *drive, const char *msg, u8 stat)
|
||||
{
|
||||
if (drive->media == ide_disk)
|
||||
return ide_dump_ata_status(drive, msg, stat);
|
||||
return ide_dump_atapi_status(drive, msg, stat);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ide_dump_status);
|
||||
80
drivers/ide/ide-pnp.c
Normal file
80
drivers/ide/ide-pnp.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* linux/drivers/ide/ide-pnp.c
|
||||
*
|
||||
* This file provides autodetection for ISA PnP IDE interfaces.
|
||||
* It was tested with "ESS ES1868 Plug and Play AudioDrive" IDE interface.
|
||||
*
|
||||
* Copyright (C) 2000 Andrey Panin <pazke@donpac.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* (for example /usr/src/linux/COPYING); if not, write to the Free
|
||||
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/pnp.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
/* Add your devices here :)) */
|
||||
static struct pnp_device_id idepnp_devices[] = {
|
||||
/* Generic ESDI/IDE/ATA compatible hard disk controller */
|
||||
{.id = "PNP0600", .driver_data = 0},
|
||||
{.id = ""}
|
||||
};
|
||||
|
||||
static int idepnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id)
|
||||
{
|
||||
hw_regs_t hw;
|
||||
ide_hwif_t *hwif;
|
||||
int index;
|
||||
|
||||
if (!(pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) && pnp_irq_valid(dev, 0)))
|
||||
return -1;
|
||||
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
ide_std_init_ports(&hw, pnp_port_start(dev, 0),
|
||||
pnp_port_start(dev, 1));
|
||||
hw.irq = pnp_irq(dev, 0);
|
||||
hw.dma = NO_DMA;
|
||||
|
||||
index = ide_register_hw(&hw, &hwif);
|
||||
|
||||
if (index != -1) {
|
||||
printk(KERN_INFO "ide%d: generic PnP IDE interface\n", index);
|
||||
pnp_set_drvdata(dev,hwif);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void idepnp_remove(struct pnp_dev * dev)
|
||||
{
|
||||
ide_hwif_t *hwif = pnp_get_drvdata(dev);
|
||||
if (hwif) {
|
||||
ide_unregister(hwif->index);
|
||||
} else
|
||||
printk(KERN_ERR "idepnp: Unable to remove device, please report.\n");
|
||||
}
|
||||
|
||||
static struct pnp_driver idepnp_driver = {
|
||||
.name = "ide",
|
||||
.id_table = idepnp_devices,
|
||||
.probe = idepnp_probe,
|
||||
.remove = idepnp_remove,
|
||||
};
|
||||
|
||||
void __init pnpide_init(void)
|
||||
{
|
||||
pnp_register_driver(&idepnp_driver);
|
||||
}
|
||||
|
||||
void __exit pnpide_exit(void)
|
||||
{
|
||||
pnp_unregister_driver(&idepnp_driver);
|
||||
}
|
||||
1433
drivers/ide/ide-probe.c
Normal file
1433
drivers/ide/ide-probe.c
Normal file
File diff suppressed because it is too large
Load Diff
575
drivers/ide/ide-proc.c
Normal file
575
drivers/ide/ide-proc.c
Normal file
@@ -0,0 +1,575 @@
|
||||
/*
|
||||
* linux/drivers/ide/ide-proc.c Version 1.05 Mar 05, 2003
|
||||
*
|
||||
* Copyright (C) 1997-1998 Mark Lord
|
||||
* Copyright (C) 2003 Red Hat <alan@redhat.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the /proc/ide/ filesystem implementation.
|
||||
*
|
||||
* Drive/Driver settings can be retrieved by reading the drive's
|
||||
* "settings" files. e.g. "cat /proc/ide0/hda/settings"
|
||||
* To write a new value "val" into a specific setting "name", use:
|
||||
* echo "name:val" >/proc/ide/ide0/hda/settings
|
||||
*
|
||||
* Also useful, "cat /proc/ide0/hda/[identify, smart_values,
|
||||
* smart_thresholds, capabilities]" will issue an IDENTIFY /
|
||||
* PACKET_IDENTIFY / SMART_READ_VALUES / SMART_READ_THRESHOLDS /
|
||||
* SENSE CAPABILITIES command to /dev/hda, and then dump out the
|
||||
* returned data as 256 16-bit words. The "hdparm" utility will
|
||||
* be updated someday soon to use this mechanism.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
static int proc_ide_read_imodel
|
||||
(char *page, char **start, off_t off, int count, int *eof, void *data)
|
||||
{
|
||||
ide_hwif_t *hwif = (ide_hwif_t *) data;
|
||||
int len;
|
||||
const char *name;
|
||||
|
||||
/*
|
||||
* Neither ide_unknown nor ide_forced should be set at this point.
|
||||
*/
|
||||
switch (hwif->chipset) {
|
||||
case ide_generic: name = "generic"; break;
|
||||
case ide_pci: name = "pci"; break;
|
||||
case ide_cmd640: name = "cmd640"; break;
|
||||
case ide_dtc2278: name = "dtc2278"; break;
|
||||
case ide_ali14xx: name = "ali14xx"; break;
|
||||
case ide_qd65xx: name = "qd65xx"; break;
|
||||
case ide_umc8672: name = "umc8672"; break;
|
||||
case ide_ht6560b: name = "ht6560b"; break;
|
||||
case ide_rz1000: name = "rz1000"; break;
|
||||
case ide_trm290: name = "trm290"; break;
|
||||
case ide_cmd646: name = "cmd646"; break;
|
||||
case ide_cy82c693: name = "cy82c693"; break;
|
||||
case ide_4drives: name = "4drives"; break;
|
||||
case ide_pmac: name = "mac-io"; break;
|
||||
case ide_au1xxx: name = "au1xxx"; break;
|
||||
default: name = "(unknown)"; break;
|
||||
}
|
||||
len = sprintf(page, "%s\n", name);
|
||||
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
|
||||
}
|
||||
|
||||
static int proc_ide_read_mate
|
||||
(char *page, char **start, off_t off, int count, int *eof, void *data)
|
||||
{
|
||||
ide_hwif_t *hwif = (ide_hwif_t *) data;
|
||||
int len;
|
||||
|
||||
if (hwif && hwif->mate && hwif->mate->present)
|
||||
len = sprintf(page, "%s\n", hwif->mate->name);
|
||||
else
|
||||
len = sprintf(page, "(none)\n");
|
||||
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
|
||||
}
|
||||
|
||||
static int proc_ide_read_channel
|
||||
(char *page, char **start, off_t off, int count, int *eof, void *data)
|
||||
{
|
||||
ide_hwif_t *hwif = (ide_hwif_t *) data;
|
||||
int len;
|
||||
|
||||
page[0] = hwif->channel ? '1' : '0';
|
||||
page[1] = '\n';
|
||||
len = 2;
|
||||
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
|
||||
}
|
||||
|
||||
static int proc_ide_read_identify
|
||||
(char *page, char **start, off_t off, int count, int *eof, void *data)
|
||||
{
|
||||
ide_drive_t *drive = (ide_drive_t *)data;
|
||||
int len = 0, i = 0;
|
||||
int err = 0;
|
||||
|
||||
len = sprintf(page, "\n");
|
||||
|
||||
if (drive) {
|
||||
unsigned short *val = (unsigned short *) page;
|
||||
|
||||
err = taskfile_lib_get_identify(drive, page);
|
||||
if (!err) {
|
||||
char *out = ((char *)page) + (SECTOR_WORDS * 4);
|
||||
page = out;
|
||||
do {
|
||||
out += sprintf(out, "%04x%c",
|
||||
le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
|
||||
val += 1;
|
||||
} while (i < (SECTOR_WORDS * 2));
|
||||
len = out - page;
|
||||
}
|
||||
}
|
||||
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
|
||||
}
|
||||
|
||||
static void proc_ide_settings_warn(void)
|
||||
{
|
||||
static int warned = 0;
|
||||
|
||||
if (warned)
|
||||
return;
|
||||
|
||||
printk(KERN_WARNING "Warning: /proc/ide/hd?/settings interface is "
|
||||
"obsolete, and will be removed soon!\n");
|
||||
warned = 1;
|
||||
}
|
||||
|
||||
static int proc_ide_read_settings
|
||||
(char *page, char **start, off_t off, int count, int *eof, void *data)
|
||||
{
|
||||
ide_drive_t *drive = (ide_drive_t *) data;
|
||||
ide_settings_t *setting = (ide_settings_t *) drive->settings;
|
||||
char *out = page;
|
||||
int len, rc, mul_factor, div_factor;
|
||||
|
||||
proc_ide_settings_warn();
|
||||
|
||||
down(&ide_setting_sem);
|
||||
out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
|
||||
out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
|
||||
while(setting) {
|
||||
mul_factor = setting->mul_factor;
|
||||
div_factor = setting->div_factor;
|
||||
out += sprintf(out, "%-24s", setting->name);
|
||||
if ((rc = ide_read_setting(drive, setting)) >= 0)
|
||||
out += sprintf(out, "%-16d", rc * mul_factor / div_factor);
|
||||
else
|
||||
out += sprintf(out, "%-16s", "write-only");
|
||||
out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
|
||||
if (setting->rw & SETTING_READ)
|
||||
out += sprintf(out, "r");
|
||||
if (setting->rw & SETTING_WRITE)
|
||||
out += sprintf(out, "w");
|
||||
out += sprintf(out, "\n");
|
||||
setting = setting->next;
|
||||
}
|
||||
len = out - page;
|
||||
up(&ide_setting_sem);
|
||||
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
|
||||
}
|
||||
|
||||
#define MAX_LEN 30
|
||||
|
||||
static int proc_ide_write_settings(struct file *file, const char __user *buffer,
|
||||
unsigned long count, void *data)
|
||||
{
|
||||
ide_drive_t *drive = (ide_drive_t *) data;
|
||||
char name[MAX_LEN + 1];
|
||||
int for_real = 0;
|
||||
unsigned long n;
|
||||
ide_settings_t *setting;
|
||||
char *buf, *s;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
proc_ide_settings_warn();
|
||||
|
||||
if (count >= PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
s = buf = (char *)__get_free_page(GFP_USER);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(buf, buffer, count)) {
|
||||
free_page((unsigned long)buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
buf[count] = '\0';
|
||||
|
||||
/*
|
||||
* Skip over leading whitespace
|
||||
*/
|
||||
while (count && isspace(*s)) {
|
||||
--count;
|
||||
++s;
|
||||
}
|
||||
/*
|
||||
* Do one full pass to verify all parameters,
|
||||
* then do another to actually write the new settings.
|
||||
*/
|
||||
do {
|
||||
char *p = s;
|
||||
n = count;
|
||||
while (n > 0) {
|
||||
unsigned val;
|
||||
char *q = p;
|
||||
|
||||
while (n > 0 && *p != ':') {
|
||||
--n;
|
||||
p++;
|
||||
}
|
||||
if (*p != ':')
|
||||
goto parse_error;
|
||||
if (p - q > MAX_LEN)
|
||||
goto parse_error;
|
||||
memcpy(name, q, p - q);
|
||||
name[p - q] = 0;
|
||||
|
||||
if (n > 0) {
|
||||
--n;
|
||||
p++;
|
||||
} else
|
||||
goto parse_error;
|
||||
|
||||
val = simple_strtoul(p, &q, 10);
|
||||
n -= q - p;
|
||||
p = q;
|
||||
if (n > 0 && !isspace(*p))
|
||||
goto parse_error;
|
||||
while (n > 0 && isspace(*p)) {
|
||||
--n;
|
||||
++p;
|
||||
}
|
||||
|
||||
down(&ide_setting_sem);
|
||||
setting = ide_find_setting_by_name(drive, name);
|
||||
if (!setting)
|
||||
{
|
||||
up(&ide_setting_sem);
|
||||
goto parse_error;
|
||||
}
|
||||
if (for_real)
|
||||
ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor);
|
||||
up(&ide_setting_sem);
|
||||
}
|
||||
} while (!for_real++);
|
||||
free_page((unsigned long)buf);
|
||||
return count;
|
||||
parse_error:
|
||||
free_page((unsigned long)buf);
|
||||
printk("proc_ide_write_settings(): parse error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int proc_ide_read_capacity
|
||||
(char *page, char **start, off_t off, int count, int *eof, void *data)
|
||||
{
|
||||
int len = sprintf(page,"%llu\n", (long long)0x7fffffff);
|
||||
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(proc_ide_read_capacity);
|
||||
|
||||
int proc_ide_read_geometry
|
||||
(char *page, char **start, off_t off, int count, int *eof, void *data)
|
||||
{
|
||||
ide_drive_t *drive = (ide_drive_t *) data;
|
||||
char *out = page;
|
||||
int len;
|
||||
|
||||
out += sprintf(out,"physical %d/%d/%d\n",
|
||||
drive->cyl, drive->head, drive->sect);
|
||||
out += sprintf(out,"logical %d/%d/%d\n",
|
||||
drive->bios_cyl, drive->bios_head, drive->bios_sect);
|
||||
|
||||
len = out - page;
|
||||
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(proc_ide_read_geometry);
|
||||
|
||||
static int proc_ide_read_dmodel
|
||||
(char *page, char **start, off_t off, int count, int *eof, void *data)
|
||||
{
|
||||
ide_drive_t *drive = (ide_drive_t *) data;
|
||||
struct hd_driveid *id = drive->id;
|
||||
int len;
|
||||
|
||||
len = sprintf(page, "%.40s\n",
|
||||
(id && id->model[0]) ? (char *)id->model : "(none)");
|
||||
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
|
||||
}
|
||||
|
||||
static int proc_ide_read_driver
|
||||
(char *page, char **start, off_t off, int count, int *eof, void *data)
|
||||
{
|
||||
ide_drive_t *drive = (ide_drive_t *) data;
|
||||
struct device *dev = &drive->gendev;
|
||||
ide_driver_t *ide_drv;
|
||||
int len;
|
||||
|
||||
down_read(&dev->bus->subsys.rwsem);
|
||||
if (dev->driver) {
|
||||
ide_drv = container_of(dev->driver, ide_driver_t, gen_driver);
|
||||
len = sprintf(page, "%s version %s\n",
|
||||
dev->driver->name, ide_drv->version);
|
||||
} else
|
||||
len = sprintf(page, "ide-default version 0.9.newide\n");
|
||||
up_read(&dev->bus->subsys.rwsem);
|
||||
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
|
||||
}
|
||||
|
||||
static int ide_replace_subdriver(ide_drive_t *drive, const char *driver)
|
||||
{
|
||||
struct device *dev = &drive->gendev;
|
||||
int ret = 1;
|
||||
int err;
|
||||
|
||||
down_write(&dev->bus->subsys.rwsem);
|
||||
device_release_driver(dev);
|
||||
/* FIXME: device can still be in use by previous driver */
|
||||
strlcpy(drive->driver_req, driver, sizeof(drive->driver_req));
|
||||
err = device_attach(dev);
|
||||
if (err < 0)
|
||||
printk(KERN_WARNING "IDE: %s: device_attach error: %d\n",
|
||||
__FUNCTION__, err);
|
||||
drive->driver_req[0] = 0;
|
||||
if (dev->driver == NULL) {
|
||||
err = device_attach(dev);
|
||||
if (err < 0)
|
||||
printk(KERN_WARNING
|
||||
"IDE: %s: device_attach(2) error: %d\n",
|
||||
__FUNCTION__, err);
|
||||
}
|
||||
if (dev->driver && !strcmp(dev->driver->name, driver))
|
||||
ret = 0;
|
||||
up_write(&dev->bus->subsys.rwsem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int proc_ide_write_driver
|
||||
(struct file *file, const char __user *buffer, unsigned long count, void *data)
|
||||
{
|
||||
ide_drive_t *drive = (ide_drive_t *) data;
|
||||
char name[32];
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
if (count > 31)
|
||||
count = 31;
|
||||
if (copy_from_user(name, buffer, count))
|
||||
return -EFAULT;
|
||||
name[count] = '\0';
|
||||
if (ide_replace_subdriver(drive, name))
|
||||
return -EINVAL;
|
||||
return count;
|
||||
}
|
||||
|
||||
static int proc_ide_read_media
|
||||
(char *page, char **start, off_t off, int count, int *eof, void *data)
|
||||
{
|
||||
ide_drive_t *drive = (ide_drive_t *) data;
|
||||
const char *media;
|
||||
int len;
|
||||
|
||||
switch (drive->media) {
|
||||
case ide_disk: media = "disk\n";
|
||||
break;
|
||||
case ide_cdrom: media = "cdrom\n";
|
||||
break;
|
||||
case ide_tape: media = "tape\n";
|
||||
break;
|
||||
case ide_floppy:media = "floppy\n";
|
||||
break;
|
||||
case ide_optical:media = "optical\n";
|
||||
break;
|
||||
default: media = "UNKNOWN\n";
|
||||
break;
|
||||
}
|
||||
strcpy(page,media);
|
||||
len = strlen(media);
|
||||
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
|
||||
}
|
||||
|
||||
static ide_proc_entry_t generic_drive_entries[] = {
|
||||
{ "driver", S_IFREG|S_IRUGO, proc_ide_read_driver, proc_ide_write_driver },
|
||||
{ "identify", S_IFREG|S_IRUSR, proc_ide_read_identify, NULL },
|
||||
{ "media", S_IFREG|S_IRUGO, proc_ide_read_media, NULL },
|
||||
{ "model", S_IFREG|S_IRUGO, proc_ide_read_dmodel, NULL },
|
||||
{ "settings", S_IFREG|S_IRUSR|S_IWUSR,proc_ide_read_settings, proc_ide_write_settings },
|
||||
{ NULL, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
|
||||
{
|
||||
struct proc_dir_entry *ent;
|
||||
|
||||
if (!dir || !p)
|
||||
return;
|
||||
while (p->name != NULL) {
|
||||
ent = create_proc_entry(p->name, p->mode, dir);
|
||||
if (!ent) return;
|
||||
ent->data = data;
|
||||
ent->read_proc = p->read_proc;
|
||||
ent->write_proc = p->write_proc;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
|
||||
{
|
||||
if (!dir || !p)
|
||||
return;
|
||||
while (p->name != NULL) {
|
||||
remove_proc_entry(p->name, dir);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
static void create_proc_ide_drives(ide_hwif_t *hwif)
|
||||
{
|
||||
int d;
|
||||
struct proc_dir_entry *ent;
|
||||
struct proc_dir_entry *parent = hwif->proc;
|
||||
char name[64];
|
||||
|
||||
for (d = 0; d < MAX_DRIVES; d++) {
|
||||
ide_drive_t *drive = &hwif->drives[d];
|
||||
|
||||
if (!drive->present)
|
||||
continue;
|
||||
if (drive->proc)
|
||||
continue;
|
||||
|
||||
drive->proc = proc_mkdir(drive->name, parent);
|
||||
if (drive->proc)
|
||||
ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
|
||||
sprintf(name,"ide%d/%s", (drive->name[2]-'a')/2, drive->name);
|
||||
ent = proc_symlink(drive->name, proc_ide_root, name);
|
||||
if (!ent) return;
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_proc_ide_device(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
if (drive->proc) {
|
||||
ide_remove_proc_entries(drive->proc, generic_drive_entries);
|
||||
remove_proc_entry(drive->name, proc_ide_root);
|
||||
remove_proc_entry(drive->name, hwif->proc);
|
||||
drive->proc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_proc_ide_drives(ide_hwif_t *hwif)
|
||||
{
|
||||
int d;
|
||||
|
||||
for (d = 0; d < MAX_DRIVES; d++) {
|
||||
ide_drive_t *drive = &hwif->drives[d];
|
||||
if (drive->proc)
|
||||
destroy_proc_ide_device(hwif, drive);
|
||||
}
|
||||
}
|
||||
|
||||
static ide_proc_entry_t hwif_entries[] = {
|
||||
{ "channel", S_IFREG|S_IRUGO, proc_ide_read_channel, NULL },
|
||||
{ "mate", S_IFREG|S_IRUGO, proc_ide_read_mate, NULL },
|
||||
{ "model", S_IFREG|S_IRUGO, proc_ide_read_imodel, NULL },
|
||||
{ NULL, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
void create_proc_ide_interfaces(void)
|
||||
{
|
||||
int h;
|
||||
|
||||
for (h = 0; h < MAX_HWIFS; h++) {
|
||||
ide_hwif_t *hwif = &ide_hwifs[h];
|
||||
|
||||
if (!hwif->present)
|
||||
continue;
|
||||
if (!hwif->proc) {
|
||||
hwif->proc = proc_mkdir(hwif->name, proc_ide_root);
|
||||
if (!hwif->proc)
|
||||
return;
|
||||
ide_add_proc_entries(hwif->proc, hwif_entries, hwif);
|
||||
}
|
||||
create_proc_ide_drives(hwif);
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(create_proc_ide_interfaces);
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDEPCI
|
||||
void ide_pci_create_host_proc(const char *name, get_info_t *get_info)
|
||||
{
|
||||
create_proc_info_entry(name, 0, proc_ide_root, get_info);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_pci_create_host_proc);
|
||||
#endif
|
||||
|
||||
void destroy_proc_ide_interface(ide_hwif_t *hwif)
|
||||
{
|
||||
if (hwif->proc) {
|
||||
destroy_proc_ide_drives(hwif);
|
||||
ide_remove_proc_entries(hwif->proc, hwif_entries);
|
||||
remove_proc_entry(hwif->name, proc_ide_root);
|
||||
hwif->proc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int proc_print_driver(struct device_driver *drv, void *data)
|
||||
{
|
||||
ide_driver_t *ide_drv = container_of(drv, ide_driver_t, gen_driver);
|
||||
struct seq_file *s = data;
|
||||
|
||||
seq_printf(s, "%s version %s\n", drv->name, ide_drv->version);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ide_drivers_show(struct seq_file *s, void *p)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bus_for_each_drv(&ide_bus_type, NULL, s, proc_print_driver);
|
||||
if (err < 0)
|
||||
printk(KERN_WARNING "IDE: %s: bus_for_each_drv error: %d\n",
|
||||
__FUNCTION__, err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ide_drivers_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, &ide_drivers_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations ide_drivers_operations = {
|
||||
.open = ide_drivers_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
void proc_ide_create(void)
|
||||
{
|
||||
struct proc_dir_entry *entry;
|
||||
|
||||
if (!proc_ide_root)
|
||||
return;
|
||||
|
||||
create_proc_ide_interfaces();
|
||||
|
||||
entry = create_proc_entry("drivers", 0, proc_ide_root);
|
||||
if (entry)
|
||||
entry->proc_fops = &ide_drivers_operations;
|
||||
}
|
||||
|
||||
void proc_ide_destroy(void)
|
||||
{
|
||||
remove_proc_entry("drivers", proc_ide_root);
|
||||
remove_proc_entry("ide", NULL);
|
||||
}
|
||||
4947
drivers/ide/ide-tape.c
Normal file
4947
drivers/ide/ide-tape.c
Normal file
File diff suppressed because it is too large
Load Diff
868
drivers/ide/ide-taskfile.c
Normal file
868
drivers/ide/ide-taskfile.c
Normal file
@@ -0,0 +1,868 @@
|
||||
/*
|
||||
* linux/drivers/ide/ide-taskfile.c Version 0.38 March 05, 2003
|
||||
*
|
||||
* Copyright (C) 2000-2002 Michael Cornwell <cornwell@acm.org>
|
||||
* Copyright (C) 2000-2002 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2001-2002 Klaus Smolin
|
||||
* IBM Storage Technology Division
|
||||
* Copyright (C) 2003-2004 Bartlomiej Zolnierkiewicz
|
||||
*
|
||||
* The big the bad and the ugly.
|
||||
*
|
||||
* Problems to be fixed because of BH interface or the lack therefore.
|
||||
*
|
||||
* Fill me in stupid !!!
|
||||
*
|
||||
* HOST:
|
||||
* General refers to the Controller and Driver "pair".
|
||||
* DATA HANDLER:
|
||||
* Under the context of Linux it generally refers to an interrupt handler.
|
||||
* However, it correctly describes the 'HOST'
|
||||
* DATA BLOCK:
|
||||
* The amount of data needed to be transfered as predefined in the
|
||||
* setup of the device.
|
||||
* STORAGE ATOMIC:
|
||||
* The 'DATA BLOCK' associated to the 'DATA HANDLER', and can be as
|
||||
* small as a single sector or as large as the entire command block
|
||||
* request.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <linux/blkpg.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static void ata_bswap_data (void *buffer, int wcount)
|
||||
{
|
||||
u16 *p = buffer;
|
||||
|
||||
while (wcount--) {
|
||||
*p = *p << 8 | *p >> 8; p++;
|
||||
*p = *p << 8 | *p >> 8; p++;
|
||||
}
|
||||
}
|
||||
|
||||
static void taskfile_input_data(ide_drive_t *drive, void *buffer, u32 wcount)
|
||||
{
|
||||
HWIF(drive)->ata_input_data(drive, buffer, wcount);
|
||||
if (drive->bswap)
|
||||
ata_bswap_data(buffer, wcount);
|
||||
}
|
||||
|
||||
static void taskfile_output_data(ide_drive_t *drive, void *buffer, u32 wcount)
|
||||
{
|
||||
if (drive->bswap) {
|
||||
ata_bswap_data(buffer, wcount);
|
||||
HWIF(drive)->ata_output_data(drive, buffer, wcount);
|
||||
ata_bswap_data(buffer, wcount);
|
||||
} else {
|
||||
HWIF(drive)->ata_output_data(drive, buffer, wcount);
|
||||
}
|
||||
}
|
||||
|
||||
int taskfile_lib_get_identify (ide_drive_t *drive, u8 *buf)
|
||||
{
|
||||
ide_task_t args;
|
||||
memset(&args, 0, sizeof(ide_task_t));
|
||||
args.tfRegister[IDE_NSECTOR_OFFSET] = 0x01;
|
||||
if (drive->media == ide_disk)
|
||||
args.tfRegister[IDE_COMMAND_OFFSET] = WIN_IDENTIFY;
|
||||
else
|
||||
args.tfRegister[IDE_COMMAND_OFFSET] = WIN_PIDENTIFY;
|
||||
args.command_type = IDE_DRIVE_TASK_IN;
|
||||
args.data_phase = TASKFILE_IN;
|
||||
args.handler = &task_in_intr;
|
||||
return ide_raw_taskfile(drive, &args, buf);
|
||||
}
|
||||
|
||||
ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
task_struct_t *taskfile = (task_struct_t *) task->tfRegister;
|
||||
hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister;
|
||||
u8 HIHI = (drive->addressing == 1) ? 0xE0 : 0xEF;
|
||||
|
||||
/* ALL Command Block Executions SHALL clear nIEN, unless otherwise */
|
||||
if (IDE_CONTROL_REG) {
|
||||
/* clear nIEN */
|
||||
hwif->OUTB(drive->ctl, IDE_CONTROL_REG);
|
||||
}
|
||||
SELECT_MASK(drive, 0);
|
||||
|
||||
if (drive->addressing == 1) {
|
||||
hwif->OUTB(hobfile->feature, IDE_FEATURE_REG);
|
||||
hwif->OUTB(hobfile->sector_count, IDE_NSECTOR_REG);
|
||||
hwif->OUTB(hobfile->sector_number, IDE_SECTOR_REG);
|
||||
hwif->OUTB(hobfile->low_cylinder, IDE_LCYL_REG);
|
||||
hwif->OUTB(hobfile->high_cylinder, IDE_HCYL_REG);
|
||||
}
|
||||
|
||||
hwif->OUTB(taskfile->feature, IDE_FEATURE_REG);
|
||||
hwif->OUTB(taskfile->sector_count, IDE_NSECTOR_REG);
|
||||
hwif->OUTB(taskfile->sector_number, IDE_SECTOR_REG);
|
||||
hwif->OUTB(taskfile->low_cylinder, IDE_LCYL_REG);
|
||||
hwif->OUTB(taskfile->high_cylinder, IDE_HCYL_REG);
|
||||
|
||||
hwif->OUTB((taskfile->device_head & HIHI) | drive->select.all, IDE_SELECT_REG);
|
||||
|
||||
if (task->handler != NULL) {
|
||||
if (task->prehandler != NULL) {
|
||||
hwif->OUTBSYNC(drive, taskfile->command, IDE_COMMAND_REG);
|
||||
ndelay(400); /* FIXME */
|
||||
return task->prehandler(drive, task->rq);
|
||||
}
|
||||
ide_execute_command(drive, taskfile->command, task->handler, WAIT_WORSTCASE, NULL);
|
||||
return ide_started;
|
||||
}
|
||||
|
||||
if (!drive->using_dma)
|
||||
return ide_stopped;
|
||||
|
||||
switch (taskfile->command) {
|
||||
case WIN_WRITEDMA_ONCE:
|
||||
case WIN_WRITEDMA:
|
||||
case WIN_WRITEDMA_EXT:
|
||||
case WIN_READDMA_ONCE:
|
||||
case WIN_READDMA:
|
||||
case WIN_READDMA_EXT:
|
||||
case WIN_IDENTIFY_DMA:
|
||||
if (!hwif->dma_setup(drive)) {
|
||||
hwif->dma_exec_cmd(drive, taskfile->command);
|
||||
hwif->dma_start(drive);
|
||||
return ide_started;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (task->handler == NULL)
|
||||
return ide_stopped;
|
||||
}
|
||||
|
||||
return ide_stopped;
|
||||
}
|
||||
|
||||
/*
|
||||
* set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
|
||||
*/
|
||||
ide_startstop_t set_multmode_intr (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 stat;
|
||||
|
||||
if (OK_STAT(stat = hwif->INB(IDE_STATUS_REG),READY_STAT,BAD_STAT)) {
|
||||
drive->mult_count = drive->mult_req;
|
||||
} else {
|
||||
drive->mult_req = drive->mult_count = 0;
|
||||
drive->special.b.recalibrate = 1;
|
||||
(void) ide_dump_status(drive, "set_multmode", stat);
|
||||
}
|
||||
return ide_stopped;
|
||||
}
|
||||
|
||||
/*
|
||||
* set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
|
||||
*/
|
||||
ide_startstop_t set_geometry_intr (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
int retries = 5;
|
||||
u8 stat;
|
||||
|
||||
while (((stat = hwif->INB(IDE_STATUS_REG)) & BUSY_STAT) && retries--)
|
||||
udelay(10);
|
||||
|
||||
if (OK_STAT(stat, READY_STAT, BAD_STAT))
|
||||
return ide_stopped;
|
||||
|
||||
if (stat & (ERR_STAT|DRQ_STAT))
|
||||
return ide_error(drive, "set_geometry_intr", stat);
|
||||
|
||||
BUG_ON(HWGROUP(drive)->handler != NULL);
|
||||
ide_set_handler(drive, &set_geometry_intr, WAIT_WORSTCASE, NULL);
|
||||
return ide_started;
|
||||
}
|
||||
|
||||
/*
|
||||
* recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
|
||||
*/
|
||||
ide_startstop_t recal_intr (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 stat;
|
||||
|
||||
if (!OK_STAT(stat = hwif->INB(IDE_STATUS_REG), READY_STAT, BAD_STAT))
|
||||
return ide_error(drive, "recal_intr", stat);
|
||||
return ide_stopped;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handler for commands without a data phase
|
||||
*/
|
||||
ide_startstop_t task_no_data_intr (ide_drive_t *drive)
|
||||
{
|
||||
ide_task_t *args = HWGROUP(drive)->rq->special;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 stat;
|
||||
|
||||
local_irq_enable_in_hardirq();
|
||||
if (!OK_STAT(stat = hwif->INB(IDE_STATUS_REG),READY_STAT,BAD_STAT)) {
|
||||
return ide_error(drive, "task_no_data_intr", stat);
|
||||
/* calls ide_end_drive_cmd */
|
||||
}
|
||||
if (args)
|
||||
ide_end_drive_cmd(drive, stat, hwif->INB(IDE_ERROR_REG));
|
||||
|
||||
return ide_stopped;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(task_no_data_intr);
|
||||
|
||||
static u8 wait_drive_not_busy(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
int retries = 100;
|
||||
u8 stat;
|
||||
|
||||
/*
|
||||
* Last sector was transfered, wait until drive is ready.
|
||||
* This can take up to 10 usec, but we will wait max 1 ms
|
||||
* (drive_cmd_intr() waits that long).
|
||||
*/
|
||||
while (((stat = hwif->INB(IDE_STATUS_REG)) & BUSY_STAT) && retries--)
|
||||
udelay(10);
|
||||
|
||||
if (!retries)
|
||||
printk(KERN_ERR "%s: drive still BUSY!\n", drive->name);
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
static void ide_pio_sector(ide_drive_t *drive, unsigned int write)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct scatterlist *sg = hwif->sg_table;
|
||||
struct page *page;
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
unsigned long flags;
|
||||
#endif
|
||||
unsigned int offset;
|
||||
u8 *buf;
|
||||
|
||||
page = sg[hwif->cursg].page;
|
||||
offset = sg[hwif->cursg].offset + hwif->cursg_ofs * SECTOR_SIZE;
|
||||
|
||||
/* get the current page and offset */
|
||||
page = nth_page(page, (offset >> PAGE_SHIFT));
|
||||
offset %= PAGE_SIZE;
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
local_irq_save(flags);
|
||||
#endif
|
||||
buf = kmap_atomic(page, KM_BIO_SRC_IRQ) + offset;
|
||||
|
||||
hwif->nleft--;
|
||||
hwif->cursg_ofs++;
|
||||
|
||||
if ((hwif->cursg_ofs * SECTOR_SIZE) == sg[hwif->cursg].length) {
|
||||
hwif->cursg++;
|
||||
hwif->cursg_ofs = 0;
|
||||
}
|
||||
|
||||
/* do the actual data transfer */
|
||||
if (write)
|
||||
taskfile_output_data(drive, buf, SECTOR_WORDS);
|
||||
else
|
||||
taskfile_input_data(drive, buf, SECTOR_WORDS);
|
||||
|
||||
kunmap_atomic(buf, KM_BIO_SRC_IRQ);
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
local_irq_restore(flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ide_pio_multi(ide_drive_t *drive, unsigned int write)
|
||||
{
|
||||
unsigned int nsect;
|
||||
|
||||
nsect = min_t(unsigned int, drive->hwif->nleft, drive->mult_count);
|
||||
while (nsect--)
|
||||
ide_pio_sector(drive, write);
|
||||
}
|
||||
|
||||
static void ide_pio_datablock(ide_drive_t *drive, struct request *rq,
|
||||
unsigned int write)
|
||||
{
|
||||
if (rq->bio) /* fs request */
|
||||
rq->errors = 0;
|
||||
|
||||
touch_softlockup_watchdog();
|
||||
|
||||
switch (drive->hwif->data_phase) {
|
||||
case TASKFILE_MULTI_IN:
|
||||
case TASKFILE_MULTI_OUT:
|
||||
ide_pio_multi(drive, write);
|
||||
break;
|
||||
default:
|
||||
ide_pio_sector(drive, write);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ide_startstop_t task_error(ide_drive_t *drive, struct request *rq,
|
||||
const char *s, u8 stat)
|
||||
{
|
||||
if (rq->bio) {
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
int sectors = hwif->nsect - hwif->nleft;
|
||||
|
||||
switch (hwif->data_phase) {
|
||||
case TASKFILE_IN:
|
||||
if (hwif->nleft)
|
||||
break;
|
||||
/* fall through */
|
||||
case TASKFILE_OUT:
|
||||
sectors--;
|
||||
break;
|
||||
case TASKFILE_MULTI_IN:
|
||||
if (hwif->nleft)
|
||||
break;
|
||||
/* fall through */
|
||||
case TASKFILE_MULTI_OUT:
|
||||
sectors -= drive->mult_count;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (sectors > 0) {
|
||||
ide_driver_t *drv;
|
||||
|
||||
drv = *(ide_driver_t **)rq->rq_disk->private_data;
|
||||
drv->end_request(drive, 1, sectors);
|
||||
}
|
||||
}
|
||||
return ide_error(drive, s, stat);
|
||||
}
|
||||
|
||||
static void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat)
|
||||
{
|
||||
if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
|
||||
ide_task_t *task = rq->special;
|
||||
|
||||
if (task->tf_out_flags.all) {
|
||||
u8 err = drive->hwif->INB(IDE_ERROR_REG);
|
||||
ide_end_drive_cmd(drive, stat, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (rq->rq_disk) {
|
||||
ide_driver_t *drv;
|
||||
|
||||
drv = *(ide_driver_t **)rq->rq_disk->private_data;;
|
||||
drv->end_request(drive, 1, rq->hard_nr_sectors);
|
||||
} else
|
||||
ide_end_request(drive, 1, rq->hard_nr_sectors);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handler for command with PIO data-in phase (Read/Read Multiple).
|
||||
*/
|
||||
ide_startstop_t task_in_intr (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct request *rq = HWGROUP(drive)->rq;
|
||||
u8 stat = hwif->INB(IDE_STATUS_REG);
|
||||
|
||||
/* new way for dealing with premature shared PCI interrupts */
|
||||
if (!OK_STAT(stat, DATA_READY, BAD_R_STAT)) {
|
||||
if (stat & (ERR_STAT | DRQ_STAT))
|
||||
return task_error(drive, rq, __FUNCTION__, stat);
|
||||
/* No data yet, so wait for another IRQ. */
|
||||
ide_set_handler(drive, &task_in_intr, WAIT_WORSTCASE, NULL);
|
||||
return ide_started;
|
||||
}
|
||||
|
||||
ide_pio_datablock(drive, rq, 0);
|
||||
|
||||
/* If it was the last datablock check status and finish transfer. */
|
||||
if (!hwif->nleft) {
|
||||
stat = wait_drive_not_busy(drive);
|
||||
if (!OK_STAT(stat, 0, BAD_R_STAT))
|
||||
return task_error(drive, rq, __FUNCTION__, stat);
|
||||
task_end_request(drive, rq, stat);
|
||||
return ide_stopped;
|
||||
}
|
||||
|
||||
/* Still data left to transfer. */
|
||||
ide_set_handler(drive, &task_in_intr, WAIT_WORSTCASE, NULL);
|
||||
|
||||
return ide_started;
|
||||
}
|
||||
EXPORT_SYMBOL(task_in_intr);
|
||||
|
||||
/*
|
||||
* Handler for command with PIO data-out phase (Write/Write Multiple).
|
||||
*/
|
||||
static ide_startstop_t task_out_intr (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct request *rq = HWGROUP(drive)->rq;
|
||||
u8 stat = hwif->INB(IDE_STATUS_REG);
|
||||
|
||||
if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat))
|
||||
return task_error(drive, rq, __FUNCTION__, stat);
|
||||
|
||||
/* Deal with unexpected ATA data phase. */
|
||||
if (((stat & DRQ_STAT) == 0) ^ !hwif->nleft)
|
||||
return task_error(drive, rq, __FUNCTION__, stat);
|
||||
|
||||
if (!hwif->nleft) {
|
||||
task_end_request(drive, rq, stat);
|
||||
return ide_stopped;
|
||||
}
|
||||
|
||||
/* Still data left to transfer. */
|
||||
ide_pio_datablock(drive, rq, 1);
|
||||
ide_set_handler(drive, &task_out_intr, WAIT_WORSTCASE, NULL);
|
||||
|
||||
return ide_started;
|
||||
}
|
||||
|
||||
ide_startstop_t pre_task_out_intr (ide_drive_t *drive, struct request *rq)
|
||||
{
|
||||
ide_startstop_t startstop;
|
||||
|
||||
if (ide_wait_stat(&startstop, drive, DATA_READY,
|
||||
drive->bad_wstat, WAIT_DRQ)) {
|
||||
printk(KERN_ERR "%s: no DRQ after issuing %sWRITE%s\n",
|
||||
drive->name,
|
||||
drive->hwif->data_phase ? "MULT" : "",
|
||||
drive->addressing ? "_EXT" : "");
|
||||
return startstop;
|
||||
}
|
||||
|
||||
if (!drive->unmask)
|
||||
local_irq_disable();
|
||||
|
||||
ide_set_handler(drive, &task_out_intr, WAIT_WORSTCASE, NULL);
|
||||
ide_pio_datablock(drive, rq, 1);
|
||||
|
||||
return ide_started;
|
||||
}
|
||||
EXPORT_SYMBOL(pre_task_out_intr);
|
||||
|
||||
static int ide_diag_taskfile(ide_drive_t *drive, ide_task_t *args, unsigned long data_size, u8 *buf)
|
||||
{
|
||||
struct request rq;
|
||||
|
||||
memset(&rq, 0, sizeof(rq));
|
||||
rq.cmd_type = REQ_TYPE_ATA_TASKFILE;
|
||||
rq.buffer = buf;
|
||||
|
||||
/*
|
||||
* (ks) We transfer currently only whole sectors.
|
||||
* This is suffient for now. But, it would be great,
|
||||
* if we would find a solution to transfer any size.
|
||||
* To support special commands like READ LONG.
|
||||
*/
|
||||
if (args->command_type != IDE_DRIVE_TASK_NO_DATA) {
|
||||
if (data_size == 0)
|
||||
rq.nr_sectors = (args->hobRegister[IDE_NSECTOR_OFFSET] << 8) | args->tfRegister[IDE_NSECTOR_OFFSET];
|
||||
else
|
||||
rq.nr_sectors = data_size / SECTOR_SIZE;
|
||||
|
||||
if (!rq.nr_sectors) {
|
||||
printk(KERN_ERR "%s: in/out command without data\n",
|
||||
drive->name);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
rq.hard_nr_sectors = rq.nr_sectors;
|
||||
rq.hard_cur_sectors = rq.current_nr_sectors = rq.nr_sectors;
|
||||
|
||||
if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
|
||||
rq.cmd_flags |= REQ_RW;
|
||||
}
|
||||
|
||||
rq.special = args;
|
||||
args->rq = &rq;
|
||||
return ide_do_drive_cmd(drive, &rq, ide_wait);
|
||||
}
|
||||
|
||||
int ide_raw_taskfile (ide_drive_t *drive, ide_task_t *args, u8 *buf)
|
||||
{
|
||||
return ide_diag_taskfile(drive, args, 0, buf);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ide_raw_taskfile);
|
||||
|
||||
int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
ide_task_request_t *req_task;
|
||||
ide_task_t args;
|
||||
u8 *outbuf = NULL;
|
||||
u8 *inbuf = NULL;
|
||||
task_ioreg_t *argsptr = args.tfRegister;
|
||||
task_ioreg_t *hobsptr = args.hobRegister;
|
||||
int err = 0;
|
||||
int tasksize = sizeof(struct ide_task_request_s);
|
||||
unsigned int taskin = 0;
|
||||
unsigned int taskout = 0;
|
||||
u8 io_32bit = drive->io_32bit;
|
||||
char __user *buf = (char __user *)arg;
|
||||
|
||||
// printk("IDE Taskfile ...\n");
|
||||
|
||||
req_task = kzalloc(tasksize, GFP_KERNEL);
|
||||
if (req_task == NULL) return -ENOMEM;
|
||||
if (copy_from_user(req_task, buf, tasksize)) {
|
||||
kfree(req_task);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
taskout = req_task->out_size;
|
||||
taskin = req_task->in_size;
|
||||
|
||||
if (taskin > 65536 || taskout > 65536) {
|
||||
err = -EINVAL;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (taskout) {
|
||||
int outtotal = tasksize;
|
||||
outbuf = kzalloc(taskout, GFP_KERNEL);
|
||||
if (outbuf == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto abort;
|
||||
}
|
||||
if (copy_from_user(outbuf, buf + outtotal, taskout)) {
|
||||
err = -EFAULT;
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
|
||||
if (taskin) {
|
||||
int intotal = tasksize + taskout;
|
||||
inbuf = kzalloc(taskin, GFP_KERNEL);
|
||||
if (inbuf == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto abort;
|
||||
}
|
||||
if (copy_from_user(inbuf, buf + intotal, taskin)) {
|
||||
err = -EFAULT;
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&args, 0, sizeof(ide_task_t));
|
||||
memcpy(argsptr, req_task->io_ports, HDIO_DRIVE_TASK_HDR_SIZE);
|
||||
memcpy(hobsptr, req_task->hob_ports, HDIO_DRIVE_HOB_HDR_SIZE);
|
||||
|
||||
args.tf_in_flags = req_task->in_flags;
|
||||
args.tf_out_flags = req_task->out_flags;
|
||||
args.data_phase = req_task->data_phase;
|
||||
args.command_type = req_task->req_cmd;
|
||||
|
||||
drive->io_32bit = 0;
|
||||
switch(req_task->data_phase) {
|
||||
case TASKFILE_OUT_DMAQ:
|
||||
case TASKFILE_OUT_DMA:
|
||||
err = ide_diag_taskfile(drive, &args, taskout, outbuf);
|
||||
break;
|
||||
case TASKFILE_IN_DMAQ:
|
||||
case TASKFILE_IN_DMA:
|
||||
err = ide_diag_taskfile(drive, &args, taskin, inbuf);
|
||||
break;
|
||||
case TASKFILE_MULTI_OUT:
|
||||
if (!drive->mult_count) {
|
||||
/* (hs): give up if multcount is not set */
|
||||
printk(KERN_ERR "%s: %s Multimode Write " \
|
||||
"multcount is not set\n",
|
||||
drive->name, __FUNCTION__);
|
||||
err = -EPERM;
|
||||
goto abort;
|
||||
}
|
||||
/* fall through */
|
||||
case TASKFILE_OUT:
|
||||
args.prehandler = &pre_task_out_intr;
|
||||
args.handler = &task_out_intr;
|
||||
err = ide_diag_taskfile(drive, &args, taskout, outbuf);
|
||||
break;
|
||||
case TASKFILE_MULTI_IN:
|
||||
if (!drive->mult_count) {
|
||||
/* (hs): give up if multcount is not set */
|
||||
printk(KERN_ERR "%s: %s Multimode Read failure " \
|
||||
"multcount is not set\n",
|
||||
drive->name, __FUNCTION__);
|
||||
err = -EPERM;
|
||||
goto abort;
|
||||
}
|
||||
/* fall through */
|
||||
case TASKFILE_IN:
|
||||
args.handler = &task_in_intr;
|
||||
err = ide_diag_taskfile(drive, &args, taskin, inbuf);
|
||||
break;
|
||||
case TASKFILE_NO_DATA:
|
||||
args.handler = &task_no_data_intr;
|
||||
err = ide_diag_taskfile(drive, &args, 0, NULL);
|
||||
break;
|
||||
default:
|
||||
err = -EFAULT;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
memcpy(req_task->io_ports, &(args.tfRegister), HDIO_DRIVE_TASK_HDR_SIZE);
|
||||
memcpy(req_task->hob_ports, &(args.hobRegister), HDIO_DRIVE_HOB_HDR_SIZE);
|
||||
req_task->in_flags = args.tf_in_flags;
|
||||
req_task->out_flags = args.tf_out_flags;
|
||||
|
||||
if (copy_to_user(buf, req_task, tasksize)) {
|
||||
err = -EFAULT;
|
||||
goto abort;
|
||||
}
|
||||
if (taskout) {
|
||||
int outtotal = tasksize;
|
||||
if (copy_to_user(buf + outtotal, outbuf, taskout)) {
|
||||
err = -EFAULT;
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
if (taskin) {
|
||||
int intotal = tasksize + taskout;
|
||||
if (copy_to_user(buf + intotal, inbuf, taskin)) {
|
||||
err = -EFAULT;
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
abort:
|
||||
kfree(req_task);
|
||||
kfree(outbuf);
|
||||
kfree(inbuf);
|
||||
|
||||
// printk("IDE Taskfile ioctl ended. rc = %i\n", err);
|
||||
|
||||
drive->io_32bit = io_32bit;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int ide_wait_cmd (ide_drive_t *drive, u8 cmd, u8 nsect, u8 feature, u8 sectors, u8 *buf)
|
||||
{
|
||||
struct request rq;
|
||||
u8 buffer[4];
|
||||
|
||||
if (!buf)
|
||||
buf = buffer;
|
||||
memset(buf, 0, 4 + SECTOR_WORDS * 4 * sectors);
|
||||
ide_init_drive_cmd(&rq);
|
||||
rq.buffer = buf;
|
||||
*buf++ = cmd;
|
||||
*buf++ = nsect;
|
||||
*buf++ = feature;
|
||||
*buf++ = sectors;
|
||||
return ide_do_drive_cmd(drive, &rq, ide_wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME : this needs to map into at taskfile. <andre@linux-ide.org>
|
||||
*/
|
||||
int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int err = 0;
|
||||
u8 args[4], *argbuf = args;
|
||||
u8 xfer_rate = 0;
|
||||
int argsize = 4;
|
||||
ide_task_t tfargs;
|
||||
|
||||
if (NULL == (void *) arg) {
|
||||
struct request rq;
|
||||
ide_init_drive_cmd(&rq);
|
||||
return ide_do_drive_cmd(drive, &rq, ide_wait);
|
||||
}
|
||||
|
||||
if (copy_from_user(args, (void __user *)arg, 4))
|
||||
return -EFAULT;
|
||||
|
||||
memset(&tfargs, 0, sizeof(ide_task_t));
|
||||
tfargs.tfRegister[IDE_FEATURE_OFFSET] = args[2];
|
||||
tfargs.tfRegister[IDE_NSECTOR_OFFSET] = args[3];
|
||||
tfargs.tfRegister[IDE_SECTOR_OFFSET] = args[1];
|
||||
tfargs.tfRegister[IDE_LCYL_OFFSET] = 0x00;
|
||||
tfargs.tfRegister[IDE_HCYL_OFFSET] = 0x00;
|
||||
tfargs.tfRegister[IDE_SELECT_OFFSET] = 0x00;
|
||||
tfargs.tfRegister[IDE_COMMAND_OFFSET] = args[0];
|
||||
|
||||
if (args[3]) {
|
||||
argsize = 4 + (SECTOR_WORDS * 4 * args[3]);
|
||||
argbuf = kzalloc(argsize, GFP_KERNEL);
|
||||
if (argbuf == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (set_transfer(drive, &tfargs)) {
|
||||
xfer_rate = args[1];
|
||||
if (ide_ata66_check(drive, &tfargs))
|
||||
goto abort;
|
||||
}
|
||||
|
||||
err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf);
|
||||
|
||||
if (!err && xfer_rate) {
|
||||
/* active-retuning-calls future */
|
||||
ide_set_xfer_rate(drive, xfer_rate);
|
||||
ide_driveid_update(drive);
|
||||
}
|
||||
abort:
|
||||
if (copy_to_user((void __user *)arg, argbuf, argsize))
|
||||
err = -EFAULT;
|
||||
if (argsize > 4)
|
||||
kfree(argbuf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ide_wait_cmd_task(ide_drive_t *drive, u8 *buf)
|
||||
{
|
||||
struct request rq;
|
||||
|
||||
ide_init_drive_cmd(&rq);
|
||||
rq.cmd_type = REQ_TYPE_ATA_TASK;
|
||||
rq.buffer = buf;
|
||||
return ide_do_drive_cmd(drive, &rq, ide_wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME : this needs to map into at taskfile. <andre@linux-ide.org>
|
||||
*/
|
||||
int ide_task_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *p = (void __user *)arg;
|
||||
int err = 0;
|
||||
u8 args[7], *argbuf = args;
|
||||
int argsize = 7;
|
||||
|
||||
if (copy_from_user(args, p, 7))
|
||||
return -EFAULT;
|
||||
err = ide_wait_cmd_task(drive, argbuf);
|
||||
if (copy_to_user(p, argbuf, argsize))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTICE: This is additions from IBM to provide a discrete interface,
|
||||
* for selective taskregister access operations. Nice JOB Klaus!!!
|
||||
* Glad to be able to work and co-develop this with you and IBM.
|
||||
*/
|
||||
ide_startstop_t flagged_taskfile (ide_drive_t *drive, ide_task_t *task)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
task_struct_t *taskfile = (task_struct_t *) task->tfRegister;
|
||||
hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister;
|
||||
|
||||
if (task->data_phase == TASKFILE_MULTI_IN ||
|
||||
task->data_phase == TASKFILE_MULTI_OUT) {
|
||||
if (!drive->mult_count) {
|
||||
printk(KERN_ERR "%s: multimode not set!\n", drive->name);
|
||||
return ide_stopped;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (ks) Check taskfile in flags.
|
||||
* If set, then execute as it is defined.
|
||||
* If not set, then define default settings.
|
||||
* The default values are:
|
||||
* read all taskfile registers (except data)
|
||||
* read the hob registers (sector, nsector, lcyl, hcyl)
|
||||
*/
|
||||
if (task->tf_in_flags.all == 0) {
|
||||
task->tf_in_flags.all = IDE_TASKFILE_STD_IN_FLAGS;
|
||||
if (drive->addressing == 1)
|
||||
task->tf_in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8);
|
||||
}
|
||||
|
||||
/* ALL Command Block Executions SHALL clear nIEN, unless otherwise */
|
||||
if (IDE_CONTROL_REG)
|
||||
/* clear nIEN */
|
||||
hwif->OUTB(drive->ctl, IDE_CONTROL_REG);
|
||||
SELECT_MASK(drive, 0);
|
||||
|
||||
if (task->tf_out_flags.b.data) {
|
||||
u16 data = taskfile->data + (hobfile->data << 8);
|
||||
hwif->OUTW(data, IDE_DATA_REG);
|
||||
}
|
||||
|
||||
/* (ks) send hob registers first */
|
||||
if (task->tf_out_flags.b.nsector_hob)
|
||||
hwif->OUTB(hobfile->sector_count, IDE_NSECTOR_REG);
|
||||
if (task->tf_out_flags.b.sector_hob)
|
||||
hwif->OUTB(hobfile->sector_number, IDE_SECTOR_REG);
|
||||
if (task->tf_out_flags.b.lcyl_hob)
|
||||
hwif->OUTB(hobfile->low_cylinder, IDE_LCYL_REG);
|
||||
if (task->tf_out_flags.b.hcyl_hob)
|
||||
hwif->OUTB(hobfile->high_cylinder, IDE_HCYL_REG);
|
||||
|
||||
/* (ks) Send now the standard registers */
|
||||
if (task->tf_out_flags.b.error_feature)
|
||||
hwif->OUTB(taskfile->feature, IDE_FEATURE_REG);
|
||||
/* refers to number of sectors to transfer */
|
||||
if (task->tf_out_flags.b.nsector)
|
||||
hwif->OUTB(taskfile->sector_count, IDE_NSECTOR_REG);
|
||||
/* refers to sector offset or start sector */
|
||||
if (task->tf_out_flags.b.sector)
|
||||
hwif->OUTB(taskfile->sector_number, IDE_SECTOR_REG);
|
||||
if (task->tf_out_flags.b.lcyl)
|
||||
hwif->OUTB(taskfile->low_cylinder, IDE_LCYL_REG);
|
||||
if (task->tf_out_flags.b.hcyl)
|
||||
hwif->OUTB(taskfile->high_cylinder, IDE_HCYL_REG);
|
||||
|
||||
/*
|
||||
* (ks) In the flagged taskfile approch, we will use all specified
|
||||
* registers and the register value will not be changed, except the
|
||||
* select bit (master/slave) in the drive_head register. We must make
|
||||
* sure that the desired drive is selected.
|
||||
*/
|
||||
hwif->OUTB(taskfile->device_head | drive->select.all, IDE_SELECT_REG);
|
||||
switch(task->data_phase) {
|
||||
|
||||
case TASKFILE_OUT_DMAQ:
|
||||
case TASKFILE_OUT_DMA:
|
||||
case TASKFILE_IN_DMAQ:
|
||||
case TASKFILE_IN_DMA:
|
||||
hwif->dma_setup(drive);
|
||||
hwif->dma_exec_cmd(drive, taskfile->command);
|
||||
hwif->dma_start(drive);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (task->handler == NULL)
|
||||
return ide_stopped;
|
||||
|
||||
/* Issue the command */
|
||||
if (task->prehandler) {
|
||||
hwif->OUTBSYNC(drive, taskfile->command, IDE_COMMAND_REG);
|
||||
ndelay(400); /* FIXME */
|
||||
return task->prehandler(drive, task->rq);
|
||||
}
|
||||
ide_execute_command(drive, taskfile->command, task->handler, WAIT_WORSTCASE, NULL);
|
||||
}
|
||||
|
||||
return ide_started;
|
||||
}
|
||||
286
drivers/ide/ide-timing.h
Normal file
286
drivers/ide/ide-timing.h
Normal file
@@ -0,0 +1,286 @@
|
||||
#ifndef _IDE_TIMING_H
|
||||
#define _IDE_TIMING_H
|
||||
|
||||
/*
|
||||
* $Id: ide-timing.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Copyright (c) 1999-2001 Vojtech Pavlik
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so either by
|
||||
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/hdreg.h>
|
||||
|
||||
#define XFER_PIO_5 0x0d
|
||||
#define XFER_UDMA_SLOW 0x4f
|
||||
|
||||
struct ide_timing {
|
||||
short mode;
|
||||
short setup; /* t1 */
|
||||
short act8b; /* t2 for 8-bit io */
|
||||
short rec8b; /* t2i for 8-bit io */
|
||||
short cyc8b; /* t0 for 8-bit io */
|
||||
short active; /* t2 or tD */
|
||||
short recover; /* t2i or tK */
|
||||
short cycle; /* t0 */
|
||||
short udma; /* t2CYCTYP/2 */
|
||||
};
|
||||
|
||||
/*
|
||||
* PIO 0-5, MWDMA 0-2 and UDMA 0-6 timings (in nanoseconds).
|
||||
* These were taken from ATA/ATAPI-6 standard, rev 0a, except
|
||||
* for PIO 5, which is a nonstandard extension and UDMA6, which
|
||||
* is currently supported only by Maxtor drives.
|
||||
*/
|
||||
|
||||
static struct ide_timing ide_timing[] = {
|
||||
|
||||
{ XFER_UDMA_6, 0, 0, 0, 0, 0, 0, 0, 15 },
|
||||
{ XFER_UDMA_5, 0, 0, 0, 0, 0, 0, 0, 20 },
|
||||
{ XFER_UDMA_4, 0, 0, 0, 0, 0, 0, 0, 30 },
|
||||
{ XFER_UDMA_3, 0, 0, 0, 0, 0, 0, 0, 45 },
|
||||
|
||||
{ XFER_UDMA_2, 0, 0, 0, 0, 0, 0, 0, 60 },
|
||||
{ XFER_UDMA_1, 0, 0, 0, 0, 0, 0, 0, 80 },
|
||||
{ XFER_UDMA_0, 0, 0, 0, 0, 0, 0, 0, 120 },
|
||||
|
||||
{ XFER_UDMA_SLOW, 0, 0, 0, 0, 0, 0, 0, 150 },
|
||||
|
||||
{ XFER_MW_DMA_2, 25, 0, 0, 0, 70, 25, 120, 0 },
|
||||
{ XFER_MW_DMA_1, 45, 0, 0, 0, 80, 50, 150, 0 },
|
||||
{ XFER_MW_DMA_0, 60, 0, 0, 0, 215, 215, 480, 0 },
|
||||
|
||||
{ XFER_SW_DMA_2, 60, 0, 0, 0, 120, 120, 240, 0 },
|
||||
{ XFER_SW_DMA_1, 90, 0, 0, 0, 240, 240, 480, 0 },
|
||||
{ XFER_SW_DMA_0, 120, 0, 0, 0, 480, 480, 960, 0 },
|
||||
|
||||
{ XFER_PIO_5, 20, 50, 30, 100, 50, 30, 100, 0 },
|
||||
{ XFER_PIO_4, 25, 70, 25, 120, 70, 25, 120, 0 },
|
||||
{ XFER_PIO_3, 30, 80, 70, 180, 80, 70, 180, 0 },
|
||||
|
||||
{ XFER_PIO_2, 30, 290, 40, 330, 100, 90, 240, 0 },
|
||||
{ XFER_PIO_1, 50, 290, 93, 383, 125, 100, 383, 0 },
|
||||
{ XFER_PIO_0, 70, 290, 240, 600, 165, 150, 600, 0 },
|
||||
|
||||
{ XFER_PIO_SLOW, 120, 290, 240, 960, 290, 240, 960, 0 },
|
||||
|
||||
{ -1 }
|
||||
};
|
||||
|
||||
#define IDE_TIMING_SETUP 0x01
|
||||
#define IDE_TIMING_ACT8B 0x02
|
||||
#define IDE_TIMING_REC8B 0x04
|
||||
#define IDE_TIMING_CYC8B 0x08
|
||||
#define IDE_TIMING_8BIT 0x0e
|
||||
#define IDE_TIMING_ACTIVE 0x10
|
||||
#define IDE_TIMING_RECOVER 0x20
|
||||
#define IDE_TIMING_CYCLE 0x40
|
||||
#define IDE_TIMING_UDMA 0x80
|
||||
#define IDE_TIMING_ALL 0xff
|
||||
|
||||
#define FIT(v,vmin,vmax) max_t(short,min_t(short,v,vmax),vmin)
|
||||
#define ENOUGH(v,unit) (((v)-1)/(unit)+1)
|
||||
#define EZ(v,unit) ((v)?ENOUGH(v,unit):0)
|
||||
|
||||
#define XFER_MODE 0xf0
|
||||
#define XFER_UDMA_133 0x48
|
||||
#define XFER_UDMA_100 0x44
|
||||
#define XFER_UDMA_66 0x42
|
||||
#define XFER_UDMA 0x40
|
||||
#define XFER_MWDMA 0x20
|
||||
#define XFER_SWDMA 0x10
|
||||
#define XFER_EPIO 0x01
|
||||
#define XFER_PIO 0x00
|
||||
|
||||
static short ide_find_best_mode(ide_drive_t *drive, int map)
|
||||
{
|
||||
struct hd_driveid *id = drive->id;
|
||||
short best = 0;
|
||||
|
||||
if (!id)
|
||||
return XFER_PIO_SLOW;
|
||||
|
||||
if ((map & XFER_UDMA) && (id->field_valid & 4)) { /* Want UDMA and UDMA bitmap valid */
|
||||
|
||||
if ((map & XFER_UDMA_133) == XFER_UDMA_133)
|
||||
if ((best = (id->dma_ultra & 0x0040) ? XFER_UDMA_6 : 0)) return best;
|
||||
|
||||
if ((map & XFER_UDMA_100) == XFER_UDMA_100)
|
||||
if ((best = (id->dma_ultra & 0x0020) ? XFER_UDMA_5 : 0)) return best;
|
||||
|
||||
if ((map & XFER_UDMA_66) == XFER_UDMA_66)
|
||||
if ((best = (id->dma_ultra & 0x0010) ? XFER_UDMA_4 :
|
||||
(id->dma_ultra & 0x0008) ? XFER_UDMA_3 : 0)) return best;
|
||||
|
||||
if ((best = (id->dma_ultra & 0x0004) ? XFER_UDMA_2 :
|
||||
(id->dma_ultra & 0x0002) ? XFER_UDMA_1 :
|
||||
(id->dma_ultra & 0x0001) ? XFER_UDMA_0 : 0)) return best;
|
||||
}
|
||||
|
||||
if ((map & XFER_MWDMA) && (id->field_valid & 2)) { /* Want MWDMA and drive has EIDE fields */
|
||||
|
||||
if ((best = (id->dma_mword & 0x0004) ? XFER_MW_DMA_2 :
|
||||
(id->dma_mword & 0x0002) ? XFER_MW_DMA_1 :
|
||||
(id->dma_mword & 0x0001) ? XFER_MW_DMA_0 : 0)) return best;
|
||||
}
|
||||
|
||||
if (map & XFER_SWDMA) { /* Want SWDMA */
|
||||
|
||||
if (id->field_valid & 2) { /* EIDE SWDMA */
|
||||
|
||||
if ((best = (id->dma_1word & 0x0004) ? XFER_SW_DMA_2 :
|
||||
(id->dma_1word & 0x0002) ? XFER_SW_DMA_1 :
|
||||
(id->dma_1word & 0x0001) ? XFER_SW_DMA_0 : 0)) return best;
|
||||
}
|
||||
|
||||
if (id->capability & 1) { /* Pre-EIDE style SWDMA */
|
||||
|
||||
if ((best = (id->tDMA == 2) ? XFER_SW_DMA_2 :
|
||||
(id->tDMA == 1) ? XFER_SW_DMA_1 :
|
||||
(id->tDMA == 0) ? XFER_SW_DMA_0 : 0)) return best;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ((map & XFER_EPIO) && (id->field_valid & 2)) { /* EIDE PIO modes */
|
||||
|
||||
if ((best = (drive->id->eide_pio_modes & 4) ? XFER_PIO_5 :
|
||||
(drive->id->eide_pio_modes & 2) ? XFER_PIO_4 :
|
||||
(drive->id->eide_pio_modes & 1) ? XFER_PIO_3 : 0)) return best;
|
||||
}
|
||||
|
||||
return (drive->id->tPIO == 2) ? XFER_PIO_2 :
|
||||
(drive->id->tPIO == 1) ? XFER_PIO_1 :
|
||||
(drive->id->tPIO == 0) ? XFER_PIO_0 : XFER_PIO_SLOW;
|
||||
}
|
||||
|
||||
static void ide_timing_quantize(struct ide_timing *t, struct ide_timing *q, int T, int UT)
|
||||
{
|
||||
q->setup = EZ(t->setup * 1000, T);
|
||||
q->act8b = EZ(t->act8b * 1000, T);
|
||||
q->rec8b = EZ(t->rec8b * 1000, T);
|
||||
q->cyc8b = EZ(t->cyc8b * 1000, T);
|
||||
q->active = EZ(t->active * 1000, T);
|
||||
q->recover = EZ(t->recover * 1000, T);
|
||||
q->cycle = EZ(t->cycle * 1000, T);
|
||||
q->udma = EZ(t->udma * 1000, UT);
|
||||
}
|
||||
|
||||
static void ide_timing_merge(struct ide_timing *a, struct ide_timing *b, struct ide_timing *m, unsigned int what)
|
||||
{
|
||||
if (what & IDE_TIMING_SETUP ) m->setup = max(a->setup, b->setup);
|
||||
if (what & IDE_TIMING_ACT8B ) m->act8b = max(a->act8b, b->act8b);
|
||||
if (what & IDE_TIMING_REC8B ) m->rec8b = max(a->rec8b, b->rec8b);
|
||||
if (what & IDE_TIMING_CYC8B ) m->cyc8b = max(a->cyc8b, b->cyc8b);
|
||||
if (what & IDE_TIMING_ACTIVE ) m->active = max(a->active, b->active);
|
||||
if (what & IDE_TIMING_RECOVER) m->recover = max(a->recover, b->recover);
|
||||
if (what & IDE_TIMING_CYCLE ) m->cycle = max(a->cycle, b->cycle);
|
||||
if (what & IDE_TIMING_UDMA ) m->udma = max(a->udma, b->udma);
|
||||
}
|
||||
|
||||
static struct ide_timing* ide_timing_find_mode(short speed)
|
||||
{
|
||||
struct ide_timing *t;
|
||||
|
||||
for (t = ide_timing; t->mode != speed; t++)
|
||||
if (t->mode < 0)
|
||||
return NULL;
|
||||
return t;
|
||||
}
|
||||
|
||||
static int ide_timing_compute(ide_drive_t *drive, short speed, struct ide_timing *t, int T, int UT)
|
||||
{
|
||||
struct hd_driveid *id = drive->id;
|
||||
struct ide_timing *s, p;
|
||||
|
||||
/*
|
||||
* Find the mode.
|
||||
*/
|
||||
|
||||
if (!(s = ide_timing_find_mode(speed)))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Copy the timing from the table.
|
||||
*/
|
||||
|
||||
*t = *s;
|
||||
|
||||
/*
|
||||
* If the drive is an EIDE drive, it can tell us it needs extended
|
||||
* PIO/MWDMA cycle timing.
|
||||
*/
|
||||
|
||||
if (id && id->field_valid & 2) { /* EIDE drive */
|
||||
|
||||
memset(&p, 0, sizeof(p));
|
||||
|
||||
switch (speed & XFER_MODE) {
|
||||
|
||||
case XFER_PIO:
|
||||
if (speed <= XFER_PIO_2) p.cycle = p.cyc8b = id->eide_pio;
|
||||
else p.cycle = p.cyc8b = id->eide_pio_iordy;
|
||||
break;
|
||||
|
||||
case XFER_MWDMA:
|
||||
p.cycle = id->eide_dma_min;
|
||||
break;
|
||||
}
|
||||
|
||||
ide_timing_merge(&p, t, t, IDE_TIMING_CYCLE | IDE_TIMING_CYC8B);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the timing to bus clock counts.
|
||||
*/
|
||||
|
||||
ide_timing_quantize(t, t, T, UT);
|
||||
|
||||
/*
|
||||
* Even in DMA/UDMA modes we still use PIO access for IDENTIFY, S.M.A.R.T
|
||||
* and some other commands. We have to ensure that the DMA cycle timing is
|
||||
* slower/equal than the fastest PIO timing.
|
||||
*/
|
||||
|
||||
if ((speed & XFER_MODE) != XFER_PIO) {
|
||||
ide_timing_compute(drive, ide_find_best_mode(drive, XFER_PIO | XFER_EPIO), &p, T, UT);
|
||||
ide_timing_merge(&p, t, t, IDE_TIMING_ALL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lenghten active & recovery time so that cycle time is correct.
|
||||
*/
|
||||
|
||||
if (t->act8b + t->rec8b < t->cyc8b) {
|
||||
t->act8b += (t->cyc8b - (t->act8b + t->rec8b)) / 2;
|
||||
t->rec8b = t->cyc8b - t->act8b;
|
||||
}
|
||||
|
||||
if (t->active + t->recover < t->cycle) {
|
||||
t->active += (t->cycle - (t->active + t->recover)) / 2;
|
||||
t->recover = t->cycle - t->active;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
2159
drivers/ide/ide.c
Normal file
2159
drivers/ide/ide.c
Normal file
File diff suppressed because it is too large
Load Diff
13
drivers/ide/legacy/Makefile
Normal file
13
drivers/ide/legacy/Makefile
Normal file
@@ -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
|
||||
259
drivers/ide/legacy/ali14xx.c
Normal file
259
drivers/ide/legacy/ali14xx.c
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <linux/module.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;
|
||||
}
|
||||
|
||||
int probe_ali14xx = 0;
|
||||
|
||||
module_param_named(probe, probe_ali14xx, bool, 0);
|
||||
MODULE_PARM_DESC(probe, "probe for ALI M14xx chipsets");
|
||||
|
||||
/* Can be called directly from ide.c. */
|
||||
int __init ali14xx_init(void)
|
||||
{
|
||||
if (probe_ali14xx == 0)
|
||||
goto out;
|
||||
|
||||
/* auto-detect IDE controller port */
|
||||
if (findPort()) {
|
||||
if (ali14xx_probe())
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
printk(KERN_ERR "ali14xx: not found.\n");
|
||||
out:
|
||||
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
Normal file
235
drivers/ide/legacy/buddha.c
Normal file
@@ -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 = 1;
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
170
drivers/ide/legacy/dtc2278.c
Normal file
170
drivers/ide/legacy/dtc2278.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* linux/drivers/ide/legacy/dtc2278.c Version 0.02 Feb 10, 1996
|
||||
*
|
||||
* Copyright (C) 1996 Linus Torvalds & author (see below)
|
||||
*/
|
||||
|
||||
#include <linux/module.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 dtc2278_probe(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;
|
||||
}
|
||||
|
||||
int probe_dtc2278 = 0;
|
||||
|
||||
module_param_named(probe, probe_dtc2278, bool, 0);
|
||||
MODULE_PARM_DESC(probe, "probe for DTC2278xx chipsets");
|
||||
|
||||
/* Can be called directly from ide.c. */
|
||||
int __init dtc2278_init(void)
|
||||
{
|
||||
if (probe_dtc2278 == 0)
|
||||
return -ENODEV;
|
||||
|
||||
if (dtc2278_probe()) {
|
||||
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
Normal file
78
drivers/ide/legacy/falconide.c
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
185
drivers/ide/legacy/gayle.c
Normal file
185
drivers/ide/legacy/gayle.c
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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/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 = 1;
|
||||
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);
|
||||
}
|
||||
}
|
||||
858
drivers/ide/legacy/hd.c
Normal file
858
drivers/ide/legacy/hd.c
Normal file
@@ -0,0 +1,858 @@
|
||||
/*
|
||||
* 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)
|
||||
|
||||
#include <asm/i8253.h>
|
||||
|
||||
unsigned long last_req;
|
||||
|
||||
unsigned long read_timer(void)
|
||||
{
|
||||
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 (blk_fs_request(req)) {
|
||||
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_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
||||
{
|
||||
struct hd_i_struct *disk = bdev->bd_disk->private_data;
|
||||
|
||||
geo->heads = disk->head;
|
||||
geo->sectors = disk->sect;
|
||||
geo->cylinders = disk->cyl;
|
||||
return 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)
|
||||
{
|
||||
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 = {
|
||||
.getgeo = hd_getgeo,
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the hard disk IRQ description. The IRQF_DISABLED 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, IRQF_DISABLED, "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 __init 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);
|
||||
375
drivers/ide/legacy/ht6560b.c
Normal file
375
drivers/ide/legacy/ht6560b.c
Normal file
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#include <linux/module.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)inb(HT_CONFIG_PORT);
|
||||
(void)inb(HT_CONFIG_PORT);
|
||||
(void)inb(HT_CONFIG_PORT);
|
||||
(void)inb(HT_CONFIG_PORT);
|
||||
outb(select, HT_CONFIG_PORT);
|
||||
/*
|
||||
* Set timing for this drive:
|
||||
*/
|
||||
outb(timing, IDE_SELECT_REG);
|
||||
(void)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
|
||||
}
|
||||
|
||||
int probe_ht6560b = 0;
|
||||
|
||||
module_param_named(probe, probe_ht6560b, bool, 0);
|
||||
MODULE_PARM_DESC(probe, "probe for HT6560B chipset");
|
||||
|
||||
/* Can be called directly from ide.c. */
|
||||
int __init ht6560b_init(void)
|
||||
{
|
||||
ide_hwif_t *hwif, *mate;
|
||||
int t;
|
||||
|
||||
if (probe_ht6560b == 0)
|
||||
return -ENODEV;
|
||||
|
||||
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");
|
||||
435
drivers/ide/legacy/ide-cs.c
Normal file
435
drivers/ide/legacy/ide-cs.c
Normal file
@@ -0,0 +1,435 @@
|
||||
/*======================================================================
|
||||
|
||||
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/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 <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/system.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 {
|
||||
struct pcmcia_device *p_dev;
|
||||
int ndev;
|
||||
dev_node_t node;
|
||||
int hd;
|
||||
} ide_info_t;
|
||||
|
||||
static void ide_release(struct pcmcia_device *);
|
||||
static int ide_config(struct pcmcia_device *);
|
||||
|
||||
static void ide_detach(struct pcmcia_device *p_dev);
|
||||
|
||||
|
||||
|
||||
|
||||
/*======================================================================
|
||||
|
||||
ide_attach() creates an "instance" of the driver, allocating
|
||||
local data structures for one device. The device is registered
|
||||
with Card Services.
|
||||
|
||||
======================================================================*/
|
||||
|
||||
static int ide_probe(struct pcmcia_device *link)
|
||||
{
|
||||
ide_info_t *info;
|
||||
|
||||
DEBUG(0, "ide_attach()\n");
|
||||
|
||||
/* Create new ide device */
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->p_dev = 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_DYNAMIC_SHARING;
|
||||
link->irq.IRQInfo1 = IRQ_LEVEL_ID;
|
||||
link->conf.Attributes = CONF_ENABLE_IRQ;
|
||||
link->conf.IntType = INT_MEMORY_AND_IO;
|
||||
|
||||
return ide_config(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(struct pcmcia_device *link)
|
||||
{
|
||||
DEBUG(0, "ide_detach(0x%p)\n", link);
|
||||
|
||||
ide_release(link);
|
||||
|
||||
kfree(link->priv);
|
||||
} /* ide_detach */
|
||||
|
||||
static int idecs_register(unsigned long io, unsigned long ctl, unsigned long irq, struct pcmcia_device *handle)
|
||||
{
|
||||
hw_regs_t hw;
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
ide_init_hwif_ports(&hw, io, ctl, NULL);
|
||||
hw.irq = irq;
|
||||
hw.chipset = ide_pci;
|
||||
hw.dev = &handle->dev;
|
||||
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 int ide_config(struct pcmcia_device *link)
|
||||
{
|
||||
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 = kzalloc(sizeof(*stk), GFP_KERNEL);
|
||||
if (!stk) goto err_mem;
|
||||
cfg = &stk->parse.cftable_entry;
|
||||
|
||||
tuple.TupleData = (cisdata_t *)&stk->buf;
|
||||
tuple.TupleOffset = 0;
|
||||
tuple.TupleDataMax = 255;
|
||||
tuple.Attributes = 0;
|
||||
|
||||
is_kme = ((link->manf_id == MANFID_KME) &&
|
||||
((link->card_id == PRODID_KME_KXLC005_A) ||
|
||||
(link->card_id == PRODID_KME_KXLC005_B)));
|
||||
|
||||
/* Not sure if this is right... look up the current Vcc */
|
||||
CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &stk->conf));
|
||||
|
||||
pass = io_base = ctl_base = 0;
|
||||
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
|
||||
tuple.Attributes = 0;
|
||||
CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
|
||||
while (1) {
|
||||
if (pcmcia_get_tuple_data(link, &tuple) != 0) goto next_entry;
|
||||
if (pcmcia_parse_tuple(link, &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.Vpp =
|
||||
cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
|
||||
else if (stk->dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
|
||||
link->conf.Vpp =
|
||||
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, &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, &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(link, &tuple));
|
||||
} else if (pcmcia_get_next_tuple(link, &tuple) != 0) {
|
||||
CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
|
||||
memset(&stk->dflt, 0, sizeof(stk->dflt));
|
||||
pass++;
|
||||
}
|
||||
}
|
||||
|
||||
CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
|
||||
CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &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, link);
|
||||
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, link);
|
||||
if (hd >= 0) {
|
||||
io_base += 0x10;
|
||||
ctl_base += 0x10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
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_node = &info->node;
|
||||
printk(KERN_INFO "ide-cs: %s: Vpp = %d.%d\n",
|
||||
info->node.dev_name, link->conf.Vpp / 10, link->conf.Vpp % 10);
|
||||
|
||||
kfree(stk);
|
||||
return 0;
|
||||
|
||||
err_mem:
|
||||
printk(KERN_NOTICE "ide-cs: ide_config failed memory allocation\n");
|
||||
goto failed;
|
||||
|
||||
cs_failed:
|
||||
cs_error(link, last_fn, last_ret);
|
||||
failed:
|
||||
kfree(stk);
|
||||
ide_release(link);
|
||||
return -ENODEV;
|
||||
} /* 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(struct pcmcia_device *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;
|
||||
|
||||
pcmcia_disable_device(link);
|
||||
} /* 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.
|
||||
|
||||
======================================================================*/
|
||||
|
||||
static struct pcmcia_device_id ide_ids[] = {
|
||||
PCMCIA_DEVICE_FUNC_ID(4),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0007, 0x0000), /* Hitachi */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x000a, 0x0000), /* I-O Data CFA */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x001c, 0x0001), /* Mitsubishi CFA */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0032, 0x0704),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0045, 0x0401), /* SanDisk CFA */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0098, 0x0000), /* Toshiba */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x002d),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x00ce, 0x0000), /* Samsung */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0319, 0x0000), /* Hitachi */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x2080, 0x0001),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0100), /* Viking CFA */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0200), /* Lexar, Viking CFA */
|
||||
PCMCIA_DEVICE_PROD_ID123("Caravelle", "PSC-IDE ", "PSC000", 0x8c36137c, 0xd0693ab8, 0x2768a9f0),
|
||||
PCMCIA_DEVICE_PROD_ID123("CDROM", "IDE", "MCD-601p", 0x1b9179ca, 0xede88951, 0x0d902f74),
|
||||
PCMCIA_DEVICE_PROD_ID123("PCMCIA", "IDE CARD", "F1", 0x281f1c5d, 0x1907960c, 0xf7fde8b9),
|
||||
PCMCIA_DEVICE_PROD_ID12("ARGOSY", "CD-ROM", 0x78f308dc, 0x66536591),
|
||||
PCMCIA_DEVICE_PROD_ID12("ARGOSY", "PnPIDE", 0x78f308dc, 0x0c694728),
|
||||
PCMCIA_DEVICE_PROD_ID12("CNF CD-M", "CD-ROM", 0x7d93b852, 0x66536591),
|
||||
PCMCIA_DEVICE_PROD_ID12("Creative Technology Ltd.", "PCMCIA CD-ROM Interface Card", 0xff8c8a45, 0xfe8020c4),
|
||||
PCMCIA_DEVICE_PROD_ID12("Digital Equipment Corporation.", "Digital Mobile Media CD-ROM", 0x17692a66, 0xef1dcbde),
|
||||
PCMCIA_DEVICE_PROD_ID12("EXP", "CD+GAME", 0x6f58c983, 0x63c13aaf),
|
||||
PCMCIA_DEVICE_PROD_ID12("EXP ", "CD-ROM", 0x0a5c52fd, 0x66536591),
|
||||
PCMCIA_DEVICE_PROD_ID12("EXP ", "PnPIDE", 0x0a5c52fd, 0x0c694728),
|
||||
PCMCIA_DEVICE_PROD_ID12("FREECOM", "PCCARD-IDE", 0x5714cbf7, 0x48e0ab8e),
|
||||
PCMCIA_DEVICE_PROD_ID12("HITACHI", "FLASH", 0xf4f43949, 0x9eb86aae),
|
||||
PCMCIA_DEVICE_PROD_ID12("HITACHI", "microdrive", 0xf4f43949, 0xa6d76178),
|
||||
PCMCIA_DEVICE_PROD_ID12("IBM", "microdrive", 0xb569a6e5, 0xa6d76178),
|
||||
PCMCIA_DEVICE_PROD_ID12("IBM", "IBM17JSSFP20", 0xb569a6e5, 0xf2508753),
|
||||
PCMCIA_DEVICE_PROD_ID12("IO DATA", "CBIDE2 ", 0x547e66dc, 0x8671043b),
|
||||
PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDE", 0x547e66dc, 0x5c5ab149),
|
||||
PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDEII", 0x547e66dc, 0xb3662674),
|
||||
PCMCIA_DEVICE_PROD_ID12("LOOKMEET", "CBIDE2 ", 0xe37be2b5, 0x8671043b),
|
||||
PCMCIA_DEVICE_PROD_ID12("M-Systems", "CF500", 0x7ed2ad87, 0x7a13045c),
|
||||
PCMCIA_DEVICE_PROD_ID2("NinjaATA-", 0xebe0bd79),
|
||||
PCMCIA_DEVICE_PROD_ID12("PCMCIA", "CD-ROM", 0x281f1c5d, 0x66536591),
|
||||
PCMCIA_DEVICE_PROD_ID12("PCMCIA", "PnPIDE", 0x281f1c5d, 0x0c694728),
|
||||
PCMCIA_DEVICE_PROD_ID12("SHUTTLE TECHNOLOGY LTD.", "PCCARD-IDE/ATAPI Adapter", 0x4a3f0ba0, 0x322560e1),
|
||||
PCMCIA_DEVICE_PROD_ID12("SEAGATE", "ST1", 0x87c1b330, 0xe1f30883),
|
||||
PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "04/05/06", 0x43d74cb4, 0x6a22777d),
|
||||
PCMCIA_DEVICE_PROD_ID12("SMI VENDOR", "SMI PRODUCT", 0x30896c92, 0x703cc5f6),
|
||||
PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "MK2001MPL", 0xb4585a1a, 0x3489e003),
|
||||
PCMCIA_DEVICE_PROD_ID1("TRANSCEND 512M ", 0xd0909443),
|
||||
PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS1GCF80", 0x709b1bf1, 0x2a54d4b1),
|
||||
PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF120", 0x709b1bf1, 0xf54a91c8),
|
||||
PCMCIA_DEVICE_PROD_ID12("WIT", "IDE16", 0x244e5994, 0x3e232852),
|
||||
PCMCIA_DEVICE_PROD_ID12("WEIDA", "TWTTI", 0xcc7cf69c, 0x212bb918),
|
||||
PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209),
|
||||
PCMCIA_DEVICE_PROD_ID12("STI", "Flash 5.0", 0xbf2df18d, 0x8cb57a0e),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(1, "SanDisk", "ConnectPlus", 0x7a954bd9, 0x74be00c6),
|
||||
PCMCIA_DEVICE_NULL,
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, ide_ids);
|
||||
|
||||
static struct pcmcia_driver ide_cs_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.drv = {
|
||||
.name = "ide-cs",
|
||||
},
|
||||
.probe = ide_probe,
|
||||
.remove = ide_detach,
|
||||
.id_table = ide_ids,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
late_initcall(init_ide_cs);
|
||||
module_exit(exit_ide_cs);
|
||||
154
drivers/ide/legacy/macide.c
Normal file
154
drivers/ide/legacy/macide.c
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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/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)
|
||||
{
|
||||
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 = 1;
|
||||
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);
|
||||
}
|
||||
}
|
||||
151
drivers/ide/legacy/q40ide.c
Normal file
151
drivers/ide/legacy/q40ide.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
memset(hw, 0, sizeof(hw_regs_t));
|
||||
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 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
516
drivers/ide/legacy/qd65xx.c
Normal file
516
drivers/ide/legacy/qd65xx.c
Normal file
@@ -0,0 +1,516 @@
|
||||
/*
|
||||
* 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 "hda=autotune hdb=autotune"
|
||||
* To enable 2nd channel tuning (qd6580 only), use "hdc=autotune hdd=autotune"
|
||||
*/
|
||||
|
||||
/*
|
||||
* Rewritten from the work of Colten Edwards <pje120@cs.usask.ca> by
|
||||
* Samuel Thibault <samuel.thibault@fnac.net>
|
||||
*/
|
||||
|
||||
#include <linux/module.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;
|
||||
}
|
||||
|
||||
int probe_qd65xx = 0;
|
||||
|
||||
module_param_named(probe, probe_qd65xx, bool, 0);
|
||||
MODULE_PARM_DESC(probe, "probe for QD65xx chipsets");
|
||||
|
||||
/* Can be called directly from ide.c. */
|
||||
int __init qd65xx_init(void)
|
||||
{
|
||||
if (probe_qd65xx == 0)
|
||||
return -ENODEV;
|
||||
|
||||
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
Normal file
140
drivers/ide/legacy/qd65xx.h
Normal file
@@ -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 */
|
||||
};
|
||||
191
drivers/ide/legacy/umc8672.c
Normal file
191
drivers/ide/legacy/umc8672.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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/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;
|
||||
}
|
||||
|
||||
int probe_umc8672 = 0;
|
||||
|
||||
module_param_named(probe, probe_umc8672, bool, 0);
|
||||
MODULE_PARM_DESC(probe, "probe for UMC8672 chipset");
|
||||
|
||||
/* Can be called directly from ide.c. */
|
||||
int __init umc8672_init(void)
|
||||
{
|
||||
if (probe_umc8672 == 0)
|
||||
goto out;
|
||||
|
||||
if (umc8672_probe() == 0)
|
||||
return 0;;
|
||||
out:
|
||||
return -ENODEV;;
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
module_init(umc8672_init);
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Wolfram Podien");
|
||||
MODULE_DESCRIPTION("Support for UMC 8672 IDE chipset");
|
||||
MODULE_LICENSE("GPL");
|
||||
4
drivers/ide/mips/Makefile
Normal file
4
drivers/ide/mips/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
obj-$(CONFIG_BLK_DEV_IDE_SWARM) += swarm.o
|
||||
obj-$(CONFIG_BLK_DEV_IDE_AU1XXX) += au1xxx-ide.o
|
||||
|
||||
EXTRA_CFLAGS := -Idrivers/ide
|
||||
809
drivers/ide/mips/au1xxx-ide.c
Normal file
809
drivers/ide/mips/au1xxx-ide.c
Normal file
@@ -0,0 +1,809 @@
|
||||
/*
|
||||
* linux/drivers/ide/mips/au1xxx-ide.c version 01.30.00 Aug. 02 2005
|
||||
*
|
||||
* BRIEF MODULE DESCRIPTION
|
||||
* AMD Alchemy Au1xxx IDE interface routines over the Static Bus
|
||||
*
|
||||
* Copyright (c) 2003-2005 AMD, Personal Connectivity Solutions
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Note: for more information, please refer "AMD Alchemy Au1200/Au1550 IDE
|
||||
* Interface and Linux Device Driver" Application Note.
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/sysdev.h>
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "ide-timing.h"
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/mach-au1x00/au1xxx.h>
|
||||
#include <asm/mach-au1x00/au1xxx_dbdma.h>
|
||||
|
||||
#include <asm/mach-au1x00/au1xxx_ide.h>
|
||||
|
||||
#define DRV_NAME "au1200-ide"
|
||||
#define DRV_VERSION "1.0"
|
||||
#define DRV_AUTHOR "Enrico Walther <enrico.walther@amd.com> / Pete Popov <ppopov@embeddedalley.com>"
|
||||
|
||||
/* enable the burstmode in the dbdma */
|
||||
#define IDE_AU1XXX_BURSTMODE 1
|
||||
|
||||
static _auide_hwif auide_hwif;
|
||||
static int dbdma_init_done;
|
||||
|
||||
#if defined(CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA)
|
||||
|
||||
void auide_insw(unsigned long port, void *addr, u32 count)
|
||||
{
|
||||
_auide_hwif *ahwif = &auide_hwif;
|
||||
chan_tab_t *ctp;
|
||||
au1x_ddma_desc_t *dp;
|
||||
|
||||
if(!put_dest_flags(ahwif->rx_chan, (void*)addr, count << 1,
|
||||
DDMA_FLAGS_NOIE)) {
|
||||
printk(KERN_ERR "%s failed %d\n", __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
ctp = *((chan_tab_t **)ahwif->rx_chan);
|
||||
dp = ctp->cur_ptr;
|
||||
while (dp->dscr_cmd0 & DSCR_CMD0_V)
|
||||
;
|
||||
ctp->cur_ptr = au1xxx_ddma_get_nextptr_virt(dp);
|
||||
}
|
||||
|
||||
void auide_outsw(unsigned long port, void *addr, u32 count)
|
||||
{
|
||||
_auide_hwif *ahwif = &auide_hwif;
|
||||
chan_tab_t *ctp;
|
||||
au1x_ddma_desc_t *dp;
|
||||
|
||||
if(!put_source_flags(ahwif->tx_chan, (void*)addr,
|
||||
count << 1, DDMA_FLAGS_NOIE)) {
|
||||
printk(KERN_ERR "%s failed %d\n", __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
ctp = *((chan_tab_t **)ahwif->tx_chan);
|
||||
dp = ctp->cur_ptr;
|
||||
while (dp->dscr_cmd0 & DSCR_CMD0_V)
|
||||
;
|
||||
ctp->cur_ptr = au1xxx_ddma_get_nextptr_virt(dp);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void auide_tune_drive(ide_drive_t *drive, byte pio)
|
||||
{
|
||||
int mem_sttime;
|
||||
int mem_stcfg;
|
||||
u8 speed;
|
||||
|
||||
/* get the best pio mode for the drive */
|
||||
pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
|
||||
|
||||
printk(KERN_INFO "%s: setting Au1XXX IDE to PIO mode%d\n",
|
||||
drive->name, pio);
|
||||
|
||||
mem_sttime = 0;
|
||||
mem_stcfg = au_readl(MEM_STCFG2);
|
||||
|
||||
/* set pio mode! */
|
||||
switch(pio) {
|
||||
case 0:
|
||||
mem_sttime = SBC_IDE_TIMING(PIO0);
|
||||
|
||||
/* set configuration for RCS2# */
|
||||
mem_stcfg |= TS_MASK;
|
||||
mem_stcfg &= ~TCSOE_MASK;
|
||||
mem_stcfg &= ~TOECS_MASK;
|
||||
mem_stcfg |= SBC_IDE_PIO0_TCSOE | SBC_IDE_PIO0_TOECS;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
mem_sttime = SBC_IDE_TIMING(PIO1);
|
||||
|
||||
/* set configuration for RCS2# */
|
||||
mem_stcfg |= TS_MASK;
|
||||
mem_stcfg &= ~TCSOE_MASK;
|
||||
mem_stcfg &= ~TOECS_MASK;
|
||||
mem_stcfg |= SBC_IDE_PIO1_TCSOE | SBC_IDE_PIO1_TOECS;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
mem_sttime = SBC_IDE_TIMING(PIO2);
|
||||
|
||||
/* set configuration for RCS2# */
|
||||
mem_stcfg &= ~TS_MASK;
|
||||
mem_stcfg &= ~TCSOE_MASK;
|
||||
mem_stcfg &= ~TOECS_MASK;
|
||||
mem_stcfg |= SBC_IDE_PIO2_TCSOE | SBC_IDE_PIO2_TOECS;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
mem_sttime = SBC_IDE_TIMING(PIO3);
|
||||
|
||||
/* set configuration for RCS2# */
|
||||
mem_stcfg &= ~TS_MASK;
|
||||
mem_stcfg &= ~TCSOE_MASK;
|
||||
mem_stcfg &= ~TOECS_MASK;
|
||||
mem_stcfg |= SBC_IDE_PIO3_TCSOE | SBC_IDE_PIO3_TOECS;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
mem_sttime = SBC_IDE_TIMING(PIO4);
|
||||
|
||||
/* set configuration for RCS2# */
|
||||
mem_stcfg &= ~TS_MASK;
|
||||
mem_stcfg &= ~TCSOE_MASK;
|
||||
mem_stcfg &= ~TOECS_MASK;
|
||||
mem_stcfg |= SBC_IDE_PIO4_TCSOE | SBC_IDE_PIO4_TOECS;
|
||||
break;
|
||||
}
|
||||
|
||||
au_writel(mem_sttime,MEM_STTIME2);
|
||||
au_writel(mem_stcfg,MEM_STCFG2);
|
||||
|
||||
speed = pio + XFER_PIO_0;
|
||||
ide_config_drive_speed(drive, speed);
|
||||
}
|
||||
|
||||
static int auide_tune_chipset (ide_drive_t *drive, u8 speed)
|
||||
{
|
||||
int mem_sttime;
|
||||
int mem_stcfg;
|
||||
|
||||
mem_sttime = 0;
|
||||
mem_stcfg = au_readl(MEM_STCFG2);
|
||||
|
||||
if (speed >= XFER_PIO_0 && speed <= XFER_PIO_4) {
|
||||
auide_tune_drive(drive, speed - XFER_PIO_0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch(speed) {
|
||||
#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
|
||||
case XFER_MW_DMA_2:
|
||||
mem_sttime = SBC_IDE_TIMING(MDMA2);
|
||||
|
||||
/* set configuration for RCS2# */
|
||||
mem_stcfg &= ~TS_MASK;
|
||||
mem_stcfg &= ~TCSOE_MASK;
|
||||
mem_stcfg &= ~TOECS_MASK;
|
||||
mem_stcfg |= SBC_IDE_MDMA2_TCSOE | SBC_IDE_MDMA2_TOECS;
|
||||
|
||||
break;
|
||||
case XFER_MW_DMA_1:
|
||||
mem_sttime = SBC_IDE_TIMING(MDMA1);
|
||||
|
||||
/* set configuration for RCS2# */
|
||||
mem_stcfg &= ~TS_MASK;
|
||||
mem_stcfg &= ~TCSOE_MASK;
|
||||
mem_stcfg &= ~TOECS_MASK;
|
||||
mem_stcfg |= SBC_IDE_MDMA1_TCSOE | SBC_IDE_MDMA1_TOECS;
|
||||
|
||||
break;
|
||||
case XFER_MW_DMA_0:
|
||||
mem_sttime = SBC_IDE_TIMING(MDMA0);
|
||||
|
||||
/* set configuration for RCS2# */
|
||||
mem_stcfg |= TS_MASK;
|
||||
mem_stcfg &= ~TCSOE_MASK;
|
||||
mem_stcfg &= ~TOECS_MASK;
|
||||
mem_stcfg |= SBC_IDE_MDMA0_TCSOE | SBC_IDE_MDMA0_TOECS;
|
||||
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ide_config_drive_speed(drive, speed))
|
||||
return 1;
|
||||
|
||||
au_writel(mem_sttime,MEM_STTIME2);
|
||||
au_writel(mem_stcfg,MEM_STCFG2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Multi-Word DMA + DbDMA functions
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
|
||||
|
||||
static int auide_build_sglist(ide_drive_t *drive, struct request *rq)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
_auide_hwif *ahwif = (_auide_hwif*)hwif->hwif_data;
|
||||
struct scatterlist *sg = hwif->sg_table;
|
||||
|
||||
ide_map_sg(drive, rq);
|
||||
|
||||
if (rq_data_dir(rq) == READ)
|
||||
hwif->sg_dma_direction = DMA_FROM_DEVICE;
|
||||
else
|
||||
hwif->sg_dma_direction = DMA_TO_DEVICE;
|
||||
|
||||
return dma_map_sg(ahwif->dev, sg, hwif->sg_nents,
|
||||
hwif->sg_dma_direction);
|
||||
}
|
||||
|
||||
static int auide_build_dmatable(ide_drive_t *drive)
|
||||
{
|
||||
int i, iswrite, count = 0;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
|
||||
struct request *rq = HWGROUP(drive)->rq;
|
||||
|
||||
_auide_hwif *ahwif = (_auide_hwif*)hwif->hwif_data;
|
||||
struct scatterlist *sg;
|
||||
|
||||
iswrite = (rq_data_dir(rq) == WRITE);
|
||||
/* Save for interrupt context */
|
||||
ahwif->drive = drive;
|
||||
|
||||
/* Build sglist */
|
||||
hwif->sg_nents = i = auide_build_sglist(drive, rq);
|
||||
|
||||
if (!i)
|
||||
return 0;
|
||||
|
||||
/* fill the descriptors */
|
||||
sg = hwif->sg_table;
|
||||
while (i && sg_dma_len(sg)) {
|
||||
u32 cur_addr;
|
||||
u32 cur_len;
|
||||
|
||||
cur_addr = sg_dma_address(sg);
|
||||
cur_len = sg_dma_len(sg);
|
||||
|
||||
while (cur_len) {
|
||||
u32 flags = DDMA_FLAGS_NOIE;
|
||||
unsigned int tc = (cur_len < 0xfe00)? cur_len: 0xfe00;
|
||||
|
||||
if (++count >= PRD_ENTRIES) {
|
||||
printk(KERN_WARNING "%s: DMA table too small\n",
|
||||
drive->name);
|
||||
goto use_pio_instead;
|
||||
}
|
||||
|
||||
/* Lets enable intr for the last descriptor only */
|
||||
if (1==i)
|
||||
flags = DDMA_FLAGS_IE;
|
||||
else
|
||||
flags = DDMA_FLAGS_NOIE;
|
||||
|
||||
if (iswrite) {
|
||||
if(!put_source_flags(ahwif->tx_chan,
|
||||
(void*)(page_address(sg->page)
|
||||
+ sg->offset),
|
||||
tc, flags)) {
|
||||
printk(KERN_ERR "%s failed %d\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
} else
|
||||
{
|
||||
if(!put_dest_flags(ahwif->rx_chan,
|
||||
(void*)(page_address(sg->page)
|
||||
+ sg->offset),
|
||||
tc, flags)) {
|
||||
printk(KERN_ERR "%s failed %d\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
cur_addr += tc;
|
||||
cur_len -= tc;
|
||||
}
|
||||
sg++;
|
||||
i--;
|
||||
}
|
||||
|
||||
if (count)
|
||||
return 1;
|
||||
|
||||
use_pio_instead:
|
||||
dma_unmap_sg(ahwif->dev,
|
||||
hwif->sg_table,
|
||||
hwif->sg_nents,
|
||||
hwif->sg_dma_direction);
|
||||
|
||||
return 0; /* revert to PIO for this request */
|
||||
}
|
||||
|
||||
static int auide_dma_end(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
_auide_hwif *ahwif = (_auide_hwif*)hwif->hwif_data;
|
||||
|
||||
if (hwif->sg_nents) {
|
||||
dma_unmap_sg(ahwif->dev, hwif->sg_table, hwif->sg_nents,
|
||||
hwif->sg_dma_direction);
|
||||
hwif->sg_nents = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void auide_dma_start(ide_drive_t *drive )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static void auide_dma_exec_cmd(ide_drive_t *drive, u8 command)
|
||||
{
|
||||
/* issue cmd to drive */
|
||||
ide_execute_command(drive, command, &ide_dma_intr,
|
||||
(2*WAIT_CMD), NULL);
|
||||
}
|
||||
|
||||
static int auide_dma_setup(ide_drive_t *drive)
|
||||
{
|
||||
struct request *rq = HWGROUP(drive)->rq;
|
||||
|
||||
if (!auide_build_dmatable(drive)) {
|
||||
ide_map_sg(drive, rq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
drive->waiting_for_dma = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auide_dma_check(ide_drive_t *drive)
|
||||
{
|
||||
u8 speed;
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
|
||||
|
||||
if( dbdma_init_done == 0 ){
|
||||
auide_hwif.white_list = ide_in_drive_list(drive->id,
|
||||
dma_white_list);
|
||||
auide_hwif.black_list = ide_in_drive_list(drive->id,
|
||||
dma_black_list);
|
||||
auide_hwif.drive = drive;
|
||||
auide_ddma_init(&auide_hwif);
|
||||
dbdma_init_done = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Is the drive in our DMA black list? */
|
||||
|
||||
if ( auide_hwif.black_list ) {
|
||||
drive->using_dma = 0;
|
||||
|
||||
/* Borrowed the warning message from ide-dma.c */
|
||||
|
||||
printk(KERN_WARNING "%s: Disabling DMA for %s (blacklisted)\n",
|
||||
drive->name, drive->id->model);
|
||||
}
|
||||
else
|
||||
drive->using_dma = 1;
|
||||
|
||||
speed = ide_find_best_mode(drive, XFER_PIO | XFER_MWDMA);
|
||||
|
||||
if (drive->autodma && (speed & XFER_MODE) != XFER_PIO)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int auide_dma_test_irq(ide_drive_t *drive)
|
||||
{
|
||||
if (drive->waiting_for_dma == 0)
|
||||
printk(KERN_WARNING "%s: ide_dma_test_irq \
|
||||
called while not waiting\n", drive->name);
|
||||
|
||||
/* If dbdma didn't execute the STOP command yet, the
|
||||
* active bit is still set
|
||||
*/
|
||||
drive->waiting_for_dma++;
|
||||
if (drive->waiting_for_dma >= DMA_WAIT_TIMEOUT) {
|
||||
printk(KERN_WARNING "%s: timeout waiting for ddma to \
|
||||
complete\n", drive->name);
|
||||
return 1;
|
||||
}
|
||||
udelay(10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void auide_dma_host_on(ide_drive_t *drive)
|
||||
{
|
||||
}
|
||||
|
||||
static int auide_dma_on(ide_drive_t *drive)
|
||||
{
|
||||
drive->using_dma = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void auide_dma_host_off(ide_drive_t *drive)
|
||||
{
|
||||
}
|
||||
|
||||
static void auide_dma_off_quietly(ide_drive_t *drive)
|
||||
{
|
||||
drive->using_dma = 0;
|
||||
}
|
||||
|
||||
static int auide_dma_lostirq(ide_drive_t *drive)
|
||||
{
|
||||
printk(KERN_ERR "%s: IRQ lost\n", drive->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void auide_ddma_tx_callback(int irq, void *param)
|
||||
{
|
||||
_auide_hwif *ahwif = (_auide_hwif*)param;
|
||||
ahwif->drive->waiting_for_dma = 0;
|
||||
}
|
||||
|
||||
static void auide_ddma_rx_callback(int irq, void *param)
|
||||
{
|
||||
_auide_hwif *ahwif = (_auide_hwif*)param;
|
||||
ahwif->drive->waiting_for_dma = 0;
|
||||
}
|
||||
|
||||
#endif /* end CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA */
|
||||
|
||||
static void auide_init_dbdma_dev(dbdev_tab_t *dev, u32 dev_id, u32 tsize, u32 devwidth, u32 flags)
|
||||
{
|
||||
dev->dev_id = dev_id;
|
||||
dev->dev_physaddr = (u32)AU1XXX_ATA_PHYS_ADDR;
|
||||
dev->dev_intlevel = 0;
|
||||
dev->dev_intpolarity = 0;
|
||||
dev->dev_tsize = tsize;
|
||||
dev->dev_devwidth = devwidth;
|
||||
dev->dev_flags = flags;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA)
|
||||
|
||||
static int auide_dma_timeout(ide_drive_t *drive)
|
||||
{
|
||||
// printk("%s\n", __FUNCTION__);
|
||||
|
||||
printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name);
|
||||
|
||||
if (HWIF(drive)->ide_dma_test_irq(drive))
|
||||
return 0;
|
||||
|
||||
return HWIF(drive)->ide_dma_end(drive);
|
||||
}
|
||||
|
||||
|
||||
static int auide_ddma_init(_auide_hwif *auide) {
|
||||
|
||||
dbdev_tab_t source_dev_tab, target_dev_tab;
|
||||
u32 dev_id, tsize, devwidth, flags;
|
||||
ide_hwif_t *hwif = auide->hwif;
|
||||
|
||||
dev_id = AU1XXX_ATA_DDMA_REQ;
|
||||
|
||||
if (auide->white_list || auide->black_list) {
|
||||
tsize = 8;
|
||||
devwidth = 32;
|
||||
}
|
||||
else {
|
||||
tsize = 1;
|
||||
devwidth = 16;
|
||||
|
||||
printk(KERN_ERR "au1xxx-ide: %s is not on ide driver whitelist.\n",auide_hwif.drive->id->model);
|
||||
printk(KERN_ERR " please read 'Documentation/mips/AU1xxx_IDE.README'");
|
||||
}
|
||||
|
||||
#ifdef IDE_AU1XXX_BURSTMODE
|
||||
flags = DEV_FLAGS_SYNC | DEV_FLAGS_BURSTABLE;
|
||||
#else
|
||||
flags = DEV_FLAGS_SYNC;
|
||||
#endif
|
||||
|
||||
/* setup dev_tab for tx channel */
|
||||
auide_init_dbdma_dev( &source_dev_tab,
|
||||
dev_id,
|
||||
tsize, devwidth, DEV_FLAGS_OUT | flags);
|
||||
auide->tx_dev_id = au1xxx_ddma_add_device( &source_dev_tab );
|
||||
|
||||
auide_init_dbdma_dev( &source_dev_tab,
|
||||
dev_id,
|
||||
tsize, devwidth, DEV_FLAGS_IN | flags);
|
||||
auide->rx_dev_id = au1xxx_ddma_add_device( &source_dev_tab );
|
||||
|
||||
/* We also need to add a target device for the DMA */
|
||||
auide_init_dbdma_dev( &target_dev_tab,
|
||||
(u32)DSCR_CMD0_ALWAYS,
|
||||
tsize, devwidth, DEV_FLAGS_ANYUSE);
|
||||
auide->target_dev_id = au1xxx_ddma_add_device(&target_dev_tab);
|
||||
|
||||
/* Get a channel for TX */
|
||||
auide->tx_chan = au1xxx_dbdma_chan_alloc(auide->target_dev_id,
|
||||
auide->tx_dev_id,
|
||||
auide_ddma_tx_callback,
|
||||
(void*)auide);
|
||||
|
||||
/* Get a channel for RX */
|
||||
auide->rx_chan = au1xxx_dbdma_chan_alloc(auide->rx_dev_id,
|
||||
auide->target_dev_id,
|
||||
auide_ddma_rx_callback,
|
||||
(void*)auide);
|
||||
|
||||
auide->tx_desc_head = (void*)au1xxx_dbdma_ring_alloc(auide->tx_chan,
|
||||
NUM_DESCRIPTORS);
|
||||
auide->rx_desc_head = (void*)au1xxx_dbdma_ring_alloc(auide->rx_chan,
|
||||
NUM_DESCRIPTORS);
|
||||
|
||||
hwif->dmatable_cpu = dma_alloc_coherent(auide->dev,
|
||||
PRD_ENTRIES * PRD_BYTES, /* 1 Page */
|
||||
&hwif->dmatable_dma, GFP_KERNEL);
|
||||
|
||||
au1xxx_dbdma_start( auide->tx_chan );
|
||||
au1xxx_dbdma_start( auide->rx_chan );
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
||||
static int auide_ddma_init( _auide_hwif *auide )
|
||||
{
|
||||
dbdev_tab_t source_dev_tab;
|
||||
int flags;
|
||||
|
||||
#ifdef IDE_AU1XXX_BURSTMODE
|
||||
flags = DEV_FLAGS_SYNC | DEV_FLAGS_BURSTABLE;
|
||||
#else
|
||||
flags = DEV_FLAGS_SYNC;
|
||||
#endif
|
||||
|
||||
/* setup dev_tab for tx channel */
|
||||
auide_init_dbdma_dev( &source_dev_tab,
|
||||
(u32)DSCR_CMD0_ALWAYS,
|
||||
8, 32, DEV_FLAGS_OUT | flags);
|
||||
auide->tx_dev_id = au1xxx_ddma_add_device( &source_dev_tab );
|
||||
|
||||
auide_init_dbdma_dev( &source_dev_tab,
|
||||
(u32)DSCR_CMD0_ALWAYS,
|
||||
8, 32, DEV_FLAGS_IN | flags);
|
||||
auide->rx_dev_id = au1xxx_ddma_add_device( &source_dev_tab );
|
||||
|
||||
/* Get a channel for TX */
|
||||
auide->tx_chan = au1xxx_dbdma_chan_alloc(DSCR_CMD0_ALWAYS,
|
||||
auide->tx_dev_id,
|
||||
NULL,
|
||||
(void*)auide);
|
||||
|
||||
/* Get a channel for RX */
|
||||
auide->rx_chan = au1xxx_dbdma_chan_alloc(auide->rx_dev_id,
|
||||
DSCR_CMD0_ALWAYS,
|
||||
NULL,
|
||||
(void*)auide);
|
||||
|
||||
auide->tx_desc_head = (void*)au1xxx_dbdma_ring_alloc(auide->tx_chan,
|
||||
NUM_DESCRIPTORS);
|
||||
auide->rx_desc_head = (void*)au1xxx_dbdma_ring_alloc(auide->rx_chan,
|
||||
NUM_DESCRIPTORS);
|
||||
|
||||
au1xxx_dbdma_start( auide->tx_chan );
|
||||
au1xxx_dbdma_start( auide->rx_chan );
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void auide_setup_ports(hw_regs_t *hw, _auide_hwif *ahwif)
|
||||
{
|
||||
int i;
|
||||
unsigned long *ata_regs = hw->io_ports;
|
||||
|
||||
/* FIXME? */
|
||||
for (i = 0; i < IDE_CONTROL_OFFSET; i++) {
|
||||
*ata_regs++ = ahwif->regbase + (i << AU1XXX_ATA_REG_OFFSET);
|
||||
}
|
||||
|
||||
/* set the Alternative Status register */
|
||||
*ata_regs = ahwif->regbase + (14 << AU1XXX_ATA_REG_OFFSET);
|
||||
}
|
||||
|
||||
static int au_ide_probe(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
_auide_hwif *ahwif = &auide_hwif;
|
||||
ide_hwif_t *hwif;
|
||||
struct resource *res;
|
||||
hw_regs_t *hw;
|
||||
int ret = 0;
|
||||
|
||||
#if defined(CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA)
|
||||
char *mode = "MWDMA2";
|
||||
#elif defined(CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA)
|
||||
char *mode = "PIO+DDMA(offload)";
|
||||
#endif
|
||||
|
||||
memset(&auide_hwif, 0, sizeof(_auide_hwif));
|
||||
auide_hwif.dev = 0;
|
||||
|
||||
ahwif->dev = dev;
|
||||
ahwif->irq = platform_get_irq(pdev, 0);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
if (res == NULL) {
|
||||
pr_debug("%s %d: no base address\n", DRV_NAME, pdev->id);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (ahwif->irq < 0) {
|
||||
pr_debug("%s %d: no IRQ\n", DRV_NAME, pdev->id);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!request_mem_region (res->start, res->end-res->start, pdev->name)) {
|
||||
pr_debug("%s: request_mem_region failed\n", DRV_NAME);
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ahwif->regbase = (u32)ioremap(res->start, res->end-res->start);
|
||||
if (ahwif->regbase == 0) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* FIXME: This might possibly break PCMCIA IDE devices */
|
||||
|
||||
hwif = &ide_hwifs[pdev->id];
|
||||
hw = &hwif->hw;
|
||||
hwif->irq = hw->irq = ahwif->irq;
|
||||
hwif->chipset = ide_au1xxx;
|
||||
|
||||
auide_setup_ports(hw, ahwif);
|
||||
memcpy(hwif->io_ports, hw->io_ports, sizeof(hwif->io_ports));
|
||||
|
||||
hwif->ultra_mask = 0x0; /* Disable Ultra DMA */
|
||||
#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
|
||||
hwif->mwdma_mask = 0x07; /* Multimode-2 DMA */
|
||||
hwif->swdma_mask = 0x00;
|
||||
#else
|
||||
hwif->mwdma_mask = 0x0;
|
||||
hwif->swdma_mask = 0x0;
|
||||
#endif
|
||||
|
||||
hwif->noprobe = 0;
|
||||
hwif->drives[0].unmask = 1;
|
||||
hwif->drives[1].unmask = 1;
|
||||
|
||||
/* hold should be on in all cases */
|
||||
hwif->hold = 1;
|
||||
|
||||
hwif->mmio = 1;
|
||||
|
||||
/* If the user has selected DDMA assisted copies,
|
||||
then set up a few local I/O function entry points
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA
|
||||
hwif->INSW = auide_insw;
|
||||
hwif->OUTSW = auide_outsw;
|
||||
#endif
|
||||
|
||||
hwif->tuneproc = &auide_tune_drive;
|
||||
hwif->speedproc = &auide_tune_chipset;
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
|
||||
hwif->dma_off_quietly = &auide_dma_off_quietly;
|
||||
hwif->ide_dma_timeout = &auide_dma_timeout;
|
||||
|
||||
hwif->ide_dma_check = &auide_dma_check;
|
||||
hwif->dma_exec_cmd = &auide_dma_exec_cmd;
|
||||
hwif->dma_start = &auide_dma_start;
|
||||
hwif->ide_dma_end = &auide_dma_end;
|
||||
hwif->dma_setup = &auide_dma_setup;
|
||||
hwif->ide_dma_test_irq = &auide_dma_test_irq;
|
||||
hwif->dma_host_off = &auide_dma_host_off;
|
||||
hwif->dma_host_on = &auide_dma_host_on;
|
||||
hwif->ide_dma_lostirq = &auide_dma_lostirq;
|
||||
hwif->ide_dma_on = &auide_dma_on;
|
||||
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
hwif->atapi_dma = 1;
|
||||
|
||||
#else /* !CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA */
|
||||
hwif->autodma = 0;
|
||||
hwif->channel = 0;
|
||||
hwif->hold = 1;
|
||||
hwif->select_data = 0; /* no chipset-specific code */
|
||||
hwif->config_data = 0; /* no chipset-specific code */
|
||||
|
||||
hwif->drives[0].autodma = 0;
|
||||
hwif->drives[0].autotune = 1; /* 1=autotune, 2=noautotune, 0=default */
|
||||
#endif
|
||||
hwif->drives[0].no_io_32bit = 1;
|
||||
|
||||
auide_hwif.hwif = hwif;
|
||||
hwif->hwif_data = &auide_hwif;
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA
|
||||
auide_ddma_init(&auide_hwif);
|
||||
dbdma_init_done = 1;
|
||||
#endif
|
||||
|
||||
probe_hwif_init(hwif);
|
||||
dev_set_drvdata(dev, hwif);
|
||||
|
||||
printk(KERN_INFO "Au1xxx IDE(builtin) configured for %s\n", mode );
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int au_ide_remove(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct resource *res;
|
||||
ide_hwif_t *hwif = dev_get_drvdata(dev);
|
||||
_auide_hwif *ahwif = &auide_hwif;
|
||||
|
||||
ide_unregister(hwif - ide_hwifs);
|
||||
|
||||
iounmap((void *)ahwif->regbase);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(res->start, res->end - res->start);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device_driver au1200_ide_driver = {
|
||||
.name = "au1200-ide",
|
||||
.bus = &platform_bus_type,
|
||||
.probe = au_ide_probe,
|
||||
.remove = au_ide_remove,
|
||||
};
|
||||
|
||||
static int __init au_ide_init(void)
|
||||
{
|
||||
return driver_register(&au1200_ide_driver);
|
||||
}
|
||||
|
||||
static void __exit au_ide_exit(void)
|
||||
{
|
||||
driver_unregister(&au1200_ide_driver);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("AU1200 IDE driver");
|
||||
|
||||
module_init(au_ide_init);
|
||||
module_exit(au_ide_exit);
|
||||
203
drivers/ide/mips/swarm.c
Normal file
203
drivers/ide/mips/swarm.c
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) 2001, 2002, 2003 Broadcom Corporation
|
||||
* Copyright (C) 2004 MontaVista Software Inc.
|
||||
* Author: Manish Lachwani, mlachwani@mvista.com
|
||||
* Copyright (C) 2004 MIPS Technologies, Inc. All rights reserved.
|
||||
* Author: Maciej W. Rozycki <macro@mips.com>
|
||||
* Copyright (c) 2006 Maciej W. Rozycki
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Derived loosely from ide-pmac.c, so:
|
||||
* Copyright (C) 1998 Paul Mackerras.
|
||||
* Copyright (C) 1995-1998 Mark Lord
|
||||
*/
|
||||
|
||||
/*
|
||||
* Boards with SiByte processors so far have supported IDE devices via
|
||||
* the Generic Bus, PCI bus, and built-in PCMCIA interface. In all
|
||||
* cases, byte-swapping must be avoided for these devices (whereas
|
||||
* other PCI devices, for example, will require swapping). Any
|
||||
* SiByte-targetted kernel including IDE support will include this
|
||||
* file. Probing of a Generic Bus for an IDE device is controlled by
|
||||
* the definition of "SIBYTE_HAVE_IDE", which is provided by
|
||||
* <asm/sibyte/board.h> for Broadcom boards.
|
||||
*/
|
||||
|
||||
#include <linux/ide.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <asm/sibyte/board.h>
|
||||
#include <asm/sibyte/sb1250_genbus.h>
|
||||
#include <asm/sibyte/sb1250_regs.h>
|
||||
|
||||
#define DRV_NAME "ide-swarm"
|
||||
|
||||
static char swarm_ide_string[] = DRV_NAME;
|
||||
|
||||
static struct resource swarm_ide_resource = {
|
||||
.name = "SWARM GenBus IDE",
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
static struct platform_device *swarm_ide_dev;
|
||||
|
||||
/*
|
||||
* swarm_ide_probe - if the board header indicates the existence of
|
||||
* Generic Bus IDE, allocate a HWIF for it.
|
||||
*/
|
||||
static int __devinit swarm_ide_probe(struct device *dev)
|
||||
{
|
||||
ide_hwif_t *hwif;
|
||||
u8 __iomem *base;
|
||||
phys_t offset, size;
|
||||
int i;
|
||||
|
||||
if (!SIBYTE_HAVE_IDE)
|
||||
return -ENODEV;
|
||||
|
||||
/* Find an empty slot. */
|
||||
for (i = 0; i < MAX_HWIFS; i++)
|
||||
if (!ide_hwifs[i].io_ports[IDE_DATA_OFFSET])
|
||||
break;
|
||||
if (i >= MAX_HWIFS) {
|
||||
printk(KERN_ERR DRV_NAME ": no free slot for interface\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hwif = ide_hwifs + i;
|
||||
|
||||
base = ioremap(A_IO_EXT_BASE, 0x800);
|
||||
offset = __raw_readq(base + R_IO_EXT_REG(R_IO_EXT_START_ADDR, IDE_CS));
|
||||
size = __raw_readq(base + R_IO_EXT_REG(R_IO_EXT_MULT_SIZE, IDE_CS));
|
||||
iounmap(base);
|
||||
|
||||
offset = G_IO_START_ADDR(offset) << S_IO_ADDRBASE;
|
||||
size = (G_IO_MULT_SIZE(size) + 1) << S_IO_REGSIZE;
|
||||
if (offset < A_PHYS_GENBUS || offset >= A_PHYS_GENBUS_END) {
|
||||
printk(KERN_INFO DRV_NAME
|
||||
": IDE interface at GenBus disabled\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
printk(KERN_INFO DRV_NAME ": IDE interface at GenBus slot %i\n",
|
||||
IDE_CS);
|
||||
|
||||
swarm_ide_resource.start = offset;
|
||||
swarm_ide_resource.end = offset + size - 1;
|
||||
if (request_resource(&iomem_resource, &swarm_ide_resource)) {
|
||||
printk(KERN_ERR DRV_NAME
|
||||
": can't request I/O memory resource\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
base = ioremap(offset, size);
|
||||
|
||||
/* Setup MMIO ops. */
|
||||
default_hwif_mmiops(hwif);
|
||||
/* Prevent resource map manipulation. */
|
||||
hwif->mmio = 1;
|
||||
hwif->noprobe = 0;
|
||||
|
||||
for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++)
|
||||
hwif->hw.io_ports[i] =
|
||||
(unsigned long)(base + ((0x1f0 + i) << 5));
|
||||
hwif->hw.io_ports[IDE_CONTROL_OFFSET] =
|
||||
(unsigned long)(base + (0x3f6 << 5));
|
||||
hwif->hw.irq = K_INT_GB_IDE;
|
||||
|
||||
memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
|
||||
hwif->irq = hwif->hw.irq;
|
||||
|
||||
probe_hwif_init(hwif);
|
||||
dev_set_drvdata(dev, hwif);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device_driver swarm_ide_driver = {
|
||||
.name = swarm_ide_string,
|
||||
.bus = &platform_bus_type,
|
||||
.probe = swarm_ide_probe,
|
||||
};
|
||||
|
||||
static void swarm_ide_platform_release(struct device *device)
|
||||
{
|
||||
struct platform_device *pldev;
|
||||
|
||||
/* free device */
|
||||
pldev = to_platform_device(device);
|
||||
kfree(pldev);
|
||||
}
|
||||
|
||||
static int __devinit swarm_ide_init_module(void)
|
||||
{
|
||||
struct platform_device *pldev;
|
||||
int err;
|
||||
|
||||
printk(KERN_INFO "SWARM IDE driver\n");
|
||||
|
||||
if (driver_register(&swarm_ide_driver)) {
|
||||
printk(KERN_ERR "Driver registration failed\n");
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(pldev = kmalloc(sizeof (*pldev), GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
goto out_unregister_driver;
|
||||
}
|
||||
|
||||
memset (pldev, 0, sizeof (*pldev));
|
||||
pldev->name = swarm_ide_string;
|
||||
pldev->id = 0;
|
||||
pldev->dev.release = swarm_ide_platform_release;
|
||||
|
||||
if (platform_device_register(pldev)) {
|
||||
err = -ENODEV;
|
||||
goto out_free_pldev;
|
||||
}
|
||||
|
||||
if (!pldev->dev.driver) {
|
||||
/*
|
||||
* The driver was not bound to this device, there was
|
||||
* no hardware at this address. Unregister it, as the
|
||||
* release fuction will take care of freeing the
|
||||
* allocated structure
|
||||
*/
|
||||
platform_device_unregister (pldev);
|
||||
}
|
||||
|
||||
swarm_ide_dev = pldev;
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_pldev:
|
||||
kfree(pldev);
|
||||
|
||||
out_unregister_driver:
|
||||
driver_unregister(&swarm_ide_driver);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
module_init(swarm_ide_init_module);
|
||||
39
drivers/ide/pci/Makefile
Normal file
39
drivers/ide/pci/Makefile
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
obj-$(CONFIG_BLK_DEV_AEC62XX) += aec62xx.o
|
||||
obj-$(CONFIG_BLK_DEV_ALI15X3) += alim15x3.o
|
||||
obj-$(CONFIG_BLK_DEV_AMD74XX) += amd74xx.o
|
||||
obj-$(CONFIG_BLK_DEV_ATIIXP) += atiixp.o
|
||||
obj-$(CONFIG_BLK_DEV_CELLEB) += scc_pata.o
|
||||
obj-$(CONFIG_BLK_DEV_CMD64X) += cmd64x.o
|
||||
obj-$(CONFIG_BLK_DEV_CS5520) += cs5520.o
|
||||
obj-$(CONFIG_BLK_DEV_CS5530) += cs5530.o
|
||||
obj-$(CONFIG_BLK_DEV_CS5535) += cs5535.o
|
||||
obj-$(CONFIG_BLK_DEV_SC1200) += sc1200.o
|
||||
obj-$(CONFIG_BLK_DEV_CY82C693) += cy82c693.o
|
||||
obj-$(CONFIG_BLK_DEV_DELKIN) += delkin_cb.o
|
||||
obj-$(CONFIG_BLK_DEV_HPT34X) += hpt34x.o
|
||||
obj-$(CONFIG_BLK_DEV_HPT366) += hpt366.o
|
||||
obj-$(CONFIG_BLK_DEV_IT8213) += it8213.o
|
||||
obj-$(CONFIG_BLK_DEV_IT821X) += it821x.o
|
||||
obj-$(CONFIG_BLK_DEV_JMICRON) += jmicron.o
|
||||
obj-$(CONFIG_BLK_DEV_NS87415) += ns87415.o
|
||||
obj-$(CONFIG_BLK_DEV_OPTI621) += opti621.o
|
||||
obj-$(CONFIG_BLK_DEV_PDC202XX_OLD) += pdc202xx_old.o
|
||||
obj-$(CONFIG_BLK_DEV_PDC202XX_NEW) += pdc202xx_new.o
|
||||
obj-$(CONFIG_BLK_DEV_PIIX) += piix.o
|
||||
obj-$(CONFIG_BLK_DEV_RZ1000) += rz1000.o
|
||||
obj-$(CONFIG_BLK_DEV_SVWKS) += serverworks.o
|
||||
obj-$(CONFIG_BLK_DEV_SGIIOC4) += sgiioc4.o
|
||||
obj-$(CONFIG_BLK_DEV_SIIMAGE) += siimage.o
|
||||
obj-$(CONFIG_BLK_DEV_SIS5513) += sis5513.o
|
||||
obj-$(CONFIG_BLK_DEV_SL82C105) += sl82c105.o
|
||||
obj-$(CONFIG_BLK_DEV_SLC90E66) += slc90e66.o
|
||||
obj-$(CONFIG_BLK_DEV_TC86C001) += tc86c001.o
|
||||
obj-$(CONFIG_BLK_DEV_TRIFLEX) += triflex.o
|
||||
obj-$(CONFIG_BLK_DEV_TRM290) += trm290.o
|
||||
obj-$(CONFIG_BLK_DEV_VIA82CXXX) += via82cxxx.o
|
||||
|
||||
# Must appear at the end of the block
|
||||
obj-$(CONFIG_BLK_DEV_GENERIC) += generic.o
|
||||
|
||||
EXTRA_CFLAGS := -Idrivers/ide
|
||||
439
drivers/ide/pci/aec62xx.c
Normal file
439
drivers/ide/pci/aec62xx.c
Normal file
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/aec62xx.c Version 0.11 March 27, 2002
|
||||
*
|
||||
* Copyright (C) 1999-2002 Andre Hedrick <andre@linux-ide.org>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
struct chipset_bus_clock_list_entry {
|
||||
u8 xfer_speed;
|
||||
u8 chipset_settings;
|
||||
u8 ultra_settings;
|
||||
};
|
||||
|
||||
static const struct chipset_bus_clock_list_entry aec6xxx_33_base [] = {
|
||||
{ XFER_UDMA_6, 0x31, 0x07 },
|
||||
{ XFER_UDMA_5, 0x31, 0x06 },
|
||||
{ XFER_UDMA_4, 0x31, 0x05 },
|
||||
{ XFER_UDMA_3, 0x31, 0x04 },
|
||||
{ XFER_UDMA_2, 0x31, 0x03 },
|
||||
{ XFER_UDMA_1, 0x31, 0x02 },
|
||||
{ XFER_UDMA_0, 0x31, 0x01 },
|
||||
|
||||
{ XFER_MW_DMA_2, 0x31, 0x00 },
|
||||
{ XFER_MW_DMA_1, 0x31, 0x00 },
|
||||
{ XFER_MW_DMA_0, 0x0a, 0x00 },
|
||||
{ XFER_PIO_4, 0x31, 0x00 },
|
||||
{ XFER_PIO_3, 0x33, 0x00 },
|
||||
{ XFER_PIO_2, 0x08, 0x00 },
|
||||
{ XFER_PIO_1, 0x0a, 0x00 },
|
||||
{ XFER_PIO_0, 0x00, 0x00 },
|
||||
{ 0, 0x00, 0x00 }
|
||||
};
|
||||
|
||||
static const struct chipset_bus_clock_list_entry aec6xxx_34_base [] = {
|
||||
{ XFER_UDMA_6, 0x41, 0x06 },
|
||||
{ XFER_UDMA_5, 0x41, 0x05 },
|
||||
{ XFER_UDMA_4, 0x41, 0x04 },
|
||||
{ XFER_UDMA_3, 0x41, 0x03 },
|
||||
{ XFER_UDMA_2, 0x41, 0x02 },
|
||||
{ XFER_UDMA_1, 0x41, 0x01 },
|
||||
{ XFER_UDMA_0, 0x41, 0x01 },
|
||||
|
||||
{ XFER_MW_DMA_2, 0x41, 0x00 },
|
||||
{ XFER_MW_DMA_1, 0x42, 0x00 },
|
||||
{ XFER_MW_DMA_0, 0x7a, 0x00 },
|
||||
{ XFER_PIO_4, 0x41, 0x00 },
|
||||
{ XFER_PIO_3, 0x43, 0x00 },
|
||||
{ XFER_PIO_2, 0x78, 0x00 },
|
||||
{ XFER_PIO_1, 0x7a, 0x00 },
|
||||
{ XFER_PIO_0, 0x70, 0x00 },
|
||||
{ 0, 0x00, 0x00 }
|
||||
};
|
||||
|
||||
#define BUSCLOCK(D) \
|
||||
((struct chipset_bus_clock_list_entry *) pci_get_drvdata((D)))
|
||||
|
||||
|
||||
/*
|
||||
* TO DO: active tuning and correction of cards without a bios.
|
||||
*/
|
||||
static u8 pci_bus_clock_list (u8 speed, struct chipset_bus_clock_list_entry * chipset_table)
|
||||
{
|
||||
for ( ; chipset_table->xfer_speed ; chipset_table++)
|
||||
if (chipset_table->xfer_speed == speed) {
|
||||
return chipset_table->chipset_settings;
|
||||
}
|
||||
return chipset_table->chipset_settings;
|
||||
}
|
||||
|
||||
static u8 pci_bus_clock_list_ultra (u8 speed, struct chipset_bus_clock_list_entry * chipset_table)
|
||||
{
|
||||
for ( ; chipset_table->xfer_speed ; chipset_table++)
|
||||
if (chipset_table->xfer_speed == speed) {
|
||||
return chipset_table->ultra_settings;
|
||||
}
|
||||
return chipset_table->ultra_settings;
|
||||
}
|
||||
|
||||
static u8 aec62xx_ratemask (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 mode;
|
||||
|
||||
switch(hwif->pci_dev->device) {
|
||||
case PCI_DEVICE_ID_ARTOP_ATP865:
|
||||
case PCI_DEVICE_ID_ARTOP_ATP865R:
|
||||
mode = (inb(hwif->channel ?
|
||||
hwif->mate->dma_status :
|
||||
hwif->dma_status) & 0x10) ? 4 : 3;
|
||||
break;
|
||||
case PCI_DEVICE_ID_ARTOP_ATP860:
|
||||
case PCI_DEVICE_ID_ARTOP_ATP860R:
|
||||
mode = 2;
|
||||
break;
|
||||
case PCI_DEVICE_ID_ARTOP_ATP850UF:
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min(mode, (u8)1);
|
||||
return mode;
|
||||
}
|
||||
|
||||
static int aec6210_tune_chipset (ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u16 d_conf = 0;
|
||||
u8 speed = ide_rate_filter(aec62xx_ratemask(drive), xferspeed);
|
||||
u8 ultra = 0, ultra_conf = 0;
|
||||
u8 tmp0 = 0, tmp1 = 0, tmp2 = 0;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
/* 0x40|(2*drive->dn): Active, 0x41|(2*drive->dn): Recovery */
|
||||
pci_read_config_word(dev, 0x40|(2*drive->dn), &d_conf);
|
||||
tmp0 = pci_bus_clock_list(speed, BUSCLOCK(dev));
|
||||
d_conf = ((tmp0 & 0xf0) << 4) | (tmp0 & 0xf);
|
||||
pci_write_config_word(dev, 0x40|(2*drive->dn), d_conf);
|
||||
|
||||
tmp1 = 0x00;
|
||||
tmp2 = 0x00;
|
||||
pci_read_config_byte(dev, 0x54, &ultra);
|
||||
tmp1 = ((0x00 << (2*drive->dn)) | (ultra & ~(3 << (2*drive->dn))));
|
||||
ultra_conf = pci_bus_clock_list_ultra(speed, BUSCLOCK(dev));
|
||||
tmp2 = ((ultra_conf << (2*drive->dn)) | (tmp1 & ~(3 << (2*drive->dn))));
|
||||
pci_write_config_byte(dev, 0x54, tmp2);
|
||||
local_irq_restore(flags);
|
||||
return(ide_config_drive_speed(drive, speed));
|
||||
}
|
||||
|
||||
static int aec6260_tune_chipset (ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u8 speed = ide_rate_filter(aec62xx_ratemask(drive), xferspeed);
|
||||
u8 unit = (drive->select.b.unit & 0x01);
|
||||
u8 tmp1 = 0, tmp2 = 0;
|
||||
u8 ultra = 0, drive_conf = 0, ultra_conf = 0;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
/* high 4-bits: Active, low 4-bits: Recovery */
|
||||
pci_read_config_byte(dev, 0x40|drive->dn, &drive_conf);
|
||||
drive_conf = pci_bus_clock_list(speed, BUSCLOCK(dev));
|
||||
pci_write_config_byte(dev, 0x40|drive->dn, drive_conf);
|
||||
|
||||
pci_read_config_byte(dev, (0x44|hwif->channel), &ultra);
|
||||
tmp1 = ((0x00 << (4*unit)) | (ultra & ~(7 << (4*unit))));
|
||||
ultra_conf = pci_bus_clock_list_ultra(speed, BUSCLOCK(dev));
|
||||
tmp2 = ((ultra_conf << (4*unit)) | (tmp1 & ~(7 << (4*unit))));
|
||||
pci_write_config_byte(dev, (0x44|hwif->channel), tmp2);
|
||||
local_irq_restore(flags);
|
||||
return(ide_config_drive_speed(drive, speed));
|
||||
}
|
||||
|
||||
static int aec62xx_tune_chipset (ide_drive_t *drive, u8 speed)
|
||||
{
|
||||
switch (HWIF(drive)->pci_dev->device) {
|
||||
case PCI_DEVICE_ID_ARTOP_ATP865:
|
||||
case PCI_DEVICE_ID_ARTOP_ATP865R:
|
||||
case PCI_DEVICE_ID_ARTOP_ATP860:
|
||||
case PCI_DEVICE_ID_ARTOP_ATP860R:
|
||||
return ((int) aec6260_tune_chipset(drive, speed));
|
||||
case PCI_DEVICE_ID_ARTOP_ATP850UF:
|
||||
return ((int) aec6210_tune_chipset(drive, speed));
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int config_chipset_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, aec62xx_ratemask(drive));
|
||||
|
||||
if (!(speed))
|
||||
return 0;
|
||||
|
||||
(void) aec62xx_tune_chipset(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
static void aec62xx_tune_drive (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
u8 speed = 0;
|
||||
u8 new_pio = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
|
||||
|
||||
switch(pio) {
|
||||
case 5: speed = new_pio; break;
|
||||
case 4: speed = XFER_PIO_4; break;
|
||||
case 3: speed = XFER_PIO_3; break;
|
||||
case 2: speed = XFER_PIO_2; break;
|
||||
case 1: speed = XFER_PIO_1; break;
|
||||
default: speed = XFER_PIO_0; break;
|
||||
}
|
||||
(void) aec62xx_tune_chipset(drive, speed);
|
||||
}
|
||||
|
||||
static int aec62xx_config_drive_xfer_rate (ide_drive_t *drive)
|
||||
{
|
||||
if (ide_use_dma(drive) && config_chipset_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
if (ide_use_fast_pio(drive))
|
||||
aec62xx_tune_drive(drive, 5);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int aec62xx_irq_timeout (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
switch(dev->device) {
|
||||
case PCI_DEVICE_ID_ARTOP_ATP860:
|
||||
case PCI_DEVICE_ID_ARTOP_ATP860R:
|
||||
case PCI_DEVICE_ID_ARTOP_ATP865:
|
||||
case PCI_DEVICE_ID_ARTOP_ATP865R:
|
||||
printk(" AEC62XX time out ");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev, const char *name)
|
||||
{
|
||||
int bus_speed = system_bus_clock();
|
||||
|
||||
if (dev->resource[PCI_ROM_RESOURCE].start) {
|
||||
pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
|
||||
printk(KERN_INFO "%s: ROM enabled at 0x%08lx\n", name,
|
||||
(unsigned long)dev->resource[PCI_ROM_RESOURCE].start);
|
||||
}
|
||||
|
||||
if (bus_speed <= 33)
|
||||
pci_set_drvdata(dev, (void *) aec6xxx_33_base);
|
||||
else
|
||||
pci_set_drvdata(dev, (void *) aec6xxx_34_base);
|
||||
|
||||
/* These are necessary to get AEC6280 Macintosh cards to work */
|
||||
if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP865) ||
|
||||
(dev->device == PCI_DEVICE_ID_ARTOP_ATP865R)) {
|
||||
u8 reg49h = 0, reg4ah = 0;
|
||||
/* Clear reset and test bits. */
|
||||
pci_read_config_byte(dev, 0x49, ®49h);
|
||||
pci_write_config_byte(dev, 0x49, reg49h & ~0x30);
|
||||
/* Enable chip interrupt output. */
|
||||
pci_read_config_byte(dev, 0x4a, ®4ah);
|
||||
pci_write_config_byte(dev, 0x4a, reg4ah & ~0x01);
|
||||
/* Enable burst mode. */
|
||||
pci_read_config_byte(dev, 0x4a, ®4ah);
|
||||
pci_write_config_byte(dev, 0x4a, reg4ah | 0x80);
|
||||
}
|
||||
|
||||
return dev->irq;
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_aec62xx(ide_hwif_t *hwif)
|
||||
{
|
||||
hwif->autodma = 0;
|
||||
hwif->tuneproc = &aec62xx_tune_drive;
|
||||
hwif->speedproc = &aec62xx_tune_chipset;
|
||||
|
||||
if (hwif->pci_dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF)
|
||||
hwif->serialized = hwif->channel;
|
||||
|
||||
if (hwif->mate)
|
||||
hwif->mate->serialized = hwif->serialized;
|
||||
|
||||
if (!hwif->dma_base) {
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].autotune = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
hwif->ultra_mask = 0x7f;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
hwif->swdma_mask = 0x07;
|
||||
|
||||
hwif->ide_dma_check = &aec62xx_config_drive_xfer_rate;
|
||||
hwif->ide_dma_lostirq = &aec62xx_irq_timeout;
|
||||
hwif->ide_dma_timeout = &aec62xx_irq_timeout;
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
static void __devinit init_dma_aec62xx(ide_hwif_t *hwif, unsigned long dmabase)
|
||||
{
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
if (dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) {
|
||||
u8 reg54h = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ide_lock, flags);
|
||||
pci_read_config_byte(dev, 0x54, ®54h);
|
||||
pci_write_config_byte(dev, 0x54, reg54h & ~(hwif->channel ? 0xF0 : 0x0F));
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
} else {
|
||||
u8 ata66 = 0;
|
||||
pci_read_config_byte(hwif->pci_dev, 0x49, &ata66);
|
||||
if (!(hwif->udma_four))
|
||||
hwif->udma_four = (ata66&(hwif->channel?0x02:0x01))?0:1;
|
||||
}
|
||||
|
||||
ide_setup_dma(hwif, dmabase, 8);
|
||||
}
|
||||
|
||||
static int __devinit init_setup_aec62xx(struct pci_dev *dev, ide_pci_device_t *d)
|
||||
{
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
static int __devinit init_setup_aec6x80(struct pci_dev *dev, ide_pci_device_t *d)
|
||||
{
|
||||
unsigned long bar4reg = pci_resource_start(dev, 4);
|
||||
|
||||
if (inb(bar4reg+2) & 0x10) {
|
||||
strcpy(d->name, "AEC6880");
|
||||
if (dev->device == PCI_DEVICE_ID_ARTOP_ATP865R)
|
||||
strcpy(d->name, "AEC6880R");
|
||||
} else {
|
||||
strcpy(d->name, "AEC6280");
|
||||
if (dev->device == PCI_DEVICE_ID_ARTOP_ATP865R)
|
||||
strcpy(d->name, "AEC6280R");
|
||||
}
|
||||
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
static ide_pci_device_t aec62xx_chipsets[] __devinitdata = {
|
||||
{ /* 0 */
|
||||
.name = "AEC6210",
|
||||
.init_setup = init_setup_aec62xx,
|
||||
.init_chipset = init_chipset_aec62xx,
|
||||
.init_hwif = init_hwif_aec62xx,
|
||||
.init_dma = init_dma_aec62xx,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}},
|
||||
.bootable = OFF_BOARD,
|
||||
},{ /* 1 */
|
||||
.name = "AEC6260",
|
||||
.init_setup = init_setup_aec62xx,
|
||||
.init_chipset = init_chipset_aec62xx,
|
||||
.init_hwif = init_hwif_aec62xx,
|
||||
.init_dma = init_dma_aec62xx,
|
||||
.channels = 2,
|
||||
.autodma = NOAUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
},{ /* 2 */
|
||||
.name = "AEC6260R",
|
||||
.init_setup = init_setup_aec62xx,
|
||||
.init_chipset = init_chipset_aec62xx,
|
||||
.init_hwif = init_hwif_aec62xx,
|
||||
.init_dma = init_dma_aec62xx,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}},
|
||||
.bootable = NEVER_BOARD,
|
||||
},{ /* 3 */
|
||||
.name = "AEC6X80",
|
||||
.init_setup = init_setup_aec6x80,
|
||||
.init_chipset = init_chipset_aec62xx,
|
||||
.init_hwif = init_hwif_aec62xx,
|
||||
.init_dma = init_dma_aec62xx,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
},{ /* 4 */
|
||||
.name = "AEC6X80R",
|
||||
.init_setup = init_setup_aec6x80,
|
||||
.init_chipset = init_chipset_aec62xx,
|
||||
.init_hwif = init_hwif_aec62xx,
|
||||
.init_dma = init_dma_aec62xx,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}},
|
||||
.bootable = OFF_BOARD,
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* aec62xx_init_one - called when a AEC is found
|
||||
* @dev: the aec62xx device
|
||||
* @id: the matching pci id
|
||||
*
|
||||
* Called when the PCI registration layer (or the IDE initialization)
|
||||
* finds a device matching our IDE device tables.
|
||||
*/
|
||||
|
||||
static int __devinit aec62xx_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
ide_pci_device_t *d = &aec62xx_chipsets[id->driver_data];
|
||||
|
||||
return d->init_setup(dev, d);
|
||||
}
|
||||
|
||||
static struct pci_device_id aec62xx_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
|
||||
{ PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860R, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 },
|
||||
{ PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP865, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 },
|
||||
{ PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP865R, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, aec62xx_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "AEC62xx_IDE",
|
||||
.id_table = aec62xx_pci_tbl,
|
||||
.probe = aec62xx_init_one,
|
||||
};
|
||||
|
||||
static int __init aec62xx_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(aec62xx_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for ARTOP AEC62xx IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
937
drivers/ide/pci/alim15x3.c
Normal file
937
drivers/ide/pci/alim15x3.c
Normal file
@@ -0,0 +1,937 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/alim15x3.c Version 0.21 2007/02/03
|
||||
*
|
||||
* Copyright (C) 1998-2000 Michel Aubry, Maintainer
|
||||
* Copyright (C) 1998-2000 Andrzej Krzysztofowicz, Maintainer
|
||||
* Copyright (C) 1999-2000 CJ, cjtsai@ali.com.tw, Maintainer
|
||||
*
|
||||
* Copyright (C) 1998-2000 Andre Hedrick (andre@linux-ide.org)
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
* Copyright (C) 2002 Alan Cox <alan@redhat.com>
|
||||
* ALi (now ULi M5228) support by Clear Zhang <Clear.Zhang@ali.com.tw>
|
||||
* Copyright (C) 2007 MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* (U)DMA capable version of ali 1533/1543(C), 1535(D)
|
||||
*
|
||||
**********************************************************************
|
||||
* 9/7/99 --Parts from the above author are included and need to be
|
||||
* converted into standard interface, once I finish the thought.
|
||||
*
|
||||
* Recent changes
|
||||
* Don't use LBA48 mode on ALi <= 0xC4
|
||||
* Don't poke 0x79 with a non ALi northbridge
|
||||
* Don't flip undefined bits on newer chipsets (fix Fujitsu laptop hang)
|
||||
* Allow UDMA6 on revisions > 0xC4
|
||||
*
|
||||
* Documentation
|
||||
* Chipset documentation available under NDA only
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DISPLAY_ALI_TIMINGS
|
||||
|
||||
/*
|
||||
* ALi devices are not plug in. Otherwise these static values would
|
||||
* need to go. They ought to go away anyway
|
||||
*/
|
||||
|
||||
static u8 m5229_revision;
|
||||
static u8 chip_is_1543c_e;
|
||||
static struct pci_dev *isa_dev;
|
||||
|
||||
#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
|
||||
#include <linux/stat.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
static u8 ali_proc = 0;
|
||||
|
||||
static struct pci_dev *bmide_dev;
|
||||
|
||||
static char *fifo[4] = {
|
||||
"FIFO Off",
|
||||
"FIFO On ",
|
||||
"DMA mode",
|
||||
"PIO mode" };
|
||||
|
||||
static char *udmaT[8] = {
|
||||
"1.5T",
|
||||
" 2T",
|
||||
"2.5T",
|
||||
" 3T",
|
||||
"3.5T",
|
||||
" 4T",
|
||||
" 6T",
|
||||
" 8T"
|
||||
};
|
||||
|
||||
static char *channel_status[8] = {
|
||||
"OK ",
|
||||
"busy ",
|
||||
"DRQ ",
|
||||
"DRQ busy ",
|
||||
"error ",
|
||||
"error busy ",
|
||||
"error DRQ ",
|
||||
"error DRQ busy"
|
||||
};
|
||||
|
||||
/**
|
||||
* ali_get_info - generate proc file for ALi IDE
|
||||
* @buffer: buffer to fill
|
||||
* @addr: address of user start in buffer
|
||||
* @offset: offset into 'file'
|
||||
* @count: buffer count
|
||||
*
|
||||
* Walks the Ali devices and outputs summary data on the tuning and
|
||||
* anything else that will help with debugging
|
||||
*/
|
||||
|
||||
static int ali_get_info (char *buffer, char **addr, off_t offset, int count)
|
||||
{
|
||||
unsigned long bibma;
|
||||
u8 reg53h, reg5xh, reg5yh, reg5xh1, reg5yh1, c0, c1, rev, tmp;
|
||||
char *q, *p = buffer;
|
||||
|
||||
/* fetch rev. */
|
||||
pci_read_config_byte(bmide_dev, 0x08, &rev);
|
||||
if (rev >= 0xc1) /* M1543C or newer */
|
||||
udmaT[7] = " ???";
|
||||
else
|
||||
fifo[3] = " ??? ";
|
||||
|
||||
/* first fetch bibma: */
|
||||
|
||||
bibma = pci_resource_start(bmide_dev, 4);
|
||||
|
||||
/*
|
||||
* at that point bibma+0x2 et bibma+0xa are byte
|
||||
* registers to investigate:
|
||||
*/
|
||||
c0 = inb(bibma + 0x02);
|
||||
c1 = inb(bibma + 0x0a);
|
||||
|
||||
p += sprintf(p,
|
||||
"\n Ali M15x3 Chipset.\n");
|
||||
p += sprintf(p,
|
||||
" ------------------\n");
|
||||
pci_read_config_byte(bmide_dev, 0x78, ®53h);
|
||||
p += sprintf(p, "PCI Clock: %d.\n", reg53h);
|
||||
|
||||
pci_read_config_byte(bmide_dev, 0x53, ®53h);
|
||||
p += sprintf(p,
|
||||
"CD_ROM FIFO:%s, CD_ROM DMA:%s\n",
|
||||
(reg53h & 0x02) ? "Yes" : "No ",
|
||||
(reg53h & 0x01) ? "Yes" : "No " );
|
||||
pci_read_config_byte(bmide_dev, 0x74, ®53h);
|
||||
p += sprintf(p,
|
||||
"FIFO Status: contains %d Words, runs%s%s\n\n",
|
||||
(reg53h & 0x3f),
|
||||
(reg53h & 0x40) ? " OVERWR" : "",
|
||||
(reg53h & 0x80) ? " OVERRD." : "." );
|
||||
|
||||
p += sprintf(p,
|
||||
"-------------------primary channel"
|
||||
"-------------------secondary channel"
|
||||
"---------\n\n");
|
||||
|
||||
pci_read_config_byte(bmide_dev, 0x09, ®53h);
|
||||
p += sprintf(p,
|
||||
"channel status: %s"
|
||||
" %s\n",
|
||||
(reg53h & 0x20) ? "On " : "Off",
|
||||
(reg53h & 0x10) ? "On " : "Off" );
|
||||
|
||||
p += sprintf(p,
|
||||
"both channels togth: %s"
|
||||
" %s\n",
|
||||
(c0&0x80) ? "No " : "Yes",
|
||||
(c1&0x80) ? "No " : "Yes" );
|
||||
|
||||
pci_read_config_byte(bmide_dev, 0x76, ®53h);
|
||||
p += sprintf(p,
|
||||
"Channel state: %s %s\n",
|
||||
channel_status[reg53h & 0x07],
|
||||
channel_status[(reg53h & 0x70) >> 4] );
|
||||
|
||||
pci_read_config_byte(bmide_dev, 0x58, ®5xh);
|
||||
pci_read_config_byte(bmide_dev, 0x5c, ®5yh);
|
||||
p += sprintf(p,
|
||||
"Add. Setup Timing: %dT"
|
||||
" %dT\n",
|
||||
(reg5xh & 0x07) ? (reg5xh & 0x07) : 8,
|
||||
(reg5yh & 0x07) ? (reg5yh & 0x07) : 8 );
|
||||
|
||||
pci_read_config_byte(bmide_dev, 0x59, ®5xh);
|
||||
pci_read_config_byte(bmide_dev, 0x5d, ®5yh);
|
||||
p += sprintf(p,
|
||||
"Command Act. Count: %dT"
|
||||
" %dT\n"
|
||||
"Command Rec. Count: %dT"
|
||||
" %dT\n\n",
|
||||
(reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8,
|
||||
(reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8,
|
||||
(reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16,
|
||||
(reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16 );
|
||||
|
||||
p += sprintf(p,
|
||||
"----------------drive0-----------drive1"
|
||||
"------------drive0-----------drive1------\n\n");
|
||||
p += sprintf(p,
|
||||
"DMA enabled: %s %s"
|
||||
" %s %s\n",
|
||||
(c0&0x20) ? "Yes" : "No ",
|
||||
(c0&0x40) ? "Yes" : "No ",
|
||||
(c1&0x20) ? "Yes" : "No ",
|
||||
(c1&0x40) ? "Yes" : "No " );
|
||||
|
||||
pci_read_config_byte(bmide_dev, 0x54, ®5xh);
|
||||
pci_read_config_byte(bmide_dev, 0x55, ®5yh);
|
||||
q = "FIFO threshold: %2d Words %2d Words"
|
||||
" %2d Words %2d Words\n";
|
||||
if (rev < 0xc1) {
|
||||
if ((rev == 0x20) &&
|
||||
(pci_read_config_byte(bmide_dev, 0x4f, &tmp), (tmp &= 0x20))) {
|
||||
p += sprintf(p, q, 8, 8, 8, 8);
|
||||
} else {
|
||||
p += sprintf(p, q,
|
||||
(reg5xh & 0x03) + 12,
|
||||
((reg5xh & 0x30)>>4) + 12,
|
||||
(reg5yh & 0x03) + 12,
|
||||
((reg5yh & 0x30)>>4) + 12 );
|
||||
}
|
||||
} else {
|
||||
int t1 = (tmp = (reg5xh & 0x03)) ? (tmp << 3) : 4;
|
||||
int t2 = (tmp = ((reg5xh & 0x30)>>4)) ? (tmp << 3) : 4;
|
||||
int t3 = (tmp = (reg5yh & 0x03)) ? (tmp << 3) : 4;
|
||||
int t4 = (tmp = ((reg5yh & 0x30)>>4)) ? (tmp << 3) : 4;
|
||||
p += sprintf(p, q, t1, t2, t3, t4);
|
||||
}
|
||||
|
||||
#if 0
|
||||
p += sprintf(p,
|
||||
"FIFO threshold: %2d Words %2d Words"
|
||||
" %2d Words %2d Words\n",
|
||||
(reg5xh & 0x03) + 12,
|
||||
((reg5xh & 0x30)>>4) + 12,
|
||||
(reg5yh & 0x03) + 12,
|
||||
((reg5yh & 0x30)>>4) + 12 );
|
||||
#endif
|
||||
|
||||
p += sprintf(p,
|
||||
"FIFO mode: %s %s %s %s\n",
|
||||
fifo[((reg5xh & 0x0c) >> 2)],
|
||||
fifo[((reg5xh & 0xc0) >> 6)],
|
||||
fifo[((reg5yh & 0x0c) >> 2)],
|
||||
fifo[((reg5yh & 0xc0) >> 6)] );
|
||||
|
||||
pci_read_config_byte(bmide_dev, 0x5a, ®5xh);
|
||||
pci_read_config_byte(bmide_dev, 0x5b, ®5xh1);
|
||||
pci_read_config_byte(bmide_dev, 0x5e, ®5yh);
|
||||
pci_read_config_byte(bmide_dev, 0x5f, ®5yh1);
|
||||
|
||||
p += sprintf(p,/*
|
||||
"------------------drive0-----------drive1"
|
||||
"------------drive0-----------drive1------\n")*/
|
||||
"Dt RW act. Cnt %2dT %2dT"
|
||||
" %2dT %2dT\n"
|
||||
"Dt RW rec. Cnt %2dT %2dT"
|
||||
" %2dT %2dT\n\n",
|
||||
(reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8,
|
||||
(reg5xh1 & 0x70) ? ((reg5xh1 & 0x70) >> 4) : 8,
|
||||
(reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8,
|
||||
(reg5yh1 & 0x70) ? ((reg5yh1 & 0x70) >> 4) : 8,
|
||||
(reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16,
|
||||
(reg5xh1 & 0x0f) ? (reg5xh1 & 0x0f) : 16,
|
||||
(reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16,
|
||||
(reg5yh1 & 0x0f) ? (reg5yh1 & 0x0f) : 16 );
|
||||
|
||||
p += sprintf(p,
|
||||
"-----------------------------------UDMA Timings"
|
||||
"--------------------------------\n\n");
|
||||
|
||||
pci_read_config_byte(bmide_dev, 0x56, ®5xh);
|
||||
pci_read_config_byte(bmide_dev, 0x57, ®5yh);
|
||||
p += sprintf(p,
|
||||
"UDMA: %s %s"
|
||||
" %s %s\n"
|
||||
"UDMA timings: %s %s"
|
||||
" %s %s\n\n",
|
||||
(reg5xh & 0x08) ? "OK" : "No",
|
||||
(reg5xh & 0x80) ? "OK" : "No",
|
||||
(reg5yh & 0x08) ? "OK" : "No",
|
||||
(reg5yh & 0x80) ? "OK" : "No",
|
||||
udmaT[(reg5xh & 0x07)],
|
||||
udmaT[(reg5xh & 0x70) >> 4],
|
||||
udmaT[reg5yh & 0x07],
|
||||
udmaT[(reg5yh & 0x70) >> 4] );
|
||||
|
||||
return p-buffer; /* => must be less than 4k! */
|
||||
}
|
||||
#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
|
||||
|
||||
/**
|
||||
* ali15x3_tune_pio - set up chipset for PIO mode
|
||||
* @drive: drive to tune
|
||||
* @pio: desired mode
|
||||
*
|
||||
* Select the best PIO mode for the drive in question.
|
||||
* Then program the controller for this mode.
|
||||
*
|
||||
* Returns the PIO mode programmed.
|
||||
*/
|
||||
|
||||
static u8 ali15x3_tune_pio (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
ide_pio_data_t d;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
int s_time, a_time, c_time;
|
||||
u8 s_clc, a_clc, r_clc;
|
||||
unsigned long flags;
|
||||
int bus_speed = system_bus_clock();
|
||||
int port = hwif->channel ? 0x5c : 0x58;
|
||||
int portFIFO = hwif->channel ? 0x55 : 0x54;
|
||||
u8 cd_dma_fifo = 0;
|
||||
int unit = drive->select.b.unit & 1;
|
||||
|
||||
pio = ide_get_best_pio_mode(drive, pio, 5, &d);
|
||||
s_time = ide_pio_timings[pio].setup_time;
|
||||
a_time = ide_pio_timings[pio].active_time;
|
||||
if ((s_clc = (s_time * bus_speed + 999) / 1000) >= 8)
|
||||
s_clc = 0;
|
||||
if ((a_clc = (a_time * bus_speed + 999) / 1000) >= 8)
|
||||
a_clc = 0;
|
||||
c_time = ide_pio_timings[pio].cycle_time;
|
||||
|
||||
#if 0
|
||||
if ((r_clc = ((c_time - s_time - a_time) * bus_speed + 999) / 1000) >= 16)
|
||||
r_clc = 0;
|
||||
#endif
|
||||
|
||||
if (!(r_clc = (c_time * bus_speed + 999) / 1000 - a_clc - s_clc)) {
|
||||
r_clc = 1;
|
||||
} else {
|
||||
if (r_clc >= 16)
|
||||
r_clc = 0;
|
||||
}
|
||||
local_irq_save(flags);
|
||||
|
||||
/*
|
||||
* PIO mode => ATA FIFO on, ATAPI FIFO off
|
||||
*/
|
||||
pci_read_config_byte(dev, portFIFO, &cd_dma_fifo);
|
||||
if (drive->media==ide_disk) {
|
||||
if (unit) {
|
||||
pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0x0F) | 0x50);
|
||||
} else {
|
||||
pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0xF0) | 0x05);
|
||||
}
|
||||
} else {
|
||||
if (unit) {
|
||||
pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0x0F);
|
||||
} else {
|
||||
pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0xF0);
|
||||
}
|
||||
}
|
||||
|
||||
pci_write_config_byte(dev, port, s_clc);
|
||||
pci_write_config_byte(dev, port+drive->select.b.unit+2, (a_clc << 4) | r_clc);
|
||||
local_irq_restore(flags);
|
||||
|
||||
/*
|
||||
* setup active rec
|
||||
* { 70, 165, 365 }, PIO Mode 0
|
||||
* { 50, 125, 208 }, PIO Mode 1
|
||||
* { 30, 100, 110 }, PIO Mode 2
|
||||
* { 30, 80, 70 }, PIO Mode 3 with IORDY
|
||||
* { 25, 70, 25 }, PIO Mode 4 with IORDY ns
|
||||
* { 20, 50, 30 } PIO Mode 5 with IORDY (nonstandard)
|
||||
*/
|
||||
|
||||
return pio;
|
||||
}
|
||||
|
||||
/**
|
||||
* ali15x3_tune_drive - set up drive for PIO mode
|
||||
* @drive: drive to tune
|
||||
* @pio: desired mode
|
||||
*
|
||||
* Program the controller with the best PIO timing for the given drive.
|
||||
* Then set up the drive itself.
|
||||
*/
|
||||
|
||||
static void ali15x3_tune_drive (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
pio = ali15x3_tune_pio(drive, pio);
|
||||
(void) ide_config_drive_speed(drive, XFER_PIO_0 + pio);
|
||||
}
|
||||
|
||||
/**
|
||||
* ali15x3_can_ultra - check for ultra DMA support
|
||||
* @drive: drive to do the check
|
||||
*
|
||||
* Check the drive and controller revisions. Return 0 if UDMA is
|
||||
* not available, or 1 if UDMA can be used. The actual rules for
|
||||
* the ALi are
|
||||
* No UDMA on revisions <= 0x20
|
||||
* Disk only for revisions < 0xC2
|
||||
* Not WDC drives for revisions < 0xC2
|
||||
*
|
||||
* FIXME: WDC ifdef needs to die
|
||||
*/
|
||||
|
||||
static u8 ali15x3_can_ultra (ide_drive_t *drive)
|
||||
{
|
||||
#ifndef CONFIG_WDC_ALI15X3
|
||||
struct hd_driveid *id = drive->id;
|
||||
#endif /* CONFIG_WDC_ALI15X3 */
|
||||
|
||||
if (m5229_revision <= 0x20) {
|
||||
return 0;
|
||||
} else if ((m5229_revision < 0xC2) &&
|
||||
#ifndef CONFIG_WDC_ALI15X3
|
||||
((chip_is_1543c_e && strstr(id->model, "WDC ")) ||
|
||||
(drive->media!=ide_disk))) {
|
||||
#else /* CONFIG_WDC_ALI15X3 */
|
||||
(drive->media!=ide_disk)) {
|
||||
#endif /* CONFIG_WDC_ALI15X3 */
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ali15x3_ratemask - generate DMA mode list
|
||||
* @drive: drive to compute against
|
||||
*
|
||||
* Generate a list of the available DMA modes for the drive.
|
||||
* FIXME: this function contains lots of bogus masking we can dump
|
||||
*
|
||||
* Return the highest available mode (UDMA33, UDMA66, UDMA100,..)
|
||||
*/
|
||||
|
||||
static u8 ali15x3_ratemask (ide_drive_t *drive)
|
||||
{
|
||||
u8 mode = 0, can_ultra = ali15x3_can_ultra(drive);
|
||||
|
||||
if (m5229_revision > 0xC4 && can_ultra) {
|
||||
mode = 4;
|
||||
} else if (m5229_revision == 0xC4 && can_ultra) {
|
||||
mode = 3;
|
||||
} else if (m5229_revision >= 0xC2 && can_ultra) {
|
||||
mode = 2;
|
||||
} else if (can_ultra) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the drive sees no suitable cable then UDMA 33
|
||||
* is the highest permitted mode
|
||||
*/
|
||||
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min(mode, (u8)1);
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* ali15x3_tune_chipset - set up chipset/drive for new speed
|
||||
* @drive: drive to configure for
|
||||
* @xferspeed: desired speed
|
||||
*
|
||||
* Configure the hardware for the desired IDE transfer mode.
|
||||
* We also do the needed drive configuration through helpers
|
||||
*/
|
||||
|
||||
static int ali15x3_tune_chipset (ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u8 speed = ide_rate_filter(ali15x3_ratemask(drive), xferspeed);
|
||||
u8 speed1 = speed;
|
||||
u8 unit = (drive->select.b.unit & 0x01);
|
||||
u8 tmpbyte = 0x00;
|
||||
int m5229_udma = (hwif->channel) ? 0x57 : 0x56;
|
||||
|
||||
if (speed == XFER_UDMA_6)
|
||||
speed1 = 0x47;
|
||||
|
||||
if (speed < XFER_UDMA_0) {
|
||||
u8 ultra_enable = (unit) ? 0x7f : 0xf7;
|
||||
/*
|
||||
* clear "ultra enable" bit
|
||||
*/
|
||||
pci_read_config_byte(dev, m5229_udma, &tmpbyte);
|
||||
tmpbyte &= ultra_enable;
|
||||
pci_write_config_byte(dev, m5229_udma, tmpbyte);
|
||||
|
||||
if (speed < XFER_SW_DMA_0)
|
||||
(void) ali15x3_tune_pio(drive, speed - XFER_PIO_0);
|
||||
} else {
|
||||
pci_read_config_byte(dev, m5229_udma, &tmpbyte);
|
||||
tmpbyte &= (0x0f << ((1-unit) << 2));
|
||||
/*
|
||||
* enable ultra dma and set timing
|
||||
*/
|
||||
tmpbyte |= ((0x08 | ((4-speed1)&0x07)) << (unit << 2));
|
||||
pci_write_config_byte(dev, m5229_udma, tmpbyte);
|
||||
if (speed >= XFER_UDMA_3) {
|
||||
pci_read_config_byte(dev, 0x4b, &tmpbyte);
|
||||
tmpbyte |= 1;
|
||||
pci_write_config_byte(dev, 0x4b, tmpbyte);
|
||||
}
|
||||
}
|
||||
return (ide_config_drive_speed(drive, speed));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* config_chipset_for_dma - set up DMA mode
|
||||
* @drive: drive to configure for
|
||||
*
|
||||
* Place a drive into DMA mode and tune the chipset for
|
||||
* the selected speed.
|
||||
*
|
||||
* Returns true if DMA mode can be used
|
||||
*/
|
||||
|
||||
static int config_chipset_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, ali15x3_ratemask(drive));
|
||||
|
||||
if (!(speed))
|
||||
return 0;
|
||||
|
||||
(void) ali15x3_tune_chipset(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
/**
|
||||
* ali15x3_config_drive_for_dma - configure for DMA
|
||||
* @drive: drive to configure
|
||||
*
|
||||
* Configure a drive for DMA operation. If DMA is not possible we
|
||||
* drop the drive into PIO mode instead.
|
||||
*/
|
||||
|
||||
static int ali15x3_config_drive_for_dma(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct hd_driveid *id = drive->id;
|
||||
|
||||
if ((m5229_revision<=0x20) && (drive->media!=ide_disk))
|
||||
goto no_dma_set;
|
||||
|
||||
drive->init_speed = 0;
|
||||
|
||||
if ((id != NULL) && ((id->capability & 1) != 0) && drive->autodma) {
|
||||
/* Consult the list of known "bad" drives */
|
||||
if (__ide_dma_bad_drive(drive))
|
||||
goto ata_pio;
|
||||
if ((id->field_valid & 4) && (m5229_revision >= 0xC2)) {
|
||||
if (id->dma_ultra & hwif->ultra_mask) {
|
||||
/* Force if Capable UltraDMA */
|
||||
int dma = config_chipset_for_dma(drive);
|
||||
if ((id->field_valid & 2) && !dma)
|
||||
goto try_dma_modes;
|
||||
}
|
||||
} else if (id->field_valid & 2) {
|
||||
try_dma_modes:
|
||||
if ((id->dma_mword & hwif->mwdma_mask) ||
|
||||
(id->dma_1word & hwif->swdma_mask)) {
|
||||
/* Force if Capable regular DMA modes */
|
||||
if (!config_chipset_for_dma(drive))
|
||||
goto no_dma_set;
|
||||
}
|
||||
} else if (__ide_dma_good_drive(drive) &&
|
||||
(id->eide_dma_time < 150)) {
|
||||
/* Consult the list of known "good" drives */
|
||||
if (!config_chipset_for_dma(drive))
|
||||
goto no_dma_set;
|
||||
} else {
|
||||
goto ata_pio;
|
||||
}
|
||||
} else {
|
||||
ata_pio:
|
||||
hwif->tuneproc(drive, 255);
|
||||
no_dma_set:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ali15x3_dma_setup - begin a DMA phase
|
||||
* @drive: target device
|
||||
*
|
||||
* Returns 1 if the DMA cannot be performed, zero on success.
|
||||
*/
|
||||
|
||||
static int ali15x3_dma_setup(ide_drive_t *drive)
|
||||
{
|
||||
if (m5229_revision < 0xC2 && drive->media != ide_disk) {
|
||||
if (rq_data_dir(drive->hwif->hwgroup->rq))
|
||||
return 1; /* try PIO instead of DMA */
|
||||
}
|
||||
return ide_dma_setup(drive);
|
||||
}
|
||||
|
||||
/**
|
||||
* init_chipset_ali15x3 - Initialise an ALi IDE controller
|
||||
* @dev: PCI device
|
||||
* @name: Name of the controller
|
||||
*
|
||||
* This function initializes the ALI IDE controller and where
|
||||
* appropriate also sets up the 1533 southbridge.
|
||||
*/
|
||||
|
||||
static unsigned int __devinit init_chipset_ali15x3 (struct pci_dev *dev, const char *name)
|
||||
{
|
||||
unsigned long flags;
|
||||
u8 tmpbyte;
|
||||
struct pci_dev *north = pci_get_slot(dev->bus, PCI_DEVFN(0,0));
|
||||
|
||||
pci_read_config_byte(dev, PCI_REVISION_ID, &m5229_revision);
|
||||
|
||||
isa_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL);
|
||||
|
||||
#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
|
||||
if (!ali_proc) {
|
||||
ali_proc = 1;
|
||||
bmide_dev = dev;
|
||||
ide_pci_create_host_proc("ali", ali_get_info);
|
||||
}
|
||||
#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
if (m5229_revision < 0xC2) {
|
||||
/*
|
||||
* revision 0x20 (1543-E, 1543-F)
|
||||
* revision 0xC0, 0xC1 (1543C-C, 1543C-D, 1543C-E)
|
||||
* clear CD-ROM DMA write bit, m5229, 0x4b, bit 7
|
||||
*/
|
||||
pci_read_config_byte(dev, 0x4b, &tmpbyte);
|
||||
/*
|
||||
* clear bit 7
|
||||
*/
|
||||
pci_write_config_byte(dev, 0x4b, tmpbyte & 0x7F);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1543C-B?, 1535, 1535D, 1553
|
||||
* Note 1: not all "motherboard" support this detection
|
||||
* Note 2: if no udma 66 device, the detection may "error".
|
||||
* but in this case, we will not set the device to
|
||||
* ultra 66, the detection result is not important
|
||||
*/
|
||||
|
||||
/*
|
||||
* enable "Cable Detection", m5229, 0x4b, bit3
|
||||
*/
|
||||
pci_read_config_byte(dev, 0x4b, &tmpbyte);
|
||||
pci_write_config_byte(dev, 0x4b, tmpbyte | 0x08);
|
||||
|
||||
/*
|
||||
* We should only tune the 1533 enable if we are using an ALi
|
||||
* North bridge. We might have no north found on some zany
|
||||
* box without a device at 0:0.0. The ALi bridge will be at
|
||||
* 0:0.0 so if we didn't find one we know what is cooking.
|
||||
*/
|
||||
if (north && north->vendor != PCI_VENDOR_ID_AL)
|
||||
goto out;
|
||||
|
||||
if (m5229_revision < 0xC5 && isa_dev)
|
||||
{
|
||||
/*
|
||||
* set south-bridge's enable bit, m1533, 0x79
|
||||
*/
|
||||
|
||||
pci_read_config_byte(isa_dev, 0x79, &tmpbyte);
|
||||
if (m5229_revision == 0xC2) {
|
||||
/*
|
||||
* 1543C-B0 (m1533, 0x79, bit 2)
|
||||
*/
|
||||
pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x04);
|
||||
} else if (m5229_revision >= 0xC3) {
|
||||
/*
|
||||
* 1553/1535 (m1533, 0x79, bit 1)
|
||||
*/
|
||||
pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x02);
|
||||
}
|
||||
}
|
||||
out:
|
||||
pci_dev_put(north);
|
||||
pci_dev_put(isa_dev);
|
||||
local_irq_restore(flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ata66_ali15x3 - check for UDMA 66 support
|
||||
* @hwif: IDE interface
|
||||
*
|
||||
* This checks if the controller and the cable are capable
|
||||
* of UDMA66 transfers. It doesn't check the drives.
|
||||
* But see note 2 below!
|
||||
*
|
||||
* FIXME: frobs bits that are not defined on newer ALi devicea
|
||||
*/
|
||||
|
||||
static unsigned int __devinit ata66_ali15x3 (ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
unsigned int ata66 = 0;
|
||||
u8 cable_80_pin[2] = { 0, 0 };
|
||||
|
||||
unsigned long flags;
|
||||
u8 tmpbyte;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
if (m5229_revision >= 0xC2) {
|
||||
/*
|
||||
* Ultra66 cable detection (from Host View)
|
||||
* m5229, 0x4a, bit0: primary, bit1: secondary 80 pin
|
||||
*/
|
||||
pci_read_config_byte(dev, 0x4a, &tmpbyte);
|
||||
/*
|
||||
* 0x4a, bit0 is 0 => primary channel
|
||||
* has 80-pin (from host view)
|
||||
*/
|
||||
if (!(tmpbyte & 0x01)) cable_80_pin[0] = 1;
|
||||
/*
|
||||
* 0x4a, bit1 is 0 => secondary channel
|
||||
* has 80-pin (from host view)
|
||||
*/
|
||||
if (!(tmpbyte & 0x02)) cable_80_pin[1] = 1;
|
||||
/*
|
||||
* Allow ata66 if cable of current channel has 80 pins
|
||||
*/
|
||||
ata66 = (hwif->channel)?cable_80_pin[1]:cable_80_pin[0];
|
||||
} else {
|
||||
/*
|
||||
* check m1533, 0x5e, bit 1~4 == 1001 => & 00011110 = 00010010
|
||||
*/
|
||||
pci_read_config_byte(isa_dev, 0x5e, &tmpbyte);
|
||||
chip_is_1543c_e = ((tmpbyte & 0x1e) == 0x12) ? 1: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* CD_ROM DMA on (m5229, 0x53, bit0)
|
||||
* Enable this bit even if we want to use PIO
|
||||
* PIO FIFO off (m5229, 0x53, bit1)
|
||||
* The hardware will use 0x54h and 0x55h to control PIO FIFO
|
||||
* (Not on later devices it seems)
|
||||
*
|
||||
* 0x53 changes meaning on later revs - we must no touch
|
||||
* bit 1 on them. Need to check if 0x20 is the right break
|
||||
*/
|
||||
|
||||
pci_read_config_byte(dev, 0x53, &tmpbyte);
|
||||
|
||||
if(m5229_revision <= 0x20)
|
||||
tmpbyte = (tmpbyte & (~0x02)) | 0x01;
|
||||
else if (m5229_revision == 0xc7 || m5229_revision == 0xc8)
|
||||
tmpbyte |= 0x03;
|
||||
else
|
||||
tmpbyte |= 0x01;
|
||||
|
||||
pci_write_config_byte(dev, 0x53, tmpbyte);
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
return(ata66);
|
||||
}
|
||||
|
||||
/**
|
||||
* init_hwif_common_ali15x3 - Set up ALI IDE hardware
|
||||
* @hwif: IDE interface
|
||||
*
|
||||
* Initialize the IDE structure side of the ALi 15x3 driver.
|
||||
*/
|
||||
|
||||
static void __devinit init_hwif_common_ali15x3 (ide_hwif_t *hwif)
|
||||
{
|
||||
hwif->autodma = 0;
|
||||
hwif->tuneproc = &ali15x3_tune_drive;
|
||||
hwif->speedproc = &ali15x3_tune_chipset;
|
||||
|
||||
/* don't use LBA48 DMA on ALi devices before rev 0xC5 */
|
||||
hwif->no_lba48_dma = (m5229_revision <= 0xC4) ? 1 : 0;
|
||||
|
||||
if (!hwif->dma_base) {
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].autotune = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
|
||||
if (m5229_revision > 0x20)
|
||||
hwif->ultra_mask = 0x7f;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
hwif->swdma_mask = 0x07;
|
||||
|
||||
if (m5229_revision >= 0x20) {
|
||||
/*
|
||||
* M1543C or newer for DMAing
|
||||
*/
|
||||
hwif->ide_dma_check = &ali15x3_config_drive_for_dma;
|
||||
hwif->dma_setup = &ali15x3_dma_setup;
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
if (!(hwif->udma_four))
|
||||
hwif->udma_four = ata66_ali15x3(hwif);
|
||||
}
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_hwif_ali15x3 - Initialize the ALI IDE x86 stuff
|
||||
* @hwif: interface to configure
|
||||
*
|
||||
* Obtain the IRQ tables for an ALi based IDE solution on the PC
|
||||
* class platforms. This part of the code isn't applicable to the
|
||||
* Sparc systems
|
||||
*/
|
||||
|
||||
static void __devinit init_hwif_ali15x3 (ide_hwif_t *hwif)
|
||||
{
|
||||
u8 ideic, inmir;
|
||||
s8 irq_routing_table[] = { -1, 9, 3, 10, 4, 5, 7, 6,
|
||||
1, 11, 0, 12, 0, 14, 0, 15 };
|
||||
int irq = -1;
|
||||
|
||||
if (hwif->pci_dev->device == PCI_DEVICE_ID_AL_M5229)
|
||||
hwif->irq = hwif->channel ? 15 : 14;
|
||||
|
||||
if (isa_dev) {
|
||||
/*
|
||||
* read IDE interface control
|
||||
*/
|
||||
pci_read_config_byte(isa_dev, 0x58, &ideic);
|
||||
|
||||
/* bit0, bit1 */
|
||||
ideic = ideic & 0x03;
|
||||
|
||||
/* get IRQ for IDE Controller */
|
||||
if ((hwif->channel && ideic == 0x03) ||
|
||||
(!hwif->channel && !ideic)) {
|
||||
/*
|
||||
* get SIRQ1 routing table
|
||||
*/
|
||||
pci_read_config_byte(isa_dev, 0x44, &inmir);
|
||||
inmir = inmir & 0x0f;
|
||||
irq = irq_routing_table[inmir];
|
||||
} else if (hwif->channel && !(ideic & 0x01)) {
|
||||
/*
|
||||
* get SIRQ2 routing table
|
||||
*/
|
||||
pci_read_config_byte(isa_dev, 0x75, &inmir);
|
||||
inmir = inmir & 0x0f;
|
||||
irq = irq_routing_table[inmir];
|
||||
}
|
||||
if(irq >= 0)
|
||||
hwif->irq = irq;
|
||||
}
|
||||
|
||||
init_hwif_common_ali15x3(hwif);
|
||||
}
|
||||
|
||||
/**
|
||||
* init_dma_ali15x3 - set up DMA on ALi15x3
|
||||
* @hwif: IDE interface
|
||||
* @dmabase: DMA interface base PCI address
|
||||
*
|
||||
* Set up the DMA functionality on the ALi 15x3. For the ALi
|
||||
* controllers this is generic so we can let the generic code do
|
||||
* the actual work.
|
||||
*/
|
||||
|
||||
static void __devinit init_dma_ali15x3 (ide_hwif_t *hwif, unsigned long dmabase)
|
||||
{
|
||||
if (m5229_revision < 0x20)
|
||||
return;
|
||||
if (!hwif->channel)
|
||||
outb(inb(dmabase + 2) & 0x60, dmabase + 2);
|
||||
ide_setup_dma(hwif, dmabase, 8);
|
||||
}
|
||||
|
||||
static ide_pci_device_t ali15x3_chipset __devinitdata = {
|
||||
.name = "ALI15X3",
|
||||
.init_chipset = init_chipset_ali15x3,
|
||||
.init_hwif = init_hwif_ali15x3,
|
||||
.init_dma = init_dma_ali15x3,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
};
|
||||
|
||||
/**
|
||||
* alim15x3_init_one - set up an ALi15x3 IDE controller
|
||||
* @dev: PCI device to set up
|
||||
*
|
||||
* Perform the actual set up for an ALi15x3 that has been found by the
|
||||
* hot plug layer.
|
||||
*/
|
||||
|
||||
static int __devinit alim15x3_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
static struct pci_device_id ati_rs100[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS100) },
|
||||
{ },
|
||||
};
|
||||
|
||||
ide_pci_device_t *d = &ali15x3_chipset;
|
||||
|
||||
if (pci_dev_present(ati_rs100))
|
||||
printk(KERN_WARNING "alim15x3: ATI Radeon IGP Northbridge is not yet fully tested.\n");
|
||||
|
||||
#if defined(CONFIG_SPARC64)
|
||||
d->init_hwif = init_hwif_common_ali15x3;
|
||||
#endif /* CONFIG_SPARC64 */
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
|
||||
static struct pci_device_id alim15x3_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5228, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, alim15x3_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "ALI15x3_IDE",
|
||||
.id_table = alim15x3_pci_tbl,
|
||||
.probe = alim15x3_init_one,
|
||||
};
|
||||
|
||||
static int __init ali15x3_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(ali15x3_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Michael Aubry, Andrzej Krzysztofowicz, CJ, Andre Hedrick, Alan Cox");
|
||||
MODULE_DESCRIPTION("PCI driver module for ALi 15x3 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
557
drivers/ide/pci/amd74xx.c
Normal file
557
drivers/ide/pci/amd74xx.c
Normal file
@@ -0,0 +1,557 @@
|
||||
/*
|
||||
* Version 2.13
|
||||
*
|
||||
* AMD 755/756/766/8111 and nVidia nForce/2/2s/3/3s/CK804/MCP04
|
||||
* IDE driver for Linux.
|
||||
*
|
||||
* Copyright (c) 2000-2002 Vojtech Pavlik
|
||||
*
|
||||
* Based on the work of:
|
||||
* Andre Hedrick
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "ide-timing.h"
|
||||
|
||||
#define DISPLAY_AMD_TIMINGS
|
||||
|
||||
#define AMD_IDE_ENABLE (0x00 + amd_config->base)
|
||||
#define AMD_IDE_CONFIG (0x01 + amd_config->base)
|
||||
#define AMD_CABLE_DETECT (0x02 + amd_config->base)
|
||||
#define AMD_DRIVE_TIMING (0x08 + amd_config->base)
|
||||
#define AMD_8BIT_TIMING (0x0e + amd_config->base)
|
||||
#define AMD_ADDRESS_SETUP (0x0c + amd_config->base)
|
||||
#define AMD_UDMA_TIMING (0x10 + amd_config->base)
|
||||
|
||||
#define AMD_UDMA 0x07
|
||||
#define AMD_UDMA_33 0x01
|
||||
#define AMD_UDMA_66 0x02
|
||||
#define AMD_UDMA_100 0x03
|
||||
#define AMD_UDMA_133 0x04
|
||||
#define AMD_CHECK_SWDMA 0x08
|
||||
#define AMD_BAD_SWDMA 0x10
|
||||
#define AMD_BAD_FIFO 0x20
|
||||
#define AMD_CHECK_SERENADE 0x40
|
||||
|
||||
/*
|
||||
* AMD SouthBridge chips.
|
||||
*/
|
||||
|
||||
static struct amd_ide_chip {
|
||||
unsigned short id;
|
||||
unsigned long base;
|
||||
unsigned char flags;
|
||||
} amd_ide_chips[] = {
|
||||
{ PCI_DEVICE_ID_AMD_COBRA_7401, 0x40, AMD_UDMA_33 | AMD_BAD_SWDMA },
|
||||
{ PCI_DEVICE_ID_AMD_VIPER_7409, 0x40, AMD_UDMA_66 | AMD_CHECK_SWDMA },
|
||||
{ PCI_DEVICE_ID_AMD_VIPER_7411, 0x40, AMD_UDMA_100 | AMD_BAD_FIFO },
|
||||
{ PCI_DEVICE_ID_AMD_OPUS_7441, 0x40, AMD_UDMA_100 },
|
||||
{ PCI_DEVICE_ID_AMD_8111_IDE, 0x40, AMD_UDMA_133 | AMD_CHECK_SERENADE },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE_IDE, 0x50, AMD_UDMA_100 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE, 0x50, AMD_UDMA_133 },
|
||||
{ PCI_DEVICE_ID_AMD_CS5536_IDE, 0x40, AMD_UDMA_100 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static struct amd_ide_chip *amd_config;
|
||||
static ide_pci_device_t *amd_chipset;
|
||||
static unsigned int amd_80w;
|
||||
static unsigned int amd_clock;
|
||||
|
||||
static char *amd_dma[] = { "MWDMA16", "UDMA33", "UDMA66", "UDMA100", "UDMA133" };
|
||||
static unsigned char amd_cyc2udma[] = { 6, 6, 5, 4, 0, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 7 };
|
||||
|
||||
/*
|
||||
* AMD /proc entry.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
#include <linux/stat.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
static u8 amd74xx_proc;
|
||||
|
||||
static unsigned char amd_udma2cyc[] = { 4, 6, 8, 10, 3, 2, 1, 15 };
|
||||
static unsigned long amd_base;
|
||||
static struct pci_dev *bmide_dev;
|
||||
extern int (*amd74xx_display_info)(char *, char **, off_t, int); /* ide-proc.c */
|
||||
|
||||
#define amd_print(format, arg...) p += sprintf(p, format "\n" , ## arg)
|
||||
#define amd_print_drive(name, format, arg...)\
|
||||
p += sprintf(p, name); for (i = 0; i < 4; i++) p += sprintf(p, format, ## arg); p += sprintf(p, "\n");
|
||||
|
||||
static int amd74xx_get_info(char *buffer, char **addr, off_t offset, int count)
|
||||
{
|
||||
int speed[4], cycle[4], setup[4], active[4], recover[4], den[4],
|
||||
uen[4], udma[4], active8b[4], recover8b[4];
|
||||
struct pci_dev *dev = bmide_dev;
|
||||
unsigned int v, u, i;
|
||||
unsigned short c, w;
|
||||
unsigned char t;
|
||||
int len;
|
||||
char *p = buffer;
|
||||
|
||||
amd_print("----------AMD BusMastering IDE Configuration----------------");
|
||||
|
||||
amd_print("Driver Version: 2.13");
|
||||
amd_print("South Bridge: %s", pci_name(bmide_dev));
|
||||
|
||||
pci_read_config_byte(dev, PCI_REVISION_ID, &t);
|
||||
amd_print("Revision: IDE %#x", t);
|
||||
amd_print("Highest DMA rate: %s", amd_dma[amd_config->flags & AMD_UDMA]);
|
||||
|
||||
amd_print("BM-DMA base: %#lx", amd_base);
|
||||
amd_print("PCI clock: %d.%dMHz", amd_clock / 1000, amd_clock / 100 % 10);
|
||||
|
||||
amd_print("-----------------------Primary IDE-------Secondary IDE------");
|
||||
|
||||
pci_read_config_byte(dev, AMD_IDE_CONFIG, &t);
|
||||
amd_print("Prefetch Buffer: %10s%20s", (t & 0x80) ? "yes" : "no", (t & 0x20) ? "yes" : "no");
|
||||
amd_print("Post Write Buffer: %10s%20s", (t & 0x40) ? "yes" : "no", (t & 0x10) ? "yes" : "no");
|
||||
|
||||
pci_read_config_byte(dev, AMD_IDE_ENABLE, &t);
|
||||
amd_print("Enabled: %10s%20s", (t & 0x02) ? "yes" : "no", (t & 0x01) ? "yes" : "no");
|
||||
|
||||
c = inb(amd_base + 0x02) | (inb(amd_base + 0x0a) << 8);
|
||||
amd_print("Simplex only: %10s%20s", (c & 0x80) ? "yes" : "no", (c & 0x8000) ? "yes" : "no");
|
||||
|
||||
amd_print("Cable Type: %10s%20s", (amd_80w & 1) ? "80w" : "40w", (amd_80w & 2) ? "80w" : "40w");
|
||||
|
||||
if (!amd_clock)
|
||||
return p - buffer;
|
||||
|
||||
amd_print("-------------------drive0----drive1----drive2----drive3-----");
|
||||
|
||||
pci_read_config_byte(dev, AMD_ADDRESS_SETUP, &t);
|
||||
pci_read_config_dword(dev, AMD_DRIVE_TIMING, &v);
|
||||
pci_read_config_word(dev, AMD_8BIT_TIMING, &w);
|
||||
pci_read_config_dword(dev, AMD_UDMA_TIMING, &u);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
setup[i] = ((t >> ((3 - i) << 1)) & 0x3) + 1;
|
||||
recover8b[i] = ((w >> ((1 - (i >> 1)) << 3)) & 0xf) + 1;
|
||||
active8b[i] = ((w >> (((1 - (i >> 1)) << 3) + 4)) & 0xf) + 1;
|
||||
active[i] = ((v >> (((3 - i) << 3) + 4)) & 0xf) + 1;
|
||||
recover[i] = ((v >> ((3 - i) << 3)) & 0xf) + 1;
|
||||
|
||||
udma[i] = amd_udma2cyc[((u >> ((3 - i) << 3)) & 0x7)];
|
||||
uen[i] = ((u >> ((3 - i) << 3)) & 0x40) ? 1 : 0;
|
||||
den[i] = (c & ((i & 1) ? 0x40 : 0x20) << ((i & 2) << 2));
|
||||
|
||||
if (den[i] && uen[i] && udma[i] == 1) {
|
||||
speed[i] = amd_clock * 3;
|
||||
cycle[i] = 666666 / amd_clock;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (den[i] && uen[i] && udma[i] == 15) {
|
||||
speed[i] = amd_clock * 4;
|
||||
cycle[i] = 500000 / amd_clock;
|
||||
continue;
|
||||
}
|
||||
|
||||
speed[i] = 4 * amd_clock / ((den[i] && uen[i]) ? udma[i] : (active[i] + recover[i]) * 2);
|
||||
cycle[i] = 1000000 * ((den[i] && uen[i]) ? udma[i] : (active[i] + recover[i]) * 2) / amd_clock / 2;
|
||||
}
|
||||
|
||||
amd_print_drive("Transfer Mode: ", "%10s", den[i] ? (uen[i] ? "UDMA" : "DMA") : "PIO");
|
||||
|
||||
amd_print_drive("Address Setup: ", "%8dns", 1000000 * setup[i] / amd_clock);
|
||||
amd_print_drive("Cmd Active: ", "%8dns", 1000000 * active8b[i] / amd_clock);
|
||||
amd_print_drive("Cmd Recovery: ", "%8dns", 1000000 * recover8b[i] / amd_clock);
|
||||
amd_print_drive("Data Active: ", "%8dns", 1000000 * active[i] / amd_clock);
|
||||
amd_print_drive("Data Recovery: ", "%8dns", 1000000 * recover[i] / amd_clock);
|
||||
amd_print_drive("Cycle Time: ", "%8dns", cycle[i]);
|
||||
amd_print_drive("Transfer Rate: ", "%4d.%dMB/s", speed[i] / 1000, speed[i] / 100 % 10);
|
||||
|
||||
/* hoping p - buffer is less than 4K... */
|
||||
len = (p - buffer) - offset;
|
||||
*addr = buffer + offset;
|
||||
|
||||
return len > count ? count : len;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* amd_set_speed() writes timing values to the chipset registers
|
||||
*/
|
||||
|
||||
static void amd_set_speed(struct pci_dev *dev, unsigned char dn, struct ide_timing *timing)
|
||||
{
|
||||
unsigned char t;
|
||||
|
||||
pci_read_config_byte(dev, AMD_ADDRESS_SETUP, &t);
|
||||
t = (t & ~(3 << ((3 - dn) << 1))) | ((FIT(timing->setup, 1, 4) - 1) << ((3 - dn) << 1));
|
||||
pci_write_config_byte(dev, AMD_ADDRESS_SETUP, t);
|
||||
|
||||
pci_write_config_byte(dev, AMD_8BIT_TIMING + (1 - (dn >> 1)),
|
||||
((FIT(timing->act8b, 1, 16) - 1) << 4) | (FIT(timing->rec8b, 1, 16) - 1));
|
||||
|
||||
pci_write_config_byte(dev, AMD_DRIVE_TIMING + (3 - dn),
|
||||
((FIT(timing->active, 1, 16) - 1) << 4) | (FIT(timing->recover, 1, 16) - 1));
|
||||
|
||||
switch (amd_config->flags & AMD_UDMA) {
|
||||
case AMD_UDMA_33: t = timing->udma ? (0xc0 | (FIT(timing->udma, 2, 5) - 2)) : 0x03; break;
|
||||
case AMD_UDMA_66: t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 2, 10)]) : 0x03; break;
|
||||
case AMD_UDMA_100: t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 1, 10)]) : 0x03; break;
|
||||
case AMD_UDMA_133: t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 1, 15)]) : 0x03; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
pci_write_config_byte(dev, AMD_UDMA_TIMING + (3 - dn), t);
|
||||
}
|
||||
|
||||
/*
|
||||
* amd_set_drive() computes timing values configures the drive and
|
||||
* the chipset to a desired transfer mode. It also can be called
|
||||
* by upper layers.
|
||||
*/
|
||||
|
||||
static int amd_set_drive(ide_drive_t *drive, u8 speed)
|
||||
{
|
||||
ide_drive_t *peer = HWIF(drive)->drives + (~drive->dn & 1);
|
||||
struct ide_timing t, p;
|
||||
int T, UT;
|
||||
|
||||
if (speed != XFER_PIO_SLOW && speed != drive->current_speed)
|
||||
if (ide_config_drive_speed(drive, speed))
|
||||
printk(KERN_WARNING "ide%d: Drive %d didn't accept speed setting. Oh, well.\n",
|
||||
drive->dn >> 1, drive->dn & 1);
|
||||
|
||||
T = 1000000000 / amd_clock;
|
||||
UT = T / min_t(int, max_t(int, amd_config->flags & AMD_UDMA, 1), 2);
|
||||
|
||||
ide_timing_compute(drive, speed, &t, T, UT);
|
||||
|
||||
if (peer->present) {
|
||||
ide_timing_compute(peer, peer->current_speed, &p, T, UT);
|
||||
ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT);
|
||||
}
|
||||
|
||||
if (speed == XFER_UDMA_5 && amd_clock <= 33333) t.udma = 1;
|
||||
if (speed == XFER_UDMA_6 && amd_clock <= 33333) t.udma = 15;
|
||||
|
||||
amd_set_speed(HWIF(drive)->pci_dev, drive->dn, &t);
|
||||
|
||||
if (!drive->init_speed)
|
||||
drive->init_speed = speed;
|
||||
drive->current_speed = speed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* amd74xx_tune_drive() is a callback from upper layers for
|
||||
* PIO-only tuning.
|
||||
*/
|
||||
|
||||
static void amd74xx_tune_drive(ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
if (pio == 255) {
|
||||
amd_set_drive(drive, ide_find_best_mode(drive, XFER_PIO | XFER_EPIO));
|
||||
return;
|
||||
}
|
||||
|
||||
amd_set_drive(drive, XFER_PIO_0 + min_t(byte, pio, 5));
|
||||
}
|
||||
|
||||
/*
|
||||
* amd74xx_dmaproc() is a callback from upper layers that can do
|
||||
* a lot, but we use it for DMA/PIO tuning only, delegating everything
|
||||
* else to the default ide_dmaproc().
|
||||
*/
|
||||
|
||||
static int amd74xx_ide_dma_check(ide_drive_t *drive)
|
||||
{
|
||||
int w80 = HWIF(drive)->udma_four;
|
||||
|
||||
u8 speed = ide_find_best_mode(drive,
|
||||
XFER_PIO | XFER_EPIO | XFER_MWDMA | XFER_UDMA |
|
||||
((amd_config->flags & AMD_BAD_SWDMA) ? 0 : XFER_SWDMA) |
|
||||
(w80 && (amd_config->flags & AMD_UDMA) >= AMD_UDMA_66 ? XFER_UDMA_66 : 0) |
|
||||
(w80 && (amd_config->flags & AMD_UDMA) >= AMD_UDMA_100 ? XFER_UDMA_100 : 0) |
|
||||
(w80 && (amd_config->flags & AMD_UDMA) >= AMD_UDMA_133 ? XFER_UDMA_133 : 0));
|
||||
|
||||
amd_set_drive(drive, speed);
|
||||
|
||||
if (drive->autodma && (speed & XFER_MODE) != XFER_PIO)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The initialization callback. Here we determine the IDE chip type
|
||||
* and initialize its drive independent registers.
|
||||
*/
|
||||
|
||||
static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev, const char *name)
|
||||
{
|
||||
unsigned char t;
|
||||
unsigned int u;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Check for bad SWDMA.
|
||||
*/
|
||||
|
||||
if (amd_config->flags & AMD_CHECK_SWDMA) {
|
||||
pci_read_config_byte(dev, PCI_REVISION_ID, &t);
|
||||
if (t <= 7)
|
||||
amd_config->flags |= AMD_BAD_SWDMA;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check 80-wire cable presence.
|
||||
*/
|
||||
|
||||
switch (amd_config->flags & AMD_UDMA) {
|
||||
|
||||
case AMD_UDMA_133:
|
||||
case AMD_UDMA_100:
|
||||
pci_read_config_byte(dev, AMD_CABLE_DETECT, &t);
|
||||
pci_read_config_dword(dev, AMD_UDMA_TIMING, &u);
|
||||
amd_80w = ((t & 0x3) ? 1 : 0) | ((t & 0xc) ? 2 : 0);
|
||||
for (i = 24; i >= 0; i -= 8)
|
||||
if (((u >> i) & 4) && !(amd_80w & (1 << (1 - (i >> 4))))) {
|
||||
printk(KERN_WARNING "%s: BIOS didn't set cable bits correctly. Enabling workaround.\n",
|
||||
amd_chipset->name);
|
||||
amd_80w |= (1 << (1 - (i >> 4)));
|
||||
}
|
||||
break;
|
||||
|
||||
case AMD_UDMA_66:
|
||||
/* no host side cable detection */
|
||||
amd_80w = 0x03;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Take care of prefetch & postwrite.
|
||||
*/
|
||||
|
||||
pci_read_config_byte(dev, AMD_IDE_CONFIG, &t);
|
||||
pci_write_config_byte(dev, AMD_IDE_CONFIG,
|
||||
(amd_config->flags & AMD_BAD_FIFO) ? (t & 0x0f) : (t | 0xf0));
|
||||
|
||||
/*
|
||||
* Take care of incorrectly wired Serenade mainboards.
|
||||
*/
|
||||
|
||||
if ((amd_config->flags & AMD_CHECK_SERENADE) &&
|
||||
dev->subsystem_vendor == PCI_VENDOR_ID_AMD &&
|
||||
dev->subsystem_device == PCI_DEVICE_ID_AMD_SERENADE)
|
||||
amd_config->flags = AMD_UDMA_100;
|
||||
|
||||
/*
|
||||
* Determine the system bus clock.
|
||||
*/
|
||||
|
||||
amd_clock = system_bus_clock() * 1000;
|
||||
|
||||
switch (amd_clock) {
|
||||
case 33000: amd_clock = 33333; break;
|
||||
case 37000: amd_clock = 37500; break;
|
||||
case 41000: amd_clock = 41666; break;
|
||||
}
|
||||
|
||||
if (amd_clock < 20000 || amd_clock > 50000) {
|
||||
printk(KERN_WARNING "%s: User given PCI clock speed impossible (%d), using 33 MHz instead.\n",
|
||||
amd_chipset->name, amd_clock);
|
||||
amd_clock = 33333;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the boot message.
|
||||
*/
|
||||
|
||||
pci_read_config_byte(dev, PCI_REVISION_ID, &t);
|
||||
printk(KERN_INFO "%s: %s (rev %02x) %s controller\n",
|
||||
amd_chipset->name, pci_name(dev), t, amd_dma[amd_config->flags & AMD_UDMA]);
|
||||
|
||||
/*
|
||||
* Register /proc/ide/amd74xx entry
|
||||
*/
|
||||
|
||||
#if defined(DISPLAY_AMD_TIMINGS) && defined(CONFIG_PROC_FS)
|
||||
if (!amd74xx_proc) {
|
||||
amd_base = pci_resource_start(dev, 4);
|
||||
bmide_dev = dev;
|
||||
ide_pci_create_host_proc("amd74xx", amd74xx_get_info);
|
||||
amd74xx_proc = 1;
|
||||
}
|
||||
#endif /* DISPLAY_AMD_TIMINGS && CONFIG_PROC_FS */
|
||||
|
||||
return dev->irq;
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_amd74xx(ide_hwif_t *hwif)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (hwif->irq == 0) /* 0 is bogus but will do for now */
|
||||
hwif->irq = pci_get_legacy_ide_irq(hwif->pci_dev, hwif->channel);
|
||||
|
||||
hwif->autodma = 0;
|
||||
|
||||
hwif->tuneproc = &amd74xx_tune_drive;
|
||||
hwif->speedproc = &amd_set_drive;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
hwif->drives[i].io_32bit = 1;
|
||||
hwif->drives[i].unmask = 1;
|
||||
hwif->drives[i].autotune = 1;
|
||||
hwif->drives[i].dn = hwif->channel * 2 + i;
|
||||
}
|
||||
|
||||
if (!hwif->dma_base)
|
||||
return;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x7f;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
hwif->swdma_mask = 0x07;
|
||||
|
||||
if (!hwif->udma_four)
|
||||
hwif->udma_four = (amd_80w >> hwif->channel) & 1;
|
||||
hwif->ide_dma_check = &amd74xx_ide_dma_check;
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
#define DECLARE_AMD_DEV(name_str) \
|
||||
{ \
|
||||
.name = name_str, \
|
||||
.init_chipset = init_chipset_amd74xx, \
|
||||
.init_hwif = init_hwif_amd74xx, \
|
||||
.channels = 2, \
|
||||
.autodma = AUTODMA, \
|
||||
.enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, \
|
||||
.bootable = ON_BOARD, \
|
||||
}
|
||||
|
||||
#define DECLARE_NV_DEV(name_str) \
|
||||
{ \
|
||||
.name = name_str, \
|
||||
.init_chipset = init_chipset_amd74xx, \
|
||||
.init_hwif = init_hwif_amd74xx, \
|
||||
.channels = 2, \
|
||||
.autodma = AUTODMA, \
|
||||
.enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, \
|
||||
.bootable = ON_BOARD, \
|
||||
}
|
||||
|
||||
static ide_pci_device_t amd74xx_chipsets[] __devinitdata = {
|
||||
/* 0 */ DECLARE_AMD_DEV("AMD7401"),
|
||||
/* 1 */ DECLARE_AMD_DEV("AMD7409"),
|
||||
/* 2 */ DECLARE_AMD_DEV("AMD7411"),
|
||||
/* 3 */ DECLARE_AMD_DEV("AMD7441"),
|
||||
/* 4 */ DECLARE_AMD_DEV("AMD8111"),
|
||||
|
||||
/* 5 */ DECLARE_NV_DEV("NFORCE"),
|
||||
/* 6 */ DECLARE_NV_DEV("NFORCE2"),
|
||||
/* 7 */ DECLARE_NV_DEV("NFORCE2-U400R"),
|
||||
/* 8 */ DECLARE_NV_DEV("NFORCE2-U400R-SATA"),
|
||||
/* 9 */ DECLARE_NV_DEV("NFORCE3-150"),
|
||||
/* 10 */ DECLARE_NV_DEV("NFORCE3-250"),
|
||||
/* 11 */ DECLARE_NV_DEV("NFORCE3-250-SATA"),
|
||||
/* 12 */ DECLARE_NV_DEV("NFORCE3-250-SATA2"),
|
||||
/* 13 */ DECLARE_NV_DEV("NFORCE-CK804"),
|
||||
/* 14 */ DECLARE_NV_DEV("NFORCE-MCP04"),
|
||||
/* 15 */ DECLARE_NV_DEV("NFORCE-MCP51"),
|
||||
/* 16 */ DECLARE_NV_DEV("NFORCE-MCP55"),
|
||||
/* 17 */ DECLARE_NV_DEV("NFORCE-MCP61"),
|
||||
/* 18 */ DECLARE_NV_DEV("NFORCE-MCP65"),
|
||||
/* 19 */ DECLARE_NV_DEV("NFORCE-MCP67"),
|
||||
/* 20 */ DECLARE_AMD_DEV("AMD5536"),
|
||||
};
|
||||
|
||||
static int __devinit amd74xx_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
amd_chipset = amd74xx_chipsets + id->driver_data;
|
||||
amd_config = amd_ide_chips + id->driver_data;
|
||||
if (dev->device != amd_config->id) {
|
||||
printk(KERN_ERR "%s: assertion 0x%02x == 0x%02x failed !\n",
|
||||
pci_name(dev), dev->device, amd_config->id);
|
||||
return -ENODEV;
|
||||
}
|
||||
return ide_setup_pci_device(dev, amd_chipset);
|
||||
}
|
||||
|
||||
static struct pci_device_id amd74xx_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_COBRA_7401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7409, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
|
||||
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 },
|
||||
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7441, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 },
|
||||
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 },
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 },
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 },
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7 },
|
||||
#ifdef CONFIG_BLK_DEV_IDE_SATA
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 },
|
||||
#endif
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9 },
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10 },
|
||||
#ifdef CONFIG_BLK_DEV_IDE_SATA
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11 },
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12 },
|
||||
#endif
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13 },
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 14 },
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 15 },
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 16 },
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 17 },
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 18 },
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 19 },
|
||||
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 20 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, amd74xx_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "AMD_IDE",
|
||||
.id_table = amd74xx_pci_tbl,
|
||||
.probe = amd74xx_probe,
|
||||
};
|
||||
|
||||
static int __init amd74xx_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(amd74xx_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik");
|
||||
MODULE_DESCRIPTION("AMD PCI IDE driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
375
drivers/ide/pci/atiixp.c
Normal file
375
drivers/ide/pci/atiixp.c
Normal file
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/atiixp.c Version 0.01-bart2 Feb. 26, 2004
|
||||
*
|
||||
* Copyright (C) 2003 ATI Inc. <hyu@ati.com>
|
||||
* Copyright (C) 2004 Bartlomiej Zolnierkiewicz
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define ATIIXP_IDE_PIO_TIMING 0x40
|
||||
#define ATIIXP_IDE_MDMA_TIMING 0x44
|
||||
#define ATIIXP_IDE_PIO_CONTROL 0x48
|
||||
#define ATIIXP_IDE_PIO_MODE 0x4a
|
||||
#define ATIIXP_IDE_UDMA_CONTROL 0x54
|
||||
#define ATIIXP_IDE_UDMA_MODE 0x56
|
||||
|
||||
typedef struct {
|
||||
u8 command_width;
|
||||
u8 recover_width;
|
||||
} atiixp_ide_timing;
|
||||
|
||||
static atiixp_ide_timing pio_timing[] = {
|
||||
{ 0x05, 0x0d },
|
||||
{ 0x04, 0x07 },
|
||||
{ 0x03, 0x04 },
|
||||
{ 0x02, 0x02 },
|
||||
{ 0x02, 0x00 },
|
||||
};
|
||||
|
||||
static atiixp_ide_timing mdma_timing[] = {
|
||||
{ 0x07, 0x07 },
|
||||
{ 0x02, 0x01 },
|
||||
{ 0x02, 0x00 },
|
||||
};
|
||||
|
||||
static int save_mdma_mode[4];
|
||||
|
||||
static DEFINE_SPINLOCK(atiixp_lock);
|
||||
|
||||
/**
|
||||
* atiixp_ratemask - compute rate mask for ATIIXP IDE
|
||||
* @drive: IDE drive to compute for
|
||||
*
|
||||
* Returns the available modes for the ATIIXP IDE controller.
|
||||
*/
|
||||
|
||||
static u8 atiixp_ratemask(ide_drive_t *drive)
|
||||
{
|
||||
u8 mode = 3;
|
||||
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min(mode, (u8)1);
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* atiixp_dma_2_pio - return the PIO mode matching DMA
|
||||
* @xfer_rate: transfer speed
|
||||
*
|
||||
* Returns the nearest equivalent PIO timing for the PIO or DMA
|
||||
* mode requested by the controller.
|
||||
*/
|
||||
|
||||
static u8 atiixp_dma_2_pio(u8 xfer_rate) {
|
||||
switch(xfer_rate) {
|
||||
case XFER_UDMA_6:
|
||||
case XFER_UDMA_5:
|
||||
case XFER_UDMA_4:
|
||||
case XFER_UDMA_3:
|
||||
case XFER_UDMA_2:
|
||||
case XFER_UDMA_1:
|
||||
case XFER_UDMA_0:
|
||||
case XFER_MW_DMA_2:
|
||||
case XFER_PIO_4:
|
||||
return 4;
|
||||
case XFER_MW_DMA_1:
|
||||
case XFER_PIO_3:
|
||||
return 3;
|
||||
case XFER_SW_DMA_2:
|
||||
case XFER_PIO_2:
|
||||
return 2;
|
||||
case XFER_MW_DMA_0:
|
||||
case XFER_SW_DMA_1:
|
||||
case XFER_SW_DMA_0:
|
||||
case XFER_PIO_1:
|
||||
case XFER_PIO_0:
|
||||
case XFER_PIO_SLOW:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void atiixp_dma_host_on(ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = drive->hwif->pci_dev;
|
||||
unsigned long flags;
|
||||
u16 tmp16;
|
||||
|
||||
spin_lock_irqsave(&atiixp_lock, flags);
|
||||
|
||||
pci_read_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, &tmp16);
|
||||
if (save_mdma_mode[drive->dn])
|
||||
tmp16 &= ~(1 << drive->dn);
|
||||
else
|
||||
tmp16 |= (1 << drive->dn);
|
||||
pci_write_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, tmp16);
|
||||
|
||||
spin_unlock_irqrestore(&atiixp_lock, flags);
|
||||
|
||||
ide_dma_host_on(drive);
|
||||
}
|
||||
|
||||
static void atiixp_dma_host_off(ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = drive->hwif->pci_dev;
|
||||
unsigned long flags;
|
||||
u16 tmp16;
|
||||
|
||||
spin_lock_irqsave(&atiixp_lock, flags);
|
||||
|
||||
pci_read_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, &tmp16);
|
||||
tmp16 &= ~(1 << drive->dn);
|
||||
pci_write_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, tmp16);
|
||||
|
||||
spin_unlock_irqrestore(&atiixp_lock, flags);
|
||||
|
||||
ide_dma_host_off(drive);
|
||||
}
|
||||
|
||||
/**
|
||||
* atiixp_tune_drive - tune a drive attached to a ATIIXP
|
||||
* @drive: drive to tune
|
||||
* @pio: desired PIO mode
|
||||
*
|
||||
* Set the interface PIO mode.
|
||||
*/
|
||||
|
||||
static void atiixp_tuneproc(ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
struct pci_dev *dev = drive->hwif->pci_dev;
|
||||
unsigned long flags;
|
||||
int timing_shift = (drive->dn & 2) ? 16 : 0 + (drive->dn & 1) ? 0 : 8;
|
||||
u32 pio_timing_data;
|
||||
u16 pio_mode_data;
|
||||
|
||||
spin_lock_irqsave(&atiixp_lock, flags);
|
||||
|
||||
pci_read_config_word(dev, ATIIXP_IDE_PIO_MODE, &pio_mode_data);
|
||||
pio_mode_data &= ~(0x07 << (drive->dn * 4));
|
||||
pio_mode_data |= (pio << (drive->dn * 4));
|
||||
pci_write_config_word(dev, ATIIXP_IDE_PIO_MODE, pio_mode_data);
|
||||
|
||||
pci_read_config_dword(dev, ATIIXP_IDE_PIO_TIMING, &pio_timing_data);
|
||||
pio_timing_data &= ~(0xff << timing_shift);
|
||||
pio_timing_data |= (pio_timing[pio].recover_width << timing_shift) |
|
||||
(pio_timing[pio].command_width << (timing_shift + 4));
|
||||
pci_write_config_dword(dev, ATIIXP_IDE_PIO_TIMING, pio_timing_data);
|
||||
|
||||
spin_unlock_irqrestore(&atiixp_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* atiixp_tune_chipset - tune a ATIIXP interface
|
||||
* @drive: IDE drive to tune
|
||||
* @xferspeed: speed to configure
|
||||
*
|
||||
* Set a ATIIXP interface channel to the desired speeds. This involves
|
||||
* requires the right timing data into the ATIIXP configuration space
|
||||
* then setting the drive parameters appropriately
|
||||
*/
|
||||
|
||||
static int atiixp_speedproc(ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
struct pci_dev *dev = drive->hwif->pci_dev;
|
||||
unsigned long flags;
|
||||
int timing_shift = (drive->dn & 2) ? 16 : 0 + (drive->dn & 1) ? 0 : 8;
|
||||
u32 tmp32;
|
||||
u16 tmp16;
|
||||
u8 speed, pio;
|
||||
|
||||
speed = ide_rate_filter(atiixp_ratemask(drive), xferspeed);
|
||||
|
||||
spin_lock_irqsave(&atiixp_lock, flags);
|
||||
|
||||
save_mdma_mode[drive->dn] = 0;
|
||||
if (speed >= XFER_UDMA_0) {
|
||||
pci_read_config_word(dev, ATIIXP_IDE_UDMA_MODE, &tmp16);
|
||||
tmp16 &= ~(0x07 << (drive->dn * 4));
|
||||
tmp16 |= ((speed & 0x07) << (drive->dn * 4));
|
||||
pci_write_config_word(dev, ATIIXP_IDE_UDMA_MODE, tmp16);
|
||||
} else {
|
||||
if ((speed >= XFER_MW_DMA_0) && (speed <= XFER_MW_DMA_2)) {
|
||||
save_mdma_mode[drive->dn] = speed;
|
||||
pci_read_config_dword(dev, ATIIXP_IDE_MDMA_TIMING, &tmp32);
|
||||
tmp32 &= ~(0xff << timing_shift);
|
||||
tmp32 |= (mdma_timing[speed & 0x03].recover_width << timing_shift) |
|
||||
(mdma_timing[speed & 0x03].command_width << (timing_shift + 4));
|
||||
pci_write_config_dword(dev, ATIIXP_IDE_MDMA_TIMING, tmp32);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&atiixp_lock, flags);
|
||||
|
||||
if (speed >= XFER_SW_DMA_0)
|
||||
pio = atiixp_dma_2_pio(speed);
|
||||
else
|
||||
pio = speed - XFER_PIO_0;
|
||||
|
||||
atiixp_tuneproc(drive, pio);
|
||||
|
||||
return ide_config_drive_speed(drive, speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* atiixp_config_drive_for_dma - configure drive for DMA
|
||||
* @drive: IDE drive to configure
|
||||
*
|
||||
* Set up a ATIIXP interface channel for the best available speed.
|
||||
* We prefer UDMA if it is available and then MWDMA. If DMA is
|
||||
* not available we switch to PIO and return 0.
|
||||
*/
|
||||
|
||||
static int atiixp_config_drive_for_dma(ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, atiixp_ratemask(drive));
|
||||
|
||||
if (!speed)
|
||||
return 0;
|
||||
|
||||
(void) atiixp_speedproc(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
/**
|
||||
* atiixp_dma_check - set up an IDE device
|
||||
* @drive: IDE drive to configure
|
||||
*
|
||||
* Set up the ATIIXP interface for the best available speed on this
|
||||
* interface, preferring DMA to PIO.
|
||||
*/
|
||||
|
||||
static int atiixp_dma_check(ide_drive_t *drive)
|
||||
{
|
||||
u8 tspeed, speed;
|
||||
|
||||
drive->init_speed = 0;
|
||||
|
||||
if (ide_use_dma(drive) && atiixp_config_drive_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
if (ide_use_fast_pio(drive)) {
|
||||
tspeed = ide_get_best_pio_mode(drive, 255, 5, NULL);
|
||||
speed = atiixp_dma_2_pio(XFER_PIO_0 + tspeed) + XFER_PIO_0;
|
||||
atiixp_speedproc(drive, speed);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_hwif_atiixp - fill in the hwif for the ATIIXP
|
||||
* @hwif: IDE interface
|
||||
*
|
||||
* Set up the ide_hwif_t for the ATIIXP interface according to the
|
||||
* capabilities of the hardware.
|
||||
*/
|
||||
|
||||
static void __devinit init_hwif_atiixp(ide_hwif_t *hwif)
|
||||
{
|
||||
u8 udma_mode = 0;
|
||||
u8 ch = hwif->channel;
|
||||
struct pci_dev *pdev = hwif->pci_dev;
|
||||
|
||||
if (!hwif->irq)
|
||||
hwif->irq = ch ? 15 : 14;
|
||||
|
||||
hwif->autodma = 0;
|
||||
hwif->tuneproc = &atiixp_tuneproc;
|
||||
hwif->speedproc = &atiixp_speedproc;
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].autotune = 1;
|
||||
|
||||
if (!hwif->dma_base)
|
||||
return;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x3f;
|
||||
hwif->mwdma_mask = 0x06;
|
||||
hwif->swdma_mask = 0x04;
|
||||
|
||||
pci_read_config_byte(pdev, ATIIXP_IDE_UDMA_MODE + ch, &udma_mode);
|
||||
if ((udma_mode & 0x07) >= 0x04 || (udma_mode & 0x70) >= 0x40)
|
||||
hwif->udma_four = 1;
|
||||
else
|
||||
hwif->udma_four = 0;
|
||||
|
||||
hwif->dma_host_on = &atiixp_dma_host_on;
|
||||
hwif->dma_host_off = &atiixp_dma_host_off;
|
||||
hwif->ide_dma_check = &atiixp_dma_check;
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
|
||||
static ide_pci_device_t atiixp_pci_info[] __devinitdata = {
|
||||
{ /* 0 */
|
||||
.name = "ATIIXP",
|
||||
.init_hwif = init_hwif_atiixp,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.enablebits = {{0x48,0x01,0x00}, {0x48,0x08,0x00}},
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 1 */
|
||||
.name = "SB600_PATA",
|
||||
.init_hwif = init_hwif_atiixp,
|
||||
.channels = 1,
|
||||
.autodma = AUTODMA,
|
||||
.enablebits = {{0x48,0x01,0x00}, {0x00,0x00,0x00}},
|
||||
.bootable = ON_BOARD,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* atiixp_init_one - called when a ATIIXP is found
|
||||
* @dev: the atiixp device
|
||||
* @id: the matching pci id
|
||||
*
|
||||
* Called when the PCI registration layer (or the IDE initialization)
|
||||
* finds a device matching our IDE device tables.
|
||||
*/
|
||||
|
||||
static int __devinit atiixp_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &atiixp_pci_info[id->driver_data]);
|
||||
}
|
||||
|
||||
static struct pci_device_id atiixp_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, atiixp_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "ATIIXP_IDE",
|
||||
.id_table = atiixp_pci_tbl,
|
||||
.probe = atiixp_init_one,
|
||||
};
|
||||
|
||||
static int __init atiixp_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(atiixp_ide_init);
|
||||
|
||||
MODULE_AUTHOR("HUI YU");
|
||||
MODULE_DESCRIPTION("PCI driver module for ATI IXP IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
877
drivers/ide/pci/cmd640.c
Normal file
877
drivers/ide/pci/cmd640.c
Normal file
@@ -0,0 +1,877 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/cmd640.c Version 1.02 Sep 01, 1996
|
||||
*
|
||||
* Copyright (C) 1995-1996 Linus Torvalds & authors (see below)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Original authors: abramov@cecmow.enet.dec.com (Igor Abramov)
|
||||
* mlord@pobox.com (Mark Lord)
|
||||
*
|
||||
* See linux/MAINTAINERS for address of current maintainer.
|
||||
*
|
||||
* This file provides support for the advanced features and bugs
|
||||
* of IDE interfaces using the CMD Technologies 0640 IDE interface chip.
|
||||
*
|
||||
* These chips are basically fucked by design, and getting this driver
|
||||
* to work on every motherboard design that uses this screwed chip seems
|
||||
* bloody well impossible. However, we're still trying.
|
||||
*
|
||||
* Version 0.97 worked for everybody.
|
||||
*
|
||||
* User feedback is essential. Many thanks to the beta test team:
|
||||
*
|
||||
* A.Hartgers@stud.tue.nl, JZDQC@CUNYVM.CUNY.edu, abramov@cecmow.enet.dec.com,
|
||||
* bardj@utopia.ppp.sn.no, bart@gaga.tue.nl, bbol001@cs.auckland.ac.nz,
|
||||
* chrisc@dbass.demon.co.uk, dalecki@namu26.Num.Math.Uni-Goettingen.de,
|
||||
* derekn@vw.ece.cmu.edu, florian@btp2x3.phy.uni-bayreuth.de,
|
||||
* flynn@dei.unipd.it, gadio@netvision.net.il, godzilla@futuris.net,
|
||||
* j@pobox.com, jkemp1@mises.uni-paderborn.de, jtoppe@hiwaay.net,
|
||||
* kerouac@ssnet.com, meskes@informatik.rwth-aachen.de, hzoli@cs.elte.hu,
|
||||
* peter@udgaard.isgtec.com, phil@tazenda.demon.co.uk, roadcapw@cfw.com,
|
||||
* s0033las@sun10.vsz.bme.hu, schaffer@tam.cornell.edu, sjd@slip.net,
|
||||
* steve@ei.org, ulrpeg@bigcomm.gun.de, ism@tardis.ed.ac.uk, mack@cray.com
|
||||
* liug@mama.indstate.edu, and others.
|
||||
*
|
||||
* Version 0.01 Initial version, hacked out of ide.c,
|
||||
* and #include'd rather than compiled separately.
|
||||
* This will get cleaned up in a subsequent release.
|
||||
*
|
||||
* Version 0.02 Fixes for vlb initialization code, enable prefetch
|
||||
* for versions 'B' and 'C' of chip by default,
|
||||
* some code cleanup.
|
||||
*
|
||||
* Version 0.03 Added reset of secondary interface,
|
||||
* and black list for devices which are not compatible
|
||||
* with prefetch mode. Separate function for setting
|
||||
* prefetch is added, possibly it will be called some
|
||||
* day from ioctl processing code.
|
||||
*
|
||||
* Version 0.04 Now configs/compiles separate from ide.c
|
||||
*
|
||||
* Version 0.05 Major rewrite of interface timing code.
|
||||
* Added new function cmd640_set_mode to set PIO mode
|
||||
* from ioctl call. New drives added to black list.
|
||||
*
|
||||
* Version 0.06 More code cleanup. Prefetch is enabled only for
|
||||
* detected hard drives, not included in prefetch
|
||||
* black list.
|
||||
*
|
||||
* Version 0.07 Changed to more conservative drive tuning policy.
|
||||
* Unknown drives, which report PIO < 4 are set to
|
||||
* (reported_PIO - 1) if it is supported, or to PIO0.
|
||||
* List of known drives extended by info provided by
|
||||
* CMD at their ftp site.
|
||||
*
|
||||
* Version 0.08 Added autotune/noautotune support.
|
||||
*
|
||||
* Version 0.09 Try to be smarter about 2nd port enabling.
|
||||
* Version 0.10 Be nice and don't reset 2nd port.
|
||||
* Version 0.11 Try to handle more weird situations.
|
||||
*
|
||||
* Version 0.12 Lots of bug fixes from Laszlo Peter
|
||||
* irq unmasking disabled for reliability.
|
||||
* try to be even smarter about the second port.
|
||||
* tidy up source code formatting.
|
||||
* Version 0.13 permit irq unmasking again.
|
||||
* Version 0.90 massive code cleanup, some bugs fixed.
|
||||
* defaults all drives to PIO mode0, prefetch off.
|
||||
* autotune is OFF by default, with compile time flag.
|
||||
* prefetch can be turned OFF/ON using "hdparm -p8/-p9"
|
||||
* (requires hdparm-3.1 or newer)
|
||||
* Version 0.91 first release to linux-kernel list.
|
||||
* Version 0.92 move initial reg dump to separate callable function
|
||||
* change "readahead" to "prefetch" to avoid confusion
|
||||
* Version 0.95 respect original BIOS timings unless autotuning.
|
||||
* tons of code cleanup and rearrangement.
|
||||
* added CONFIG_BLK_DEV_CMD640_ENHANCED option
|
||||
* prevent use of unmask when prefetch is on
|
||||
* Version 0.96 prevent use of io_32bit when prefetch is off
|
||||
* Version 0.97 fix VLB secondary interface for sjd@slip.net
|
||||
* other minor tune-ups: 0.96 was very good.
|
||||
* Version 0.98 ignore PCI version when disabled by BIOS
|
||||
* Version 0.99 display setup/active/recovery clocks with PIO mode
|
||||
* Version 1.00 Mmm.. cannot depend on PCMD_ENA in all systems
|
||||
* Version 1.01 slow/fast devsel can be selected with "hdparm -p6/-p7"
|
||||
* ("fast" is necessary for 32bit I/O in some systems)
|
||||
* Version 1.02 fix bug that resulted in slow "setup times"
|
||||
* (patch courtesy of Zoltan Hidvegi)
|
||||
*/
|
||||
|
||||
#define CMD640_PREFETCH_MASKS 1
|
||||
|
||||
//#define CMD640_DUMP_REGS
|
||||
|
||||
#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>
|
||||
|
||||
/*
|
||||
* This flag is set in ide.c by the parameter: ide0=cmd640_vlb
|
||||
*/
|
||||
int cmd640_vlb = 0;
|
||||
|
||||
/*
|
||||
* CMD640 specific registers definition.
|
||||
*/
|
||||
|
||||
#define VID 0x00
|
||||
#define DID 0x02
|
||||
#define PCMD 0x04
|
||||
#define PCMD_ENA 0x01
|
||||
#define PSTTS 0x06
|
||||
#define REVID 0x08
|
||||
#define PROGIF 0x09
|
||||
#define SUBCL 0x0a
|
||||
#define BASCL 0x0b
|
||||
#define BaseA0 0x10
|
||||
#define BaseA1 0x14
|
||||
#define BaseA2 0x18
|
||||
#define BaseA3 0x1c
|
||||
#define INTLINE 0x3c
|
||||
#define INPINE 0x3d
|
||||
|
||||
#define CFR 0x50
|
||||
#define CFR_DEVREV 0x03
|
||||
#define CFR_IDE01INTR 0x04
|
||||
#define CFR_DEVID 0x18
|
||||
#define CFR_AT_VESA_078h 0x20
|
||||
#define CFR_DSA1 0x40
|
||||
#define CFR_DSA0 0x80
|
||||
|
||||
#define CNTRL 0x51
|
||||
#define CNTRL_DIS_RA0 0x40
|
||||
#define CNTRL_DIS_RA1 0x80
|
||||
#define CNTRL_ENA_2ND 0x08
|
||||
|
||||
#define CMDTIM 0x52
|
||||
#define ARTTIM0 0x53
|
||||
#define DRWTIM0 0x54
|
||||
#define ARTTIM1 0x55
|
||||
#define DRWTIM1 0x56
|
||||
#define ARTTIM23 0x57
|
||||
#define ARTTIM23_DIS_RA2 0x04
|
||||
#define ARTTIM23_DIS_RA3 0x08
|
||||
#define DRWTIM23 0x58
|
||||
#define BRST 0x59
|
||||
|
||||
/*
|
||||
* Registers and masks for easy access by drive index:
|
||||
*/
|
||||
static u8 prefetch_regs[4] = {CNTRL, CNTRL, ARTTIM23, ARTTIM23};
|
||||
static u8 prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3};
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
|
||||
|
||||
static u8 arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23};
|
||||
static u8 drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM23, DRWTIM23};
|
||||
|
||||
/*
|
||||
* Current cmd640 timing values for each drive.
|
||||
* The defaults for each are the slowest possible timings.
|
||||
*/
|
||||
static u8 setup_counts[4] = {4, 4, 4, 4}; /* Address setup count (in clocks) */
|
||||
static u8 active_counts[4] = {16, 16, 16, 16}; /* Active count (encoded) */
|
||||
static u8 recovery_counts[4] = {16, 16, 16, 16}; /* Recovery count (encoded) */
|
||||
|
||||
#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
|
||||
|
||||
/*
|
||||
* These are initialized to point at the devices we control
|
||||
*/
|
||||
static ide_hwif_t *cmd_hwif0, *cmd_hwif1;
|
||||
static ide_drive_t *cmd_drives[4];
|
||||
|
||||
/*
|
||||
* Interface to access cmd640x registers
|
||||
*/
|
||||
static unsigned int cmd640_key;
|
||||
static void (*__put_cmd640_reg)(u16 reg, u8 val);
|
||||
static u8 (*__get_cmd640_reg)(u16 reg);
|
||||
|
||||
/*
|
||||
* This is read from the CFR reg, and is used in several places.
|
||||
*/
|
||||
static unsigned int cmd640_chip_version;
|
||||
|
||||
/*
|
||||
* The CMD640x chip does not support DWORD config write cycles, but some
|
||||
* of the BIOSes use them to implement the config services.
|
||||
* Therefore, we must use direct IO instead.
|
||||
*/
|
||||
|
||||
/* PCI method 1 access */
|
||||
|
||||
static void put_cmd640_reg_pci1 (u16 reg, u8 val)
|
||||
{
|
||||
outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
|
||||
outb_p(val, (reg & 3) | 0xcfc);
|
||||
}
|
||||
|
||||
static u8 get_cmd640_reg_pci1 (u16 reg)
|
||||
{
|
||||
outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
|
||||
return inb_p((reg & 3) | 0xcfc);
|
||||
}
|
||||
|
||||
/* PCI method 2 access (from CMD datasheet) */
|
||||
|
||||
static void put_cmd640_reg_pci2 (u16 reg, u8 val)
|
||||
{
|
||||
outb_p(0x10, 0xcf8);
|
||||
outb_p(val, cmd640_key + reg);
|
||||
outb_p(0, 0xcf8);
|
||||
}
|
||||
|
||||
static u8 get_cmd640_reg_pci2 (u16 reg)
|
||||
{
|
||||
u8 b;
|
||||
|
||||
outb_p(0x10, 0xcf8);
|
||||
b = inb_p(cmd640_key + reg);
|
||||
outb_p(0, 0xcf8);
|
||||
return b;
|
||||
}
|
||||
|
||||
/* VLB access */
|
||||
|
||||
static void put_cmd640_reg_vlb (u16 reg, u8 val)
|
||||
{
|
||||
outb_p(reg, cmd640_key);
|
||||
outb_p(val, cmd640_key + 4);
|
||||
}
|
||||
|
||||
static u8 get_cmd640_reg_vlb (u16 reg)
|
||||
{
|
||||
outb_p(reg, cmd640_key);
|
||||
return inb_p(cmd640_key + 4);
|
||||
}
|
||||
|
||||
static u8 get_cmd640_reg(u16 reg)
|
||||
{
|
||||
u8 b;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ide_lock, flags);
|
||||
b = __get_cmd640_reg(reg);
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
return b;
|
||||
}
|
||||
|
||||
static void put_cmd640_reg(u16 reg, u8 val)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ide_lock, flags);
|
||||
__put_cmd640_reg(reg,val);
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
}
|
||||
|
||||
static int __init match_pci_cmd640_device (void)
|
||||
{
|
||||
const u8 ven_dev[4] = {0x95, 0x10, 0x40, 0x06};
|
||||
unsigned int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (get_cmd640_reg(i) != ven_dev[i])
|
||||
return 0;
|
||||
}
|
||||
#ifdef STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT
|
||||
if ((get_cmd640_reg(PCMD) & PCMD_ENA) == 0) {
|
||||
printk("ide: cmd640 on PCI disabled by BIOS\n");
|
||||
return 0;
|
||||
}
|
||||
#endif /* STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT */
|
||||
return 1; /* success */
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe for CMD640x -- pci method 1
|
||||
*/
|
||||
static int __init probe_for_cmd640_pci1 (void)
|
||||
{
|
||||
__get_cmd640_reg = get_cmd640_reg_pci1;
|
||||
__put_cmd640_reg = put_cmd640_reg_pci1;
|
||||
for (cmd640_key = 0x80000000;
|
||||
cmd640_key <= 0x8000f800;
|
||||
cmd640_key += 0x800) {
|
||||
if (match_pci_cmd640_device())
|
||||
return 1; /* success */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe for CMD640x -- pci method 2
|
||||
*/
|
||||
static int __init probe_for_cmd640_pci2 (void)
|
||||
{
|
||||
__get_cmd640_reg = get_cmd640_reg_pci2;
|
||||
__put_cmd640_reg = put_cmd640_reg_pci2;
|
||||
for (cmd640_key = 0xc000; cmd640_key <= 0xcf00; cmd640_key += 0x100) {
|
||||
if (match_pci_cmd640_device())
|
||||
return 1; /* success */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe for CMD640x -- vlb
|
||||
*/
|
||||
static int __init probe_for_cmd640_vlb (void)
|
||||
{
|
||||
u8 b;
|
||||
|
||||
__get_cmd640_reg = get_cmd640_reg_vlb;
|
||||
__put_cmd640_reg = put_cmd640_reg_vlb;
|
||||
cmd640_key = 0x178;
|
||||
b = get_cmd640_reg(CFR);
|
||||
if (b == 0xff || b == 0x00 || (b & CFR_AT_VESA_078h)) {
|
||||
cmd640_key = 0x78;
|
||||
b = get_cmd640_reg(CFR);
|
||||
if (b == 0xff || b == 0x00 || !(b & CFR_AT_VESA_078h))
|
||||
return 0;
|
||||
}
|
||||
return 1; /* success */
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 1 if an IDE interface/drive exists at 0x170,
|
||||
* Returns 0 otherwise.
|
||||
*/
|
||||
static int __init secondary_port_responding (void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ide_lock, flags);
|
||||
|
||||
outb_p(0x0a, 0x170 + IDE_SELECT_OFFSET); /* select drive0 */
|
||||
udelay(100);
|
||||
if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x0a) {
|
||||
outb_p(0x1a, 0x170 + IDE_SELECT_OFFSET); /* select drive1 */
|
||||
udelay(100);
|
||||
if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x1a) {
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
return 0; /* nothing responded */
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
return 1; /* success */
|
||||
}
|
||||
|
||||
#ifdef CMD640_DUMP_REGS
|
||||
/*
|
||||
* Dump out all cmd640 registers. May be called from ide.c
|
||||
*/
|
||||
static void cmd640_dump_regs (void)
|
||||
{
|
||||
unsigned int reg = cmd640_vlb ? 0x50 : 0x00;
|
||||
|
||||
/* Dump current state of chip registers */
|
||||
printk("ide: cmd640 internal register dump:");
|
||||
for (; reg <= 0x59; reg++) {
|
||||
if (!(reg & 0x0f))
|
||||
printk("\n%04x:", reg);
|
||||
printk(" %02x", get_cmd640_reg(reg));
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check whether prefetch is on for a drive,
|
||||
* and initialize the unmask flags for safe operation.
|
||||
*/
|
||||
static void __init check_prefetch (unsigned int index)
|
||||
{
|
||||
ide_drive_t *drive = cmd_drives[index];
|
||||
u8 b = get_cmd640_reg(prefetch_regs[index]);
|
||||
|
||||
if (b & prefetch_masks[index]) { /* is prefetch off? */
|
||||
drive->no_unmask = 0;
|
||||
drive->no_io_32bit = 1;
|
||||
drive->io_32bit = 0;
|
||||
} else {
|
||||
#if CMD640_PREFETCH_MASKS
|
||||
drive->no_unmask = 1;
|
||||
drive->unmask = 0;
|
||||
#endif
|
||||
drive->no_io_32bit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Figure out which devices we control
|
||||
*/
|
||||
static void __init setup_device_ptrs (void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
cmd_hwif0 = &ide_hwifs[0]; /* default, if not found below */
|
||||
cmd_hwif1 = &ide_hwifs[1]; /* default, if not found below */
|
||||
for (i = 0; i < MAX_HWIFS; i++) {
|
||||
ide_hwif_t *hwif = &ide_hwifs[i];
|
||||
if (hwif->chipset == ide_unknown || hwif->chipset == ide_forced) {
|
||||
if (hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0)
|
||||
cmd_hwif0 = hwif;
|
||||
else if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170)
|
||||
cmd_hwif1 = hwif;
|
||||
}
|
||||
}
|
||||
cmd_drives[0] = &cmd_hwif0->drives[0];
|
||||
cmd_drives[1] = &cmd_hwif0->drives[1];
|
||||
cmd_drives[2] = &cmd_hwif1->drives[0];
|
||||
cmd_drives[3] = &cmd_hwif1->drives[1];
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
|
||||
|
||||
/*
|
||||
* Sets prefetch mode for a drive.
|
||||
*/
|
||||
static void set_prefetch_mode (unsigned int index, int mode)
|
||||
{
|
||||
ide_drive_t *drive = cmd_drives[index];
|
||||
int reg = prefetch_regs[index];
|
||||
u8 b;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ide_lock, flags);
|
||||
b = __get_cmd640_reg(reg);
|
||||
if (mode) { /* want prefetch on? */
|
||||
#if CMD640_PREFETCH_MASKS
|
||||
drive->no_unmask = 1;
|
||||
drive->unmask = 0;
|
||||
#endif
|
||||
drive->no_io_32bit = 0;
|
||||
b &= ~prefetch_masks[index]; /* enable prefetch */
|
||||
} else {
|
||||
drive->no_unmask = 0;
|
||||
drive->no_io_32bit = 1;
|
||||
drive->io_32bit = 0;
|
||||
b |= prefetch_masks[index]; /* disable prefetch */
|
||||
}
|
||||
__put_cmd640_reg(reg, b);
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump out current drive clocks settings
|
||||
*/
|
||||
static void display_clocks (unsigned int index)
|
||||
{
|
||||
u8 active_count, recovery_count;
|
||||
|
||||
active_count = active_counts[index];
|
||||
if (active_count == 1)
|
||||
++active_count;
|
||||
recovery_count = recovery_counts[index];
|
||||
if (active_count > 3 && recovery_count == 1)
|
||||
++recovery_count;
|
||||
if (cmd640_chip_version > 1)
|
||||
recovery_count += 1; /* cmd640b uses (count + 1)*/
|
||||
printk(", clocks=%d/%d/%d\n", setup_counts[index], active_count, recovery_count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pack active and recovery counts into single byte representation
|
||||
* used by controller
|
||||
*/
|
||||
static inline u8 pack_nibbles (u8 upper, u8 lower)
|
||||
{
|
||||
return ((upper & 0x0f) << 4) | (lower & 0x0f);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine retrieves the initial drive timings from the chipset.
|
||||
*/
|
||||
static void __init retrieve_drive_counts (unsigned int index)
|
||||
{
|
||||
u8 b;
|
||||
|
||||
/*
|
||||
* Get the internal setup timing, and convert to clock count
|
||||
*/
|
||||
b = get_cmd640_reg(arttim_regs[index]) & ~0x3f;
|
||||
switch (b) {
|
||||
case 0x00: b = 4; break;
|
||||
case 0x80: b = 3; break;
|
||||
case 0x40: b = 2; break;
|
||||
default: b = 5; break;
|
||||
}
|
||||
setup_counts[index] = b;
|
||||
|
||||
/*
|
||||
* Get the active/recovery counts
|
||||
*/
|
||||
b = get_cmd640_reg(drwtim_regs[index]);
|
||||
active_counts[index] = (b >> 4) ? (b >> 4) : 0x10;
|
||||
recovery_counts[index] = (b & 0x0f) ? (b & 0x0f) : 0x10;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This routine writes the prepared setup/active/recovery counts
|
||||
* for a drive into the cmd640 chipset registers to active them.
|
||||
*/
|
||||
static void program_drive_counts (unsigned int index)
|
||||
{
|
||||
unsigned long flags;
|
||||
u8 setup_count = setup_counts[index];
|
||||
u8 active_count = active_counts[index];
|
||||
u8 recovery_count = recovery_counts[index];
|
||||
|
||||
/*
|
||||
* Set up address setup count and drive read/write timing registers.
|
||||
* Primary interface has individual count/timing registers for
|
||||
* each drive. Secondary interface has one common set of registers,
|
||||
* so we merge the timings, using the slowest value for each timing.
|
||||
*/
|
||||
if (index > 1) {
|
||||
unsigned int mate;
|
||||
if (cmd_drives[mate = index ^ 1]->present) {
|
||||
if (setup_count < setup_counts[mate])
|
||||
setup_count = setup_counts[mate];
|
||||
if (active_count < active_counts[mate])
|
||||
active_count = active_counts[mate];
|
||||
if (recovery_count < recovery_counts[mate])
|
||||
recovery_count = recovery_counts[mate];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert setup_count to internal chipset representation
|
||||
*/
|
||||
switch (setup_count) {
|
||||
case 4: setup_count = 0x00; break;
|
||||
case 3: setup_count = 0x80; break;
|
||||
case 1:
|
||||
case 2: setup_count = 0x40; break;
|
||||
default: setup_count = 0xc0; /* case 5 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that everything is ready, program the new timings
|
||||
*/
|
||||
spin_lock_irqsave(&ide_lock, flags);
|
||||
/*
|
||||
* Program the address_setup clocks into ARTTIM reg,
|
||||
* and then the active/recovery counts into the DRWTIM reg
|
||||
* (this converts counts of 16 into counts of zero -- okay).
|
||||
*/
|
||||
setup_count |= __get_cmd640_reg(arttim_regs[index]) & 0x3f;
|
||||
__put_cmd640_reg(arttim_regs[index], setup_count);
|
||||
__put_cmd640_reg(drwtim_regs[index], pack_nibbles(active_count, recovery_count));
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a specific pio_mode for a drive
|
||||
*/
|
||||
static void cmd640_set_mode (unsigned int index, u8 pio_mode, unsigned int cycle_time)
|
||||
{
|
||||
int setup_time, active_time, recovery_time, clock_time;
|
||||
u8 setup_count, active_count, recovery_count, recovery_count2, cycle_count;
|
||||
int bus_speed = system_bus_clock();
|
||||
|
||||
if (pio_mode > 5)
|
||||
pio_mode = 5;
|
||||
setup_time = ide_pio_timings[pio_mode].setup_time;
|
||||
active_time = ide_pio_timings[pio_mode].active_time;
|
||||
recovery_time = cycle_time - (setup_time + active_time);
|
||||
clock_time = 1000 / bus_speed;
|
||||
cycle_count = (cycle_time + clock_time - 1) / clock_time;
|
||||
|
||||
setup_count = (setup_time + clock_time - 1) / clock_time;
|
||||
|
||||
active_count = (active_time + clock_time - 1) / clock_time;
|
||||
if (active_count < 2)
|
||||
active_count = 2; /* minimum allowed by cmd640 */
|
||||
|
||||
recovery_count = (recovery_time + clock_time - 1) / clock_time;
|
||||
recovery_count2 = cycle_count - (setup_count + active_count);
|
||||
if (recovery_count2 > recovery_count)
|
||||
recovery_count = recovery_count2;
|
||||
if (recovery_count < 2)
|
||||
recovery_count = 2; /* minimum allowed by cmd640 */
|
||||
if (recovery_count > 17) {
|
||||
active_count += recovery_count - 17;
|
||||
recovery_count = 17;
|
||||
}
|
||||
if (active_count > 16)
|
||||
active_count = 16; /* maximum allowed by cmd640 */
|
||||
if (cmd640_chip_version > 1)
|
||||
recovery_count -= 1; /* cmd640b uses (count + 1)*/
|
||||
if (recovery_count > 16)
|
||||
recovery_count = 16; /* maximum allowed by cmd640 */
|
||||
|
||||
setup_counts[index] = setup_count;
|
||||
active_counts[index] = active_count;
|
||||
recovery_counts[index] = recovery_count;
|
||||
|
||||
/*
|
||||
* In a perfect world, we might set the drive pio mode here
|
||||
* (using WIN_SETFEATURE) before continuing.
|
||||
*
|
||||
* But we do not, because:
|
||||
* 1) this is the wrong place to do it (proper is do_special() in ide.c)
|
||||
* 2) in practice this is rarely, if ever, necessary
|
||||
*/
|
||||
program_drive_counts (index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Drive PIO mode selection:
|
||||
*/
|
||||
static void cmd640_tune_drive (ide_drive_t *drive, u8 mode_wanted)
|
||||
{
|
||||
u8 b;
|
||||
ide_pio_data_t d;
|
||||
unsigned int index = 0;
|
||||
|
||||
while (drive != cmd_drives[index]) {
|
||||
if (++index > 3) {
|
||||
printk("%s: bad news in cmd640_tune_drive\n", drive->name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch (mode_wanted) {
|
||||
case 6: /* set fast-devsel off */
|
||||
case 7: /* set fast-devsel on */
|
||||
mode_wanted &= 1;
|
||||
b = get_cmd640_reg(CNTRL) & ~0x27;
|
||||
if (mode_wanted)
|
||||
b |= 0x27;
|
||||
put_cmd640_reg(CNTRL, b);
|
||||
printk("%s: %sabled cmd640 fast host timing (devsel)\n", drive->name, mode_wanted ? "en" : "dis");
|
||||
return;
|
||||
|
||||
case 8: /* set prefetch off */
|
||||
case 9: /* set prefetch on */
|
||||
mode_wanted &= 1;
|
||||
set_prefetch_mode(index, mode_wanted);
|
||||
printk("%s: %sabled cmd640 prefetch\n", drive->name, mode_wanted ? "en" : "dis");
|
||||
return;
|
||||
}
|
||||
|
||||
(void) ide_get_best_pio_mode (drive, mode_wanted, 5, &d);
|
||||
cmd640_set_mode (index, d.pio_mode, d.cycle_time);
|
||||
|
||||
printk ("%s: selected cmd640 PIO mode%d (%dns)%s",
|
||||
drive->name,
|
||||
d.pio_mode,
|
||||
d.cycle_time,
|
||||
d.overridden ? " (overriding vendor mode)" : "");
|
||||
display_clocks(index);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
|
||||
|
||||
static int pci_conf1(void)
|
||||
{
|
||||
u32 tmp;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ide_lock, flags);
|
||||
outb(0x01, 0xCFB);
|
||||
tmp = inl(0xCF8);
|
||||
outl(0x80000000, 0xCF8);
|
||||
if (inl(0xCF8) == 0x80000000) {
|
||||
outl(tmp, 0xCF8);
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
return 1;
|
||||
}
|
||||
outl(tmp, 0xCF8);
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_conf2(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ide_lock, flags);
|
||||
outb(0x00, 0xCFB);
|
||||
outb(0x00, 0xCF8);
|
||||
outb(0x00, 0xCFA);
|
||||
if (inb(0xCF8) == 0x00 && inb(0xCF8) == 0x00) {
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
return 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe for a cmd640 chipset, and initialize it if found. Called from ide.c
|
||||
*/
|
||||
int __init ide_probe_for_cmd640x (void)
|
||||
{
|
||||
#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
|
||||
int second_port_toggled = 0;
|
||||
#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
|
||||
int second_port_cmd640 = 0;
|
||||
const char *bus_type, *port2;
|
||||
unsigned int index;
|
||||
u8 b, cfr;
|
||||
|
||||
if (cmd640_vlb && probe_for_cmd640_vlb()) {
|
||||
bus_type = "VLB";
|
||||
} else {
|
||||
cmd640_vlb = 0;
|
||||
/* Find out what kind of PCI probing is supported otherwise
|
||||
Justin Gibbs will sulk.. */
|
||||
if (pci_conf1() && probe_for_cmd640_pci1())
|
||||
bus_type = "PCI (type1)";
|
||||
else if (pci_conf2() && probe_for_cmd640_pci2())
|
||||
bus_type = "PCI (type2)";
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Undocumented magic (there is no 0x5b reg in specs)
|
||||
*/
|
||||
put_cmd640_reg(0x5b, 0xbd);
|
||||
if (get_cmd640_reg(0x5b) != 0xbd) {
|
||||
printk(KERN_ERR "ide: cmd640 init failed: wrong value in reg 0x5b\n");
|
||||
return 0;
|
||||
}
|
||||
put_cmd640_reg(0x5b, 0);
|
||||
|
||||
#ifdef CMD640_DUMP_REGS
|
||||
cmd640_dump_regs();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Documented magic begins here
|
||||
*/
|
||||
cfr = get_cmd640_reg(CFR);
|
||||
cmd640_chip_version = cfr & CFR_DEVREV;
|
||||
if (cmd640_chip_version == 0) {
|
||||
printk ("ide: bad cmd640 revision: %d\n", cmd640_chip_version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize data for primary port
|
||||
*/
|
||||
setup_device_ptrs ();
|
||||
printk("%s: buggy cmd640%c interface on %s, config=0x%02x\n",
|
||||
cmd_hwif0->name, 'a' + cmd640_chip_version - 1, bus_type, cfr);
|
||||
cmd_hwif0->chipset = ide_cmd640;
|
||||
#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
|
||||
cmd_hwif0->tuneproc = &cmd640_tune_drive;
|
||||
#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
|
||||
|
||||
/*
|
||||
* Ensure compatibility by always using the slowest timings
|
||||
* for access to the drive's command register block,
|
||||
* and reset the prefetch burstsize to default (512 bytes).
|
||||
*
|
||||
* Maybe we need a way to NOT do these on *some* systems?
|
||||
*/
|
||||
put_cmd640_reg(CMDTIM, 0);
|
||||
put_cmd640_reg(BRST, 0x40);
|
||||
|
||||
/*
|
||||
* Try to enable the secondary interface, if not already enabled
|
||||
*/
|
||||
if (cmd_hwif1->noprobe) {
|
||||
port2 = "not probed";
|
||||
} else {
|
||||
b = get_cmd640_reg(CNTRL);
|
||||
if (secondary_port_responding()) {
|
||||
if ((b & CNTRL_ENA_2ND)) {
|
||||
second_port_cmd640 = 1;
|
||||
port2 = "okay";
|
||||
} else if (cmd640_vlb) {
|
||||
second_port_cmd640 = 1;
|
||||
port2 = "alive";
|
||||
} else
|
||||
port2 = "not cmd640";
|
||||
} else {
|
||||
put_cmd640_reg(CNTRL, b ^ CNTRL_ENA_2ND); /* toggle the bit */
|
||||
if (secondary_port_responding()) {
|
||||
second_port_cmd640 = 1;
|
||||
#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
|
||||
second_port_toggled = 1;
|
||||
#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
|
||||
port2 = "enabled";
|
||||
} else {
|
||||
put_cmd640_reg(CNTRL, b); /* restore original setting */
|
||||
port2 = "not responding";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize data for secondary cmd640 port, if enabled
|
||||
*/
|
||||
if (second_port_cmd640) {
|
||||
cmd_hwif0->serialized = 1;
|
||||
cmd_hwif1->serialized = 1;
|
||||
cmd_hwif1->chipset = ide_cmd640;
|
||||
cmd_hwif0->mate = cmd_hwif1;
|
||||
cmd_hwif1->mate = cmd_hwif0;
|
||||
cmd_hwif1->channel = 1;
|
||||
#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
|
||||
cmd_hwif1->tuneproc = &cmd640_tune_drive;
|
||||
#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
|
||||
}
|
||||
printk(KERN_INFO "%s: %sserialized, secondary interface %s\n", cmd_hwif1->name,
|
||||
cmd_hwif0->serialized ? "" : "not ", port2);
|
||||
|
||||
/*
|
||||
* Establish initial timings/prefetch for all drives.
|
||||
* Do not unnecessarily disturb any prior BIOS setup of these.
|
||||
*/
|
||||
for (index = 0; index < (2 + (second_port_cmd640 << 1)); index++) {
|
||||
ide_drive_t *drive = cmd_drives[index];
|
||||
#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
|
||||
if (drive->autotune || ((index > 1) && second_port_toggled)) {
|
||||
/*
|
||||
* Reset timing to the slowest speed and turn off prefetch.
|
||||
* This way, the drive identify code has a better chance.
|
||||
*/
|
||||
setup_counts [index] = 4; /* max possible */
|
||||
active_counts [index] = 16; /* max possible */
|
||||
recovery_counts [index] = 16; /* max possible */
|
||||
program_drive_counts (index);
|
||||
set_prefetch_mode (index, 0);
|
||||
printk("cmd640: drive%d timings/prefetch cleared\n", index);
|
||||
} else {
|
||||
/*
|
||||
* Record timings/prefetch without changing them.
|
||||
* This preserves any prior BIOS setup.
|
||||
*/
|
||||
retrieve_drive_counts (index);
|
||||
check_prefetch (index);
|
||||
printk("cmd640: drive%d timings/prefetch(%s) preserved",
|
||||
index, drive->no_io_32bit ? "off" : "on");
|
||||
display_clocks(index);
|
||||
}
|
||||
#else
|
||||
/*
|
||||
* Set the drive unmask flags to match the prefetch setting
|
||||
*/
|
||||
check_prefetch (index);
|
||||
printk("cmd640: drive%d timings/prefetch(%s) preserved\n",
|
||||
index, drive->no_io_32bit ? "off" : "on");
|
||||
#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
|
||||
}
|
||||
|
||||
#ifdef CMD640_DUMP_REGS
|
||||
cmd640_dump_regs();
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
763
drivers/ide/pci/cmd64x.c
Normal file
763
drivers/ide/pci/cmd64x.c
Normal file
@@ -0,0 +1,763 @@
|
||||
/* $Id: cmd64x.c,v 1.21 2000/01/30 23:23:16
|
||||
*
|
||||
* linux/drivers/ide/pci/cmd64x.c Version 1.42 Feb 8, 2007
|
||||
*
|
||||
* cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines.
|
||||
* Note, this driver is not used at all on other systems because
|
||||
* there the "BIOS" has done all of the following already.
|
||||
* Due to massive hardware bugs, UltraDMA is only supported
|
||||
* on the 646U2 and not on the 646U.
|
||||
*
|
||||
* Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
|
||||
* Copyright (C) 1998 David S. Miller (davem@redhat.com)
|
||||
*
|
||||
* Copyright (C) 1999-2002 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2007 MontaVista Software, Inc. <source@mvista.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DISPLAY_CMD64X_TIMINGS
|
||||
|
||||
#define CMD_DEBUG 0
|
||||
|
||||
#if CMD_DEBUG
|
||||
#define cmdprintk(x...) printk(x)
|
||||
#else
|
||||
#define cmdprintk(x...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* CMD64x specific registers definition.
|
||||
*/
|
||||
#define CFR 0x50
|
||||
#define CFR_INTR_CH0 0x02
|
||||
#define CNTRL 0x51
|
||||
#define CNTRL_DIS_RA0 0x40
|
||||
#define CNTRL_DIS_RA1 0x80
|
||||
#define CNTRL_ENA_2ND 0x08
|
||||
|
||||
#define CMDTIM 0x52
|
||||
#define ARTTIM0 0x53
|
||||
#define DRWTIM0 0x54
|
||||
#define ARTTIM1 0x55
|
||||
#define DRWTIM1 0x56
|
||||
#define ARTTIM23 0x57
|
||||
#define ARTTIM23_DIS_RA2 0x04
|
||||
#define ARTTIM23_DIS_RA3 0x08
|
||||
#define ARTTIM23_INTR_CH1 0x10
|
||||
#define ARTTIM2 0x57
|
||||
#define ARTTIM3 0x57
|
||||
#define DRWTIM23 0x58
|
||||
#define DRWTIM2 0x58
|
||||
#define BRST 0x59
|
||||
#define DRWTIM3 0x5b
|
||||
|
||||
#define BMIDECR0 0x70
|
||||
#define MRDMODE 0x71
|
||||
#define MRDMODE_INTR_CH0 0x04
|
||||
#define MRDMODE_INTR_CH1 0x08
|
||||
#define MRDMODE_BLK_CH0 0x10
|
||||
#define MRDMODE_BLK_CH1 0x20
|
||||
#define BMIDESR0 0x72
|
||||
#define UDIDETCR0 0x73
|
||||
#define DTPR0 0x74
|
||||
#define BMIDECR1 0x78
|
||||
#define BMIDECSR 0x79
|
||||
#define BMIDESR1 0x7A
|
||||
#define UDIDETCR1 0x7B
|
||||
#define DTPR1 0x7C
|
||||
|
||||
#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
|
||||
#include <linux/stat.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
static u8 cmd64x_proc = 0;
|
||||
|
||||
#define CMD_MAX_DEVS 5
|
||||
|
||||
static struct pci_dev *cmd_devs[CMD_MAX_DEVS];
|
||||
static int n_cmd_devs;
|
||||
|
||||
static char * print_cmd64x_get_info (char *buf, struct pci_dev *dev, int index)
|
||||
{
|
||||
char *p = buf;
|
||||
|
||||
u8 reg53 = 0, reg54 = 0, reg55 = 0, reg56 = 0; /* primary */
|
||||
u8 reg57 = 0, reg58 = 0, reg5b; /* secondary */
|
||||
u8 reg72 = 0, reg73 = 0; /* primary */
|
||||
u8 reg7a = 0, reg7b = 0; /* secondary */
|
||||
u8 reg50 = 0, reg71 = 0; /* extra */
|
||||
|
||||
p += sprintf(p, "\nController: %d\n", index);
|
||||
p += sprintf(p, "CMD%x Chipset.\n", dev->device);
|
||||
(void) pci_read_config_byte(dev, CFR, ®50);
|
||||
(void) pci_read_config_byte(dev, ARTTIM0, ®53);
|
||||
(void) pci_read_config_byte(dev, DRWTIM0, ®54);
|
||||
(void) pci_read_config_byte(dev, ARTTIM1, ®55);
|
||||
(void) pci_read_config_byte(dev, DRWTIM1, ®56);
|
||||
(void) pci_read_config_byte(dev, ARTTIM2, ®57);
|
||||
(void) pci_read_config_byte(dev, DRWTIM2, ®58);
|
||||
(void) pci_read_config_byte(dev, DRWTIM3, ®5b);
|
||||
(void) pci_read_config_byte(dev, MRDMODE, ®71);
|
||||
(void) pci_read_config_byte(dev, BMIDESR0, ®72);
|
||||
(void) pci_read_config_byte(dev, UDIDETCR0, ®73);
|
||||
(void) pci_read_config_byte(dev, BMIDESR1, ®7a);
|
||||
(void) pci_read_config_byte(dev, UDIDETCR1, ®7b);
|
||||
|
||||
p += sprintf(p, "--------------- Primary Channel "
|
||||
"---------------- Secondary Channel "
|
||||
"-------------\n");
|
||||
p += sprintf(p, " %sabled "
|
||||
" %sabled\n",
|
||||
(reg72&0x80)?"dis":" en",
|
||||
(reg7a&0x80)?"dis":" en");
|
||||
p += sprintf(p, "--------------- drive0 "
|
||||
"--------- drive1 -------- drive0 "
|
||||
"---------- drive1 ------\n");
|
||||
p += sprintf(p, "DMA enabled: %s %s"
|
||||
" %s %s\n",
|
||||
(reg72&0x20)?"yes":"no ", (reg72&0x40)?"yes":"no ",
|
||||
(reg7a&0x20)?"yes":"no ", (reg7a&0x40)?"yes":"no ");
|
||||
|
||||
p += sprintf(p, "DMA Mode: %s(%s) %s(%s)",
|
||||
(reg72&0x20)?((reg73&0x01)?"UDMA":" DMA"):" PIO",
|
||||
(reg72&0x20)?(
|
||||
((reg73&0x30)==0x30)?(((reg73&0x35)==0x35)?"3":"0"):
|
||||
((reg73&0x20)==0x20)?(((reg73&0x25)==0x25)?"3":"1"):
|
||||
((reg73&0x10)==0x10)?(((reg73&0x15)==0x15)?"4":"2"):
|
||||
((reg73&0x00)==0x00)?(((reg73&0x05)==0x05)?"5":"2"):
|
||||
"X"):"?",
|
||||
(reg72&0x40)?((reg73&0x02)?"UDMA":" DMA"):" PIO",
|
||||
(reg72&0x40)?(
|
||||
((reg73&0xC0)==0xC0)?(((reg73&0xC5)==0xC5)?"3":"0"):
|
||||
((reg73&0x80)==0x80)?(((reg73&0x85)==0x85)?"3":"1"):
|
||||
((reg73&0x40)==0x40)?(((reg73&0x4A)==0x4A)?"4":"2"):
|
||||
((reg73&0x00)==0x00)?(((reg73&0x0A)==0x0A)?"5":"2"):
|
||||
"X"):"?");
|
||||
p += sprintf(p, " %s(%s) %s(%s)\n",
|
||||
(reg7a&0x20)?((reg7b&0x01)?"UDMA":" DMA"):" PIO",
|
||||
(reg7a&0x20)?(
|
||||
((reg7b&0x30)==0x30)?(((reg7b&0x35)==0x35)?"3":"0"):
|
||||
((reg7b&0x20)==0x20)?(((reg7b&0x25)==0x25)?"3":"1"):
|
||||
((reg7b&0x10)==0x10)?(((reg7b&0x15)==0x15)?"4":"2"):
|
||||
((reg7b&0x00)==0x00)?(((reg7b&0x05)==0x05)?"5":"2"):
|
||||
"X"):"?",
|
||||
(reg7a&0x40)?((reg7b&0x02)?"UDMA":" DMA"):" PIO",
|
||||
(reg7a&0x40)?(
|
||||
((reg7b&0xC0)==0xC0)?(((reg7b&0xC5)==0xC5)?"3":"0"):
|
||||
((reg7b&0x80)==0x80)?(((reg7b&0x85)==0x85)?"3":"1"):
|
||||
((reg7b&0x40)==0x40)?(((reg7b&0x4A)==0x4A)?"4":"2"):
|
||||
((reg7b&0x00)==0x00)?(((reg7b&0x0A)==0x0A)?"5":"2"):
|
||||
"X"):"?" );
|
||||
p += sprintf(p, "PIO Mode: %s %s"
|
||||
" %s %s\n",
|
||||
"?", "?", "?", "?");
|
||||
p += sprintf(p, " %s %s\n",
|
||||
(reg50 & CFR_INTR_CH0) ? "interrupting" : "polling ",
|
||||
(reg57 & ARTTIM23_INTR_CH1) ? "interrupting" : "polling");
|
||||
p += sprintf(p, " %s %s\n",
|
||||
(reg71 & MRDMODE_INTR_CH0) ? "pending" : "clear ",
|
||||
(reg71 & MRDMODE_INTR_CH1) ? "pending" : "clear");
|
||||
p += sprintf(p, " %s %s\n",
|
||||
(reg71 & MRDMODE_BLK_CH0) ? "blocked" : "enabled",
|
||||
(reg71 & MRDMODE_BLK_CH1) ? "blocked" : "enabled");
|
||||
|
||||
return (char *)p;
|
||||
}
|
||||
|
||||
static int cmd64x_get_info (char *buffer, char **addr, off_t offset, int count)
|
||||
{
|
||||
char *p = buffer;
|
||||
int i;
|
||||
|
||||
p += sprintf(p, "\n");
|
||||
for (i = 0; i < n_cmd_devs; i++) {
|
||||
struct pci_dev *dev = cmd_devs[i];
|
||||
p = print_cmd64x_get_info(p, dev, i);
|
||||
}
|
||||
return p-buffer; /* => must be less than 4k! */
|
||||
}
|
||||
|
||||
#endif /* defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) */
|
||||
|
||||
static u8 quantize_timing(int timing, int quant)
|
||||
{
|
||||
return (timing + quant - 1) / quant;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine writes the prepared setup/active/recovery counts
|
||||
* for a drive into the cmd646 chipset registers to active them.
|
||||
*/
|
||||
static void program_drive_counts (ide_drive_t *drive, int setup_count, int active_count, int recovery_count)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct pci_dev *dev = HWIF(drive)->pci_dev;
|
||||
ide_drive_t *drives = HWIF(drive)->drives;
|
||||
u8 temp_b;
|
||||
static const u8 setup_counts[] = {0x40, 0x40, 0x40, 0x80, 0, 0xc0};
|
||||
static const u8 recovery_counts[] =
|
||||
{15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0};
|
||||
static const u8 arttim_regs[2][2] = {
|
||||
{ ARTTIM0, ARTTIM1 },
|
||||
{ ARTTIM23, ARTTIM23 }
|
||||
};
|
||||
static const u8 drwtim_regs[2][2] = {
|
||||
{ DRWTIM0, DRWTIM1 },
|
||||
{ DRWTIM2, DRWTIM3 }
|
||||
};
|
||||
int channel = (int) HWIF(drive)->channel;
|
||||
int slave = (drives != drive); /* Is this really the best way to determine this?? */
|
||||
|
||||
cmdprintk("program_drive_count parameters = s(%d),a(%d),r(%d),p(%d)\n",
|
||||
setup_count, active_count, recovery_count, drive->present);
|
||||
/*
|
||||
* Set up address setup count registers.
|
||||
* Primary interface has individual count/timing registers for
|
||||
* each drive. Secondary interface has one common set of registers,
|
||||
* for address setup so we merge these timings, using the slowest
|
||||
* value.
|
||||
*/
|
||||
if (channel) {
|
||||
drive->drive_data = setup_count;
|
||||
setup_count = max(drives[0].drive_data,
|
||||
drives[1].drive_data);
|
||||
cmdprintk("Secondary interface, setup_count = %d\n",
|
||||
setup_count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert values to internal chipset representation
|
||||
*/
|
||||
setup_count = (setup_count > 5) ? 0xc0 : (int) setup_counts[setup_count];
|
||||
active_count &= 0xf; /* Remember, max value is 16 */
|
||||
recovery_count = (int) recovery_counts[recovery_count];
|
||||
|
||||
cmdprintk("Final values = %d,%d,%d\n",
|
||||
setup_count, active_count, recovery_count);
|
||||
|
||||
/*
|
||||
* Now that everything is ready, program the new timings
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
/*
|
||||
* Program the address_setup clocks into ARTTIM reg,
|
||||
* and then the active/recovery counts into the DRWTIM reg
|
||||
*/
|
||||
(void) pci_read_config_byte(dev, arttim_regs[channel][slave], &temp_b);
|
||||
(void) pci_write_config_byte(dev, arttim_regs[channel][slave],
|
||||
((u8) setup_count) | (temp_b & 0x3f));
|
||||
(void) pci_write_config_byte(dev, drwtim_regs[channel][slave],
|
||||
(u8) ((active_count << 4) | recovery_count));
|
||||
cmdprintk ("Write %x to %x\n",
|
||||
((u8) setup_count) | (temp_b & 0x3f),
|
||||
arttim_regs[channel][slave]);
|
||||
cmdprintk ("Write %x to %x\n",
|
||||
(u8) ((active_count << 4) | recovery_count),
|
||||
drwtim_regs[channel][slave]);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine selects drive's best PIO mode, calculates setup/active/recovery
|
||||
* counts, and then writes them into the chipset registers.
|
||||
*/
|
||||
static u8 cmd64x_tune_pio (ide_drive_t *drive, u8 mode_wanted)
|
||||
{
|
||||
int setup_time, active_time, cycle_time;
|
||||
u8 cycle_count, setup_count, active_count, recovery_count;
|
||||
u8 pio_mode;
|
||||
int clock_time = 1000 / system_bus_clock();
|
||||
ide_pio_data_t pio;
|
||||
|
||||
pio_mode = ide_get_best_pio_mode(drive, mode_wanted, 5, &pio);
|
||||
cycle_time = pio.cycle_time;
|
||||
|
||||
setup_time = ide_pio_timings[pio_mode].setup_time;
|
||||
active_time = ide_pio_timings[pio_mode].active_time;
|
||||
|
||||
setup_count = quantize_timing( setup_time, clock_time);
|
||||
cycle_count = quantize_timing( cycle_time, clock_time);
|
||||
active_count = quantize_timing(active_time, clock_time);
|
||||
|
||||
recovery_count = cycle_count - active_count;
|
||||
/* program_drive_counts() takes care of zero recovery cycles */
|
||||
if (recovery_count > 16) {
|
||||
active_count += recovery_count - 16;
|
||||
recovery_count = 16;
|
||||
}
|
||||
if (active_count > 16)
|
||||
active_count = 16; /* maximum allowed by cmd64x */
|
||||
|
||||
program_drive_counts (drive, setup_count, active_count, recovery_count);
|
||||
|
||||
cmdprintk("%s: PIO mode wanted %d, selected %d (%dns)%s, "
|
||||
"clocks=%d/%d/%d\n",
|
||||
drive->name, mode_wanted, pio_mode, cycle_time,
|
||||
pio.overridden ? " (overriding vendor mode)" : "",
|
||||
setup_count, active_count, recovery_count);
|
||||
|
||||
return pio_mode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to set drive's PIO mode.
|
||||
* Special cases are 8: prefetch off, 9: prefetch on (both never worked),
|
||||
* and 255: auto-select best mode (used at boot time).
|
||||
*/
|
||||
static void cmd64x_tune_drive (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
/*
|
||||
* Filter out the prefetch control values
|
||||
* to prevent PIO5 from being programmed
|
||||
*/
|
||||
if (pio == 8 || pio == 9)
|
||||
return;
|
||||
|
||||
pio = cmd64x_tune_pio(drive, pio);
|
||||
(void) ide_config_drive_speed(drive, XFER_PIO_0 + pio);
|
||||
}
|
||||
|
||||
static u8 cmd64x_ratemask (ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = HWIF(drive)->pci_dev;
|
||||
u8 mode = 0;
|
||||
|
||||
switch(dev->device) {
|
||||
case PCI_DEVICE_ID_CMD_649:
|
||||
mode = 3;
|
||||
break;
|
||||
case PCI_DEVICE_ID_CMD_648:
|
||||
mode = 2;
|
||||
break;
|
||||
case PCI_DEVICE_ID_CMD_643:
|
||||
return 0;
|
||||
|
||||
case PCI_DEVICE_ID_CMD_646:
|
||||
{
|
||||
unsigned int class_rev = 0;
|
||||
pci_read_config_dword(dev,
|
||||
PCI_CLASS_REVISION, &class_rev);
|
||||
class_rev &= 0xff;
|
||||
/*
|
||||
* UltraDMA only supported on PCI646U and PCI646U2, which
|
||||
* correspond to revisions 0x03, 0x05 and 0x07 respectively.
|
||||
* Actually, although the CMD tech support people won't
|
||||
* tell me the details, the 0x03 revision cannot support
|
||||
* UDMA correctly without hardware modifications, and even
|
||||
* then it only works with Quantum disks due to some
|
||||
* hold time assumptions in the 646U part which are fixed
|
||||
* in the 646U2.
|
||||
*
|
||||
* So we only do UltraDMA on revision 0x05 and 0x07 chipsets.
|
||||
*/
|
||||
switch(class_rev) {
|
||||
case 0x07:
|
||||
case 0x05:
|
||||
return 1;
|
||||
case 0x03:
|
||||
case 0x01:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min(mode, (u8)1);
|
||||
return mode;
|
||||
}
|
||||
|
||||
static int cmd64x_tune_chipset (ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
u8 unit = (drive->select.b.unit & 0x01);
|
||||
u8 regU = 0, pciU = (hwif->channel) ? UDIDETCR1 : UDIDETCR0;
|
||||
u8 regD = 0, pciD = (hwif->channel) ? BMIDESR1 : BMIDESR0;
|
||||
|
||||
u8 speed = ide_rate_filter(cmd64x_ratemask(drive), xferspeed);
|
||||
|
||||
if (speed >= XFER_SW_DMA_0) {
|
||||
(void) pci_read_config_byte(dev, pciD, ®D);
|
||||
(void) pci_read_config_byte(dev, pciU, ®U);
|
||||
regD &= ~(unit ? 0x40 : 0x20);
|
||||
regU &= ~(unit ? 0xCA : 0x35);
|
||||
(void) pci_write_config_byte(dev, pciD, regD);
|
||||
(void) pci_write_config_byte(dev, pciU, regU);
|
||||
(void) pci_read_config_byte(dev, pciD, ®D);
|
||||
(void) pci_read_config_byte(dev, pciU, ®U);
|
||||
}
|
||||
|
||||
switch(speed) {
|
||||
case XFER_UDMA_5: regU |= (unit ? 0x0A : 0x05); break;
|
||||
case XFER_UDMA_4: regU |= (unit ? 0x4A : 0x15); break;
|
||||
case XFER_UDMA_3: regU |= (unit ? 0x8A : 0x25); break;
|
||||
case XFER_UDMA_2: regU |= (unit ? 0x42 : 0x11); break;
|
||||
case XFER_UDMA_1: regU |= (unit ? 0x82 : 0x21); break;
|
||||
case XFER_UDMA_0: regU |= (unit ? 0xC2 : 0x31); break;
|
||||
case XFER_MW_DMA_2: regD |= (unit ? 0x40 : 0x10); break;
|
||||
case XFER_MW_DMA_1: regD |= (unit ? 0x80 : 0x20); break;
|
||||
case XFER_MW_DMA_0: regD |= (unit ? 0xC0 : 0x30); break;
|
||||
case XFER_SW_DMA_2: regD |= (unit ? 0x40 : 0x10); break;
|
||||
case XFER_SW_DMA_1: regD |= (unit ? 0x80 : 0x20); break;
|
||||
case XFER_SW_DMA_0: regD |= (unit ? 0xC0 : 0x30); break;
|
||||
case XFER_PIO_5:
|
||||
case XFER_PIO_4:
|
||||
case XFER_PIO_3:
|
||||
case XFER_PIO_2:
|
||||
case XFER_PIO_1:
|
||||
case XFER_PIO_0:
|
||||
(void) cmd64x_tune_pio(drive, speed - XFER_PIO_0);
|
||||
break;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (speed >= XFER_SW_DMA_0) {
|
||||
(void) pci_write_config_byte(dev, pciU, regU);
|
||||
regD |= (unit ? 0x40 : 0x20);
|
||||
(void) pci_write_config_byte(dev, pciD, regD);
|
||||
}
|
||||
|
||||
return (ide_config_drive_speed(drive, speed));
|
||||
}
|
||||
|
||||
static int config_chipset_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, cmd64x_ratemask(drive));
|
||||
|
||||
if (!speed)
|
||||
return 0;
|
||||
|
||||
if (cmd64x_tune_chipset(drive, speed))
|
||||
return 0;
|
||||
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
static int cmd64x_config_drive_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
if (ide_use_dma(drive) && config_chipset_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
if (ide_use_fast_pio(drive))
|
||||
cmd64x_tune_drive(drive, 255);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cmd64x_alt_dma_status (struct pci_dev *dev)
|
||||
{
|
||||
switch(dev->device) {
|
||||
case PCI_DEVICE_ID_CMD_648:
|
||||
case PCI_DEVICE_ID_CMD_649:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd64x_ide_dma_end (ide_drive_t *drive)
|
||||
{
|
||||
u8 dma_stat = 0, dma_cmd = 0;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
drive->waiting_for_dma = 0;
|
||||
/* read DMA command state */
|
||||
dma_cmd = inb(hwif->dma_command);
|
||||
/* stop DMA */
|
||||
outb(dma_cmd & ~1, hwif->dma_command);
|
||||
/* get DMA status */
|
||||
dma_stat = inb(hwif->dma_status);
|
||||
/* clear the INTR & ERROR bits */
|
||||
outb(dma_stat | 6, hwif->dma_status);
|
||||
if (cmd64x_alt_dma_status(dev)) {
|
||||
u8 dma_intr = 0;
|
||||
u8 dma_mask = (hwif->channel) ? ARTTIM23_INTR_CH1 :
|
||||
CFR_INTR_CH0;
|
||||
u8 dma_reg = (hwif->channel) ? ARTTIM2 : CFR;
|
||||
(void) pci_read_config_byte(dev, dma_reg, &dma_intr);
|
||||
/* clear the INTR bit */
|
||||
(void) pci_write_config_byte(dev, dma_reg, dma_intr|dma_mask);
|
||||
}
|
||||
/* purge DMA mappings */
|
||||
ide_destroy_dmatable(drive);
|
||||
/* verify good DMA status */
|
||||
return (dma_stat & 7) != 4;
|
||||
}
|
||||
|
||||
static int cmd64x_ide_dma_test_irq (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u8 dma_alt_stat = 0, mask = (hwif->channel) ? MRDMODE_INTR_CH1 :
|
||||
MRDMODE_INTR_CH0;
|
||||
u8 dma_stat = inb(hwif->dma_status);
|
||||
|
||||
(void) pci_read_config_byte(dev, MRDMODE, &dma_alt_stat);
|
||||
#ifdef DEBUG
|
||||
printk("%s: dma_stat: 0x%02x dma_alt_stat: "
|
||||
"0x%02x mask: 0x%02x\n", drive->name,
|
||||
dma_stat, dma_alt_stat, mask);
|
||||
#endif
|
||||
if (!(dma_alt_stat & mask))
|
||||
return 0;
|
||||
|
||||
/* return 1 if INTR asserted */
|
||||
if ((dma_stat & 4) == 4)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ASUS P55T2P4D with CMD646 chipset revision 0x01 requires the old
|
||||
* event order for DMA transfers.
|
||||
*/
|
||||
|
||||
static int cmd646_1_ide_dma_end (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 dma_stat = 0, dma_cmd = 0;
|
||||
|
||||
drive->waiting_for_dma = 0;
|
||||
/* get DMA status */
|
||||
dma_stat = inb(hwif->dma_status);
|
||||
/* read DMA command state */
|
||||
dma_cmd = inb(hwif->dma_command);
|
||||
/* stop DMA */
|
||||
outb(dma_cmd & ~1, hwif->dma_command);
|
||||
/* clear the INTR & ERROR bits */
|
||||
outb(dma_stat | 6, hwif->dma_status);
|
||||
/* and free any DMA resources */
|
||||
ide_destroy_dmatable(drive);
|
||||
/* verify good DMA status */
|
||||
return (dma_stat & 7) != 4;
|
||||
}
|
||||
|
||||
static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev, const char *name)
|
||||
{
|
||||
u32 class_rev = 0;
|
||||
u8 mrdmode = 0;
|
||||
|
||||
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
|
||||
class_rev &= 0xff;
|
||||
|
||||
switch(dev->device) {
|
||||
case PCI_DEVICE_ID_CMD_643:
|
||||
break;
|
||||
case PCI_DEVICE_ID_CMD_646:
|
||||
printk(KERN_INFO "%s: chipset revision 0x%02X, ", name, class_rev);
|
||||
switch(class_rev) {
|
||||
case 0x07:
|
||||
case 0x05:
|
||||
printk("UltraDMA Capable");
|
||||
break;
|
||||
case 0x03:
|
||||
printk("MultiWord DMA Force Limited");
|
||||
break;
|
||||
case 0x01:
|
||||
default:
|
||||
printk("MultiWord DMA Limited, IRQ workaround enabled");
|
||||
break;
|
||||
}
|
||||
printk("\n");
|
||||
break;
|
||||
case PCI_DEVICE_ID_CMD_648:
|
||||
case PCI_DEVICE_ID_CMD_649:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set a good latency timer and cache line size value. */
|
||||
(void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
|
||||
/* FIXME: pci_set_master() to ensure a good latency timer value */
|
||||
|
||||
/* Setup interrupts. */
|
||||
(void) pci_read_config_byte(dev, MRDMODE, &mrdmode);
|
||||
mrdmode &= ~(0x30);
|
||||
(void) pci_write_config_byte(dev, MRDMODE, mrdmode);
|
||||
|
||||
/* Use MEMORY READ LINE for reads.
|
||||
* NOTE: Although not mentioned in the PCI0646U specs,
|
||||
* these bits are write only and won't be read
|
||||
* back as set or not. The PCI0646U2 specs clarify
|
||||
* this point.
|
||||
*/
|
||||
(void) pci_write_config_byte(dev, MRDMODE, mrdmode | 0x02);
|
||||
|
||||
/* Set reasonable active/recovery/address-setup values. */
|
||||
(void) pci_write_config_byte(dev, ARTTIM0, 0x40);
|
||||
(void) pci_write_config_byte(dev, DRWTIM0, 0x3f);
|
||||
(void) pci_write_config_byte(dev, ARTTIM1, 0x40);
|
||||
(void) pci_write_config_byte(dev, DRWTIM1, 0x3f);
|
||||
#ifdef __i386__
|
||||
(void) pci_write_config_byte(dev, ARTTIM23, 0x1c);
|
||||
#else
|
||||
(void) pci_write_config_byte(dev, ARTTIM23, 0x5c);
|
||||
#endif
|
||||
(void) pci_write_config_byte(dev, DRWTIM23, 0x3f);
|
||||
(void) pci_write_config_byte(dev, DRWTIM3, 0x3f);
|
||||
#ifdef CONFIG_PPC
|
||||
(void) pci_write_config_byte(dev, UDIDETCR0, 0xf0);
|
||||
#endif /* CONFIG_PPC */
|
||||
|
||||
#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
|
||||
|
||||
cmd_devs[n_cmd_devs++] = dev;
|
||||
|
||||
if (!cmd64x_proc) {
|
||||
cmd64x_proc = 1;
|
||||
ide_pci_create_host_proc("cmd64x", cmd64x_get_info);
|
||||
}
|
||||
#endif /* DISPLAY_CMD64X_TIMINGS && CONFIG_PROC_FS */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int __devinit ata66_cmd64x(ide_hwif_t *hwif)
|
||||
{
|
||||
u8 ata66 = 0, mask = (hwif->channel) ? 0x02 : 0x01;
|
||||
|
||||
switch(hwif->pci_dev->device) {
|
||||
case PCI_DEVICE_ID_CMD_643:
|
||||
case PCI_DEVICE_ID_CMD_646:
|
||||
return ata66;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pci_read_config_byte(hwif->pci_dev, BMIDECSR, &ata66);
|
||||
return (ata66 & mask) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_cmd64x(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
unsigned int class_rev;
|
||||
|
||||
hwif->autodma = 0;
|
||||
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
|
||||
class_rev &= 0xff;
|
||||
|
||||
hwif->tuneproc = &cmd64x_tune_drive;
|
||||
hwif->speedproc = &cmd64x_tune_chipset;
|
||||
|
||||
hwif->drives[0].autotune = hwif->drives[1].autotune = 1;
|
||||
|
||||
if (!hwif->dma_base)
|
||||
return;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
|
||||
hwif->ultra_mask = 0x3f;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
hwif->swdma_mask = 0x07;
|
||||
|
||||
if (dev->device == PCI_DEVICE_ID_CMD_643)
|
||||
hwif->ultra_mask = 0x80;
|
||||
if (dev->device == PCI_DEVICE_ID_CMD_646)
|
||||
hwif->ultra_mask = (class_rev > 0x04) ? 0x07 : 0x80;
|
||||
if (dev->device == PCI_DEVICE_ID_CMD_648)
|
||||
hwif->ultra_mask = 0x1f;
|
||||
|
||||
hwif->ide_dma_check = &cmd64x_config_drive_for_dma;
|
||||
if (!(hwif->udma_four))
|
||||
hwif->udma_four = ata66_cmd64x(hwif);
|
||||
|
||||
if (dev->device == PCI_DEVICE_ID_CMD_646) {
|
||||
hwif->chipset = ide_cmd646;
|
||||
if (class_rev == 0x01) {
|
||||
hwif->ide_dma_end = &cmd646_1_ide_dma_end;
|
||||
} else {
|
||||
hwif->ide_dma_end = &cmd64x_ide_dma_end;
|
||||
hwif->ide_dma_test_irq = &cmd64x_ide_dma_test_irq;
|
||||
}
|
||||
} else {
|
||||
hwif->ide_dma_end = &cmd64x_ide_dma_end;
|
||||
hwif->ide_dma_test_irq = &cmd64x_ide_dma_test_irq;
|
||||
}
|
||||
|
||||
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
static ide_pci_device_t cmd64x_chipsets[] __devinitdata = {
|
||||
{ /* 0 */
|
||||
.name = "CMD643",
|
||||
.init_chipset = init_chipset_cmd64x,
|
||||
.init_hwif = init_hwif_cmd64x,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 1 */
|
||||
.name = "CMD646",
|
||||
.init_chipset = init_chipset_cmd64x,
|
||||
.init_hwif = init_hwif_cmd64x,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.enablebits = {{0x00,0x00,0x00}, {0x51,0x80,0x80}},
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 2 */
|
||||
.name = "CMD648",
|
||||
.init_chipset = init_chipset_cmd64x,
|
||||
.init_hwif = init_hwif_cmd64x,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 3 */
|
||||
.name = "CMD649",
|
||||
.init_chipset = init_chipset_cmd64x,
|
||||
.init_hwif = init_hwif_cmd64x,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
}
|
||||
};
|
||||
|
||||
static int __devinit cmd64x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &cmd64x_chipsets[id->driver_data]);
|
||||
}
|
||||
|
||||
static struct pci_device_id cmd64x_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_643, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
|
||||
{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_648, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2},
|
||||
{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, cmd64x_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "CMD64x_IDE",
|
||||
.id_table = cmd64x_pci_tbl,
|
||||
.probe = cmd64x_init_one,
|
||||
};
|
||||
|
||||
static int __init cmd64x_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(cmd64x_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Eddie Dost, David Miller, Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for CMD64x IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
271
drivers/ide/pci/cs5520.c
Normal file
271
drivers/ide/pci/cs5520.c
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* IDE tuning and bus mastering support for the CS5510/CS5520
|
||||
* chipsets
|
||||
*
|
||||
* The CS5510/CS5520 are slightly unusual devices. Unlike the
|
||||
* typical IDE controllers they do bus mastering with the drive in
|
||||
* PIO mode and smarter silicon.
|
||||
*
|
||||
* The practical upshot of this is that we must always tune the
|
||||
* drive for the right PIO mode. We must also ignore all the blacklists
|
||||
* and the drive bus mastering DMA information.
|
||||
*
|
||||
* *** This driver is strictly experimental ***
|
||||
*
|
||||
* (c) Copyright Red Hat Inc 2002
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* For the avoidance of doubt the "preferred form" of this code is one which
|
||||
* is in an open non patent encumbered format. Where cryptographic key signing
|
||||
* forms part of the process of creating an executable the information
|
||||
* including keys needed to generate an equivalently functional executable
|
||||
* are deemed to be part of the source code.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.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/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
struct pio_clocks
|
||||
{
|
||||
int address;
|
||||
int assert;
|
||||
int recovery;
|
||||
};
|
||||
|
||||
static struct pio_clocks cs5520_pio_clocks[]={
|
||||
{3, 6, 11},
|
||||
{2, 5, 6},
|
||||
{1, 4, 3},
|
||||
{1, 3, 2},
|
||||
{1, 2, 1}
|
||||
};
|
||||
|
||||
static int cs5520_tune_chipset(ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *pdev = hwif->pci_dev;
|
||||
u8 speed = min((u8)XFER_PIO_4, xferspeed);
|
||||
int pio = speed;
|
||||
u8 reg;
|
||||
int controller = drive->dn > 1 ? 1 : 0;
|
||||
int error;
|
||||
|
||||
switch(speed)
|
||||
{
|
||||
case XFER_PIO_4:
|
||||
case XFER_PIO_3:
|
||||
case XFER_PIO_2:
|
||||
case XFER_PIO_1:
|
||||
case XFER_PIO_0:
|
||||
pio -= XFER_PIO_0;
|
||||
break;
|
||||
default:
|
||||
pio = 0;
|
||||
printk(KERN_ERR "cs55x0: bad ide timing.\n");
|
||||
}
|
||||
|
||||
printk("PIO clocking = %d\n", pio);
|
||||
|
||||
/* FIXME: if DMA = 1 do we need to set the DMA bit here ? */
|
||||
|
||||
/* 8bit CAT/CRT - 8bit command timing for channel */
|
||||
pci_write_config_byte(pdev, 0x62 + controller,
|
||||
(cs5520_pio_clocks[pio].recovery << 4) |
|
||||
(cs5520_pio_clocks[pio].assert));
|
||||
|
||||
/* 0x64 - 16bit Primary, 0x68 - 16bit Secondary */
|
||||
|
||||
/* FIXME: should these use address ? */
|
||||
/* Data read timing */
|
||||
pci_write_config_byte(pdev, 0x64 + 4*controller + (drive->dn&1),
|
||||
(cs5520_pio_clocks[pio].recovery << 4) |
|
||||
(cs5520_pio_clocks[pio].assert));
|
||||
/* Write command timing */
|
||||
pci_write_config_byte(pdev, 0x66 + 4*controller + (drive->dn&1),
|
||||
(cs5520_pio_clocks[pio].recovery << 4) |
|
||||
(cs5520_pio_clocks[pio].assert));
|
||||
|
||||
/* Set the DMA enable/disable flag */
|
||||
reg = inb(hwif->dma_base + 0x02 + 8*controller);
|
||||
reg |= 1<<((drive->dn&1)+5);
|
||||
outb(reg, hwif->dma_base + 0x02 + 8*controller);
|
||||
|
||||
error = ide_config_drive_speed(drive, speed);
|
||||
/* ATAPI is harder so leave it for now */
|
||||
if(!error && drive->media == ide_disk)
|
||||
error = hwif->ide_dma_on(drive);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void cs5520_tune_drive(ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
|
||||
cs5520_tune_chipset(drive, (XFER_PIO_0 + pio));
|
||||
}
|
||||
|
||||
static int cs5520_config_drive_xfer_rate(ide_drive_t *drive)
|
||||
{
|
||||
/* Tune the drive for PIO modes up to PIO 4 */
|
||||
cs5520_tune_drive(drive, 4);
|
||||
|
||||
/* Then tell the core to use DMA operations */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We provide a callback for our nonstandard DMA location
|
||||
*/
|
||||
|
||||
static void __devinit cs5520_init_setup_dma(struct pci_dev *dev, ide_pci_device_t *d, ide_hwif_t *hwif)
|
||||
{
|
||||
unsigned long bmide = pci_resource_start(dev, 2); /* Not the usual 4 */
|
||||
if(hwif->mate && hwif->mate->dma_base) /* Second channel at primary + 8 */
|
||||
bmide += 8;
|
||||
ide_setup_dma(hwif, bmide, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
* We wrap the DMA activate to set the vdma flag. This is needed
|
||||
* so that the IDE DMA layer issues PIO not DMA commands over the
|
||||
* DMA channel
|
||||
*/
|
||||
|
||||
static int cs5520_dma_on(ide_drive_t *drive)
|
||||
{
|
||||
drive->vdma = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_cs5520(ide_hwif_t *hwif)
|
||||
{
|
||||
hwif->tuneproc = &cs5520_tune_drive;
|
||||
hwif->speedproc = &cs5520_tune_chipset;
|
||||
hwif->ide_dma_check = &cs5520_config_drive_xfer_rate;
|
||||
hwif->ide_dma_on = &cs5520_dma_on;
|
||||
|
||||
if(!noautodma)
|
||||
hwif->autodma = 1;
|
||||
|
||||
if(!hwif->dma_base)
|
||||
{
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].autotune = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
hwif->atapi_dma = 0;
|
||||
hwif->ultra_mask = 0;
|
||||
hwif->swdma_mask = 0;
|
||||
hwif->mwdma_mask = 0;
|
||||
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
#define DECLARE_CS_DEV(name_str) \
|
||||
{ \
|
||||
.name = name_str, \
|
||||
.init_setup_dma = cs5520_init_setup_dma, \
|
||||
.init_hwif = init_hwif_cs5520, \
|
||||
.channels = 2, \
|
||||
.autodma = AUTODMA, \
|
||||
.bootable = ON_BOARD, \
|
||||
.flags = IDEPCI_FLAG_ISA_PORTS, \
|
||||
}
|
||||
|
||||
static ide_pci_device_t cyrix_chipsets[] __devinitdata = {
|
||||
/* 0 */ DECLARE_CS_DEV("Cyrix 5510"),
|
||||
/* 1 */ DECLARE_CS_DEV("Cyrix 5520")
|
||||
};
|
||||
|
||||
/*
|
||||
* The 5510/5520 are a bit weird. They don't quite set up the way
|
||||
* the PCI helper layer expects so we must do much of the set up
|
||||
* work longhand.
|
||||
*/
|
||||
|
||||
static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
ata_index_t index;
|
||||
ide_pci_device_t *d = &cyrix_chipsets[id->driver_data];
|
||||
|
||||
ide_setup_pci_noise(dev, d);
|
||||
|
||||
/* We must not grab the entire device, it has 'ISA' space in its
|
||||
BARS too and we will freak out other bits of the kernel */
|
||||
if (pci_enable_device_bars(dev, 1<<2)) {
|
||||
printk(KERN_WARNING "%s: Unable to enable 55x0.\n", d->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
pci_set_master(dev);
|
||||
if (pci_set_dma_mask(dev, DMA_32BIT_MASK)) {
|
||||
printk(KERN_WARNING "cs5520: No suitable DMA available.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
index.all = 0xf0f0;
|
||||
|
||||
/*
|
||||
* Now the chipset is configured we can let the core
|
||||
* do all the device setup for us
|
||||
*/
|
||||
|
||||
ide_pci_setup_ports(dev, d, 14, &index);
|
||||
|
||||
if((index.b.low & 0xf0) != 0xf0)
|
||||
probe_hwif_init(&ide_hwifs[index.b.low]);
|
||||
if((index.b.high & 0xf0) != 0xf0)
|
||||
probe_hwif_init(&ide_hwifs[index.b.high]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pci_device_id cs5520_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, cs5520_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "Cyrix_IDE",
|
||||
.id_table = cs5520_pci_tbl,
|
||||
.probe = cs5520_init_one,
|
||||
};
|
||||
|
||||
static int __init cs5520_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(cs5520_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Alan Cox");
|
||||
MODULE_DESCRIPTION("PCI driver module for Cyrix 5510/5520 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
377
drivers/ide/pci/cs5530.c
Normal file
377
drivers/ide/pci/cs5530.c
Normal file
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/cs5530.c Version 0.7 Sept 10, 2002
|
||||
*
|
||||
* Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org>
|
||||
* Ditto of GNU General Public License.
|
||||
*
|
||||
* Copyright (C) 2000 Mark Lord <mlord@pobox.com>
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*
|
||||
* Development of this chipset driver was funded
|
||||
* by the nice folks at National Semiconductor.
|
||||
*
|
||||
* Documentation:
|
||||
* CS5530 documentation available from National Semiconductor.
|
||||
*/
|
||||
|
||||
#include <linux/module.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/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
/**
|
||||
* cs5530_xfer_set_mode - set a new transfer mode at the drive
|
||||
* @drive: drive to tune
|
||||
* @mode: new mode
|
||||
*
|
||||
* Logging wrapper to the IDE driver speed configuration. This can
|
||||
* probably go away now.
|
||||
*/
|
||||
|
||||
static int cs5530_set_xfer_mode (ide_drive_t *drive, u8 mode)
|
||||
{
|
||||
printk(KERN_DEBUG "%s: cs5530_set_xfer_mode(%s)\n",
|
||||
drive->name, ide_xfer_verbose(mode));
|
||||
return (ide_config_drive_speed(drive, mode));
|
||||
}
|
||||
|
||||
/*
|
||||
* Here are the standard PIO mode 0-4 timings for each "format".
|
||||
* Format-0 uses fast data reg timings, with slower command reg timings.
|
||||
* Format-1 uses fast timings for all registers, but won't work with all drives.
|
||||
*/
|
||||
static unsigned int cs5530_pio_timings[2][5] = {
|
||||
{0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010},
|
||||
{0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010}
|
||||
};
|
||||
|
||||
/*
|
||||
* After chip reset, the PIO timings are set to 0x0000e132, which is not valid.
|
||||
*/
|
||||
#define CS5530_BAD_PIO(timings) (((timings)&~0x80000000)==0x0000e132)
|
||||
#define CS5530_BASEREG(hwif) (((hwif)->dma_base & ~0xf) + ((hwif)->channel ? 0x30 : 0x20))
|
||||
|
||||
/**
|
||||
* cs5530_tuneproc - select/set PIO modes
|
||||
*
|
||||
* cs5530_tuneproc() handles selection/setting of PIO modes
|
||||
* for both the chipset and drive.
|
||||
*
|
||||
* The ide_init_cs5530() routine guarantees that all drives
|
||||
* will have valid default PIO timings set up before we get here.
|
||||
*/
|
||||
|
||||
static void cs5530_tuneproc (ide_drive_t *drive, u8 pio) /* pio=255 means "autotune" */
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned int format;
|
||||
unsigned long basereg = CS5530_BASEREG(hwif);
|
||||
static u8 modes[5] = { XFER_PIO_0, XFER_PIO_1, XFER_PIO_2, XFER_PIO_3, XFER_PIO_4};
|
||||
|
||||
pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
|
||||
if (!cs5530_set_xfer_mode(drive, modes[pio])) {
|
||||
format = (inl(basereg + 4) >> 31) & 1;
|
||||
outl(cs5530_pio_timings[format][pio],
|
||||
basereg+(drive->select.b.unit<<3));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cs5530_config_dma - select/set DMA and UDMA modes
|
||||
* @drive: drive to tune
|
||||
*
|
||||
* cs5530_config_dma() handles selection/setting of DMA/UDMA modes
|
||||
* for both the chipset and drive. The CS5530 has limitations about
|
||||
* mixing DMA/UDMA on the same cable.
|
||||
*/
|
||||
|
||||
static int cs5530_config_dma (ide_drive_t *drive)
|
||||
{
|
||||
int udma_ok = 1, mode = 0;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
int unit = drive->select.b.unit;
|
||||
ide_drive_t *mate = &hwif->drives[unit^1];
|
||||
struct hd_driveid *id = drive->id;
|
||||
unsigned int reg, timings = 0;
|
||||
unsigned long basereg;
|
||||
|
||||
/*
|
||||
* Default to DMA-off in case we run into trouble here.
|
||||
*/
|
||||
hwif->dma_off_quietly(drive);
|
||||
|
||||
/*
|
||||
* The CS5530 specifies that two drives sharing a cable cannot
|
||||
* mix UDMA/MDMA. It has to be one or the other, for the pair,
|
||||
* though different timings can still be chosen for each drive.
|
||||
* We could set the appropriate timing bits on the fly,
|
||||
* but that might be a bit confusing. So, for now we statically
|
||||
* handle this requirement by looking at our mate drive to see
|
||||
* what it is capable of, before choosing a mode for our own drive.
|
||||
*
|
||||
* Note: This relies on the fact we never fail from UDMA to MWDMA_2
|
||||
* but instead drop to PIO
|
||||
*/
|
||||
if (mate->present) {
|
||||
struct hd_driveid *mateid = mate->id;
|
||||
if (mateid && (mateid->capability & 1) &&
|
||||
!__ide_dma_bad_drive(mate)) {
|
||||
if ((mateid->field_valid & 4) &&
|
||||
(mateid->dma_ultra & 7))
|
||||
udma_ok = 1;
|
||||
else if ((mateid->field_valid & 2) &&
|
||||
(mateid->dma_mword & 7))
|
||||
udma_ok = 0;
|
||||
else
|
||||
udma_ok = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now see what the current drive is capable of,
|
||||
* selecting UDMA only if the mate said it was ok.
|
||||
*/
|
||||
if (id && (id->capability & 1) && drive->autodma &&
|
||||
!__ide_dma_bad_drive(drive)) {
|
||||
if (udma_ok && (id->field_valid & 4) && (id->dma_ultra & 7)) {
|
||||
if (id->dma_ultra & 4)
|
||||
mode = XFER_UDMA_2;
|
||||
else if (id->dma_ultra & 2)
|
||||
mode = XFER_UDMA_1;
|
||||
else if (id->dma_ultra & 1)
|
||||
mode = XFER_UDMA_0;
|
||||
}
|
||||
if (!mode && (id->field_valid & 2) && (id->dma_mword & 7)) {
|
||||
if (id->dma_mword & 4)
|
||||
mode = XFER_MW_DMA_2;
|
||||
else if (id->dma_mword & 2)
|
||||
mode = XFER_MW_DMA_1;
|
||||
else if (id->dma_mword & 1)
|
||||
mode = XFER_MW_DMA_0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Tell the drive to switch to the new mode; abort on failure.
|
||||
*/
|
||||
if (!mode || cs5530_set_xfer_mode(drive, mode))
|
||||
return 1; /* failure */
|
||||
|
||||
/*
|
||||
* Now tune the chipset to match the drive:
|
||||
*/
|
||||
switch (mode) {
|
||||
case XFER_UDMA_0: timings = 0x00921250; break;
|
||||
case XFER_UDMA_1: timings = 0x00911140; break;
|
||||
case XFER_UDMA_2: timings = 0x00911030; break;
|
||||
case XFER_MW_DMA_0: timings = 0x00077771; break;
|
||||
case XFER_MW_DMA_1: timings = 0x00012121; break;
|
||||
case XFER_MW_DMA_2: timings = 0x00002020; break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
basereg = CS5530_BASEREG(hwif);
|
||||
reg = inl(basereg + 4); /* get drive0 config register */
|
||||
timings |= reg & 0x80000000; /* preserve PIO format bit */
|
||||
if (unit == 0) { /* are we configuring drive0? */
|
||||
outl(timings, basereg + 4); /* write drive0 config register */
|
||||
} else {
|
||||
if (timings & 0x00100000)
|
||||
reg |= 0x00100000; /* enable UDMA timings for both drives */
|
||||
else
|
||||
reg &= ~0x00100000; /* disable UDMA timings for both drives */
|
||||
outl(reg, basereg + 4); /* write drive0 config register */
|
||||
outl(timings, basereg + 12); /* write drive1 config register */
|
||||
}
|
||||
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
/**
|
||||
* init_chipset_5530 - set up 5530 bridge
|
||||
* @dev: PCI device
|
||||
* @name: device name
|
||||
*
|
||||
* Initialize the cs5530 bridge for reliable IDE DMA operation.
|
||||
*/
|
||||
|
||||
static unsigned int __devinit init_chipset_cs5530 (struct pci_dev *dev, const char *name)
|
||||
{
|
||||
struct pci_dev *master_0 = NULL, *cs5530_0 = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
dev = NULL;
|
||||
while ((dev = pci_get_device(PCI_VENDOR_ID_CYRIX, PCI_ANY_ID, dev)) != NULL) {
|
||||
switch (dev->device) {
|
||||
case PCI_DEVICE_ID_CYRIX_PCI_MASTER:
|
||||
master_0 = pci_dev_get(dev);
|
||||
break;
|
||||
case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
|
||||
cs5530_0 = pci_dev_get(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!master_0) {
|
||||
printk(KERN_ERR "%s: unable to locate PCI MASTER function\n", name);
|
||||
goto out;
|
||||
}
|
||||
if (!cs5530_0) {
|
||||
printk(KERN_ERR "%s: unable to locate CS5530 LEGACY function\n", name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ide_lock, flags);
|
||||
/* all CPUs (there should only be one CPU with this chipset) */
|
||||
|
||||
/*
|
||||
* Enable BusMaster and MemoryWriteAndInvalidate for the cs5530:
|
||||
* --> OR 0x14 into 16-bit PCI COMMAND reg of function 0 of the cs5530
|
||||
*/
|
||||
|
||||
pci_set_master(cs5530_0);
|
||||
pci_set_mwi(cs5530_0);
|
||||
|
||||
/*
|
||||
* Set PCI CacheLineSize to 16-bytes:
|
||||
* --> Write 0x04 into 8-bit PCI CACHELINESIZE reg of function 0 of the cs5530
|
||||
*/
|
||||
|
||||
pci_write_config_byte(cs5530_0, PCI_CACHE_LINE_SIZE, 0x04);
|
||||
|
||||
/*
|
||||
* Disable trapping of UDMA register accesses (Win98 hack):
|
||||
* --> Write 0x5006 into 16-bit reg at offset 0xd0 of function 0 of the cs5530
|
||||
*/
|
||||
|
||||
pci_write_config_word(cs5530_0, 0xd0, 0x5006);
|
||||
|
||||
/*
|
||||
* Bit-1 at 0x40 enables MemoryWriteAndInvalidate on internal X-bus:
|
||||
* The other settings are what is necessary to get the register
|
||||
* into a sane state for IDE DMA operation.
|
||||
*/
|
||||
|
||||
pci_write_config_byte(master_0, 0x40, 0x1e);
|
||||
|
||||
/*
|
||||
* Set max PCI burst size (16-bytes seems to work best):
|
||||
* 16bytes: set bit-1 at 0x41 (reg value of 0x16)
|
||||
* all others: clear bit-1 at 0x41, and do:
|
||||
* 128bytes: OR 0x00 at 0x41
|
||||
* 256bytes: OR 0x04 at 0x41
|
||||
* 512bytes: OR 0x08 at 0x41
|
||||
* 1024bytes: OR 0x0c at 0x41
|
||||
*/
|
||||
|
||||
pci_write_config_byte(master_0, 0x41, 0x14);
|
||||
|
||||
/*
|
||||
* These settings are necessary to get the chip
|
||||
* into a sane state for IDE DMA operation.
|
||||
*/
|
||||
|
||||
pci_write_config_byte(master_0, 0x42, 0x00);
|
||||
pci_write_config_byte(master_0, 0x43, 0xc1);
|
||||
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
|
||||
out:
|
||||
pci_dev_put(master_0);
|
||||
pci_dev_put(cs5530_0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_hwif_cs5530 - initialise an IDE channel
|
||||
* @hwif: IDE to initialize
|
||||
*
|
||||
* This gets invoked by the IDE driver once for each channel. It
|
||||
* performs channel-specific pre-initialization before drive probing.
|
||||
*/
|
||||
|
||||
static void __devinit init_hwif_cs5530 (ide_hwif_t *hwif)
|
||||
{
|
||||
unsigned long basereg;
|
||||
u32 d0_timings;
|
||||
hwif->autodma = 0;
|
||||
|
||||
if (hwif->mate)
|
||||
hwif->serialized = hwif->mate->serialized = 1;
|
||||
|
||||
hwif->tuneproc = &cs5530_tuneproc;
|
||||
basereg = CS5530_BASEREG(hwif);
|
||||
d0_timings = inl(basereg + 0);
|
||||
if (CS5530_BAD_PIO(d0_timings)) {
|
||||
/* PIO timings not initialized? */
|
||||
outl(cs5530_pio_timings[(d0_timings >> 31) & 1][0], basereg + 0);
|
||||
if (!hwif->drives[0].autotune)
|
||||
hwif->drives[0].autotune = 1;
|
||||
/* needs autotuning later */
|
||||
}
|
||||
if (CS5530_BAD_PIO(inl(basereg + 8))) {
|
||||
/* PIO timings not initialized? */
|
||||
outl(cs5530_pio_timings[(d0_timings >> 31) & 1][0], basereg + 8);
|
||||
if (!hwif->drives[1].autotune)
|
||||
hwif->drives[1].autotune = 1;
|
||||
/* needs autotuning later */
|
||||
}
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x07;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
|
||||
hwif->ide_dma_check = &cs5530_config_dma;
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
static ide_pci_device_t cs5530_chipset __devinitdata = {
|
||||
.name = "CS5530",
|
||||
.init_chipset = init_chipset_cs5530,
|
||||
.init_hwif = init_hwif_cs5530,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
};
|
||||
|
||||
static int __devinit cs5530_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &cs5530_chipset);
|
||||
}
|
||||
|
||||
static struct pci_device_id cs5530_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, cs5530_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "CS5530 IDE",
|
||||
.id_table = cs5530_pci_tbl,
|
||||
.probe = cs5530_init_one,
|
||||
};
|
||||
|
||||
static int __init cs5530_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(cs5530_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Mark Lord");
|
||||
MODULE_DESCRIPTION("PCI driver module for Cyrix/NS 5530 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
295
drivers/ide/pci/cs5535.c
Normal file
295
drivers/ide/pci/cs5535.c
Normal file
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/cs5535.c
|
||||
*
|
||||
* Copyright (C) 2004-2005 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* History:
|
||||
* 09/20/2005 - Jaya Kumar <jayakumar.ide@gmail.com>
|
||||
* - Reworked tuneproc, set_drive, misc mods to prep for mainline
|
||||
* - Work was sponsored by CIS (M) Sdn Bhd.
|
||||
* Ported to Kernel 2.6.11 on June 26, 2005 by
|
||||
* Wolfgang Zuleger <wolfgang.zuleger@gmx.de>
|
||||
* Alexander Kiausch <alex.kiausch@t-online.de>
|
||||
* Originally developed by AMD for 2.4/2.6
|
||||
*
|
||||
* Development of this chipset driver was funded
|
||||
* by the nice folks at National Semiconductor/AMD.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Documentation:
|
||||
* CS5535 documentation available from AMD
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include "ide-timing.h"
|
||||
|
||||
#define MSR_ATAC_BASE 0x51300000
|
||||
#define ATAC_GLD_MSR_CAP (MSR_ATAC_BASE+0)
|
||||
#define ATAC_GLD_MSR_CONFIG (MSR_ATAC_BASE+0x01)
|
||||
#define ATAC_GLD_MSR_SMI (MSR_ATAC_BASE+0x02)
|
||||
#define ATAC_GLD_MSR_ERROR (MSR_ATAC_BASE+0x03)
|
||||
#define ATAC_GLD_MSR_PM (MSR_ATAC_BASE+0x04)
|
||||
#define ATAC_GLD_MSR_DIAG (MSR_ATAC_BASE+0x05)
|
||||
#define ATAC_IO_BAR (MSR_ATAC_BASE+0x08)
|
||||
#define ATAC_RESET (MSR_ATAC_BASE+0x10)
|
||||
#define ATAC_CH0D0_PIO (MSR_ATAC_BASE+0x20)
|
||||
#define ATAC_CH0D0_DMA (MSR_ATAC_BASE+0x21)
|
||||
#define ATAC_CH0D1_PIO (MSR_ATAC_BASE+0x22)
|
||||
#define ATAC_CH0D1_DMA (MSR_ATAC_BASE+0x23)
|
||||
#define ATAC_PCI_ABRTERR (MSR_ATAC_BASE+0x24)
|
||||
#define ATAC_BM0_CMD_PRIM 0x00
|
||||
#define ATAC_BM0_STS_PRIM 0x02
|
||||
#define ATAC_BM0_PRD 0x04
|
||||
#define CS5535_CABLE_DETECT 0x48
|
||||
|
||||
/* Format I PIO settings. We seperate out cmd and data for safer timings */
|
||||
|
||||
static unsigned int cs5535_pio_cmd_timings[5] =
|
||||
{ 0xF7F4, 0x53F3, 0x13F1, 0x5131, 0x1131 };
|
||||
static unsigned int cs5535_pio_dta_timings[5] =
|
||||
{ 0xF7F4, 0xF173, 0x8141, 0x5131, 0x1131 };
|
||||
|
||||
static unsigned int cs5535_mwdma_timings[3] =
|
||||
{ 0x7F0FFFF3, 0x7F035352, 0x7f024241 };
|
||||
|
||||
static unsigned int cs5535_udma_timings[5] =
|
||||
{ 0x7F7436A1, 0x7F733481, 0x7F723261, 0x7F713161, 0x7F703061 };
|
||||
|
||||
/* Macros to check if the register is the reset value - reset value is an
|
||||
invalid timing and indicates the register has not been set previously */
|
||||
|
||||
#define CS5535_BAD_PIO(timings) ( (timings&~0x80000000UL) == 0x00009172 )
|
||||
#define CS5535_BAD_DMA(timings) ( (timings & 0x000FFFFF) == 0x00077771 )
|
||||
|
||||
/****
|
||||
* cs5535_set_speed - Configure the chipset to the new speed
|
||||
* @drive: Drive to set up
|
||||
* @speed: desired speed
|
||||
*
|
||||
* cs5535_set_speed() configures the chipset to a new speed.
|
||||
*/
|
||||
static void cs5535_set_speed(ide_drive_t *drive, u8 speed)
|
||||
{
|
||||
|
||||
u32 reg = 0, dummy;
|
||||
int unit = drive->select.b.unit;
|
||||
|
||||
|
||||
/* Set the PIO timings */
|
||||
if ((speed & XFER_MODE) == XFER_PIO) {
|
||||
u8 pioa;
|
||||
u8 piob;
|
||||
u8 cmd;
|
||||
|
||||
pioa = speed - XFER_PIO_0;
|
||||
piob = ide_get_best_pio_mode(&(drive->hwif->drives[!unit]),
|
||||
255, 4, NULL);
|
||||
cmd = pioa < piob ? pioa : piob;
|
||||
|
||||
/* Write the speed of the current drive */
|
||||
reg = (cs5535_pio_cmd_timings[cmd] << 16) |
|
||||
cs5535_pio_dta_timings[pioa];
|
||||
wrmsr(unit ? ATAC_CH0D1_PIO : ATAC_CH0D0_PIO, reg, 0);
|
||||
|
||||
/* And if nessesary - change the speed of the other drive */
|
||||
rdmsr(unit ? ATAC_CH0D0_PIO : ATAC_CH0D1_PIO, reg, dummy);
|
||||
|
||||
if (((reg >> 16) & cs5535_pio_cmd_timings[cmd]) !=
|
||||
cs5535_pio_cmd_timings[cmd]) {
|
||||
reg &= 0x0000FFFF;
|
||||
reg |= cs5535_pio_cmd_timings[cmd] << 16;
|
||||
wrmsr(unit ? ATAC_CH0D0_PIO : ATAC_CH0D1_PIO, reg, 0);
|
||||
}
|
||||
|
||||
/* Set bit 31 of the DMA register for PIO format 1 timings */
|
||||
rdmsr(unit ? ATAC_CH0D1_DMA : ATAC_CH0D0_DMA, reg, dummy);
|
||||
wrmsr(unit ? ATAC_CH0D1_DMA : ATAC_CH0D0_DMA,
|
||||
reg | 0x80000000UL, 0);
|
||||
} else {
|
||||
rdmsr(unit ? ATAC_CH0D1_DMA : ATAC_CH0D0_DMA, reg, dummy);
|
||||
|
||||
reg &= 0x80000000UL; /* Preserve the PIO format bit */
|
||||
|
||||
if (speed >= XFER_UDMA_0 && speed <= XFER_UDMA_7)
|
||||
reg |= cs5535_udma_timings[speed - XFER_UDMA_0];
|
||||
else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2)
|
||||
reg |= cs5535_mwdma_timings[speed - XFER_MW_DMA_0];
|
||||
else
|
||||
return;
|
||||
|
||||
wrmsr(unit ? ATAC_CH0D1_DMA : ATAC_CH0D0_DMA, reg, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 cs5535_ratemask(ide_drive_t *drive)
|
||||
{
|
||||
/* eighty93 will return 1 if it's 80core and capable of
|
||||
exceeding udma2, 0 otherwise. we need ratemask to set
|
||||
the max speed and if we can > udma2 then we return 2
|
||||
which selects speed_max as udma4 which is the 5535's max
|
||||
speed, and 1 selects udma2 which is the max for 40c */
|
||||
if (!eighty_ninty_three(drive))
|
||||
return 1;
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
/****
|
||||
* cs5535_set_drive - Configure the drive to the new speed
|
||||
* @drive: Drive to set up
|
||||
* @speed: desired speed
|
||||
*
|
||||
* cs5535_set_drive() configures the drive and the chipset to a
|
||||
* new speed. It also can be called by upper layers.
|
||||
*/
|
||||
static int cs5535_set_drive(ide_drive_t *drive, u8 speed)
|
||||
{
|
||||
speed = ide_rate_filter(cs5535_ratemask(drive), speed);
|
||||
ide_config_drive_speed(drive, speed);
|
||||
cs5535_set_speed(drive, speed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****
|
||||
* cs5535_tuneproc - PIO setup
|
||||
* @drive: drive to set up
|
||||
* @pio: mode to use (255 for 'best possible')
|
||||
*
|
||||
* A callback from the upper layers for PIO-only tuning.
|
||||
*/
|
||||
static void cs5535_tuneproc(ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
u8 modes[] = { XFER_PIO_0, XFER_PIO_1, XFER_PIO_2, XFER_PIO_3,
|
||||
XFER_PIO_4 };
|
||||
|
||||
/* cs5535 max pio is pio 4, best_pio will check the blacklist.
|
||||
i think we don't need to rate_filter the incoming xferspeed
|
||||
since we know we're only going to choose pio */
|
||||
xferspeed = ide_get_best_pio_mode(drive, xferspeed, 4, NULL);
|
||||
ide_config_drive_speed(drive, modes[xferspeed]);
|
||||
cs5535_set_speed(drive, xferspeed);
|
||||
}
|
||||
|
||||
static int cs5535_config_drive_for_dma(ide_drive_t *drive)
|
||||
{
|
||||
u8 speed;
|
||||
|
||||
speed = ide_dma_speed(drive, cs5535_ratemask(drive));
|
||||
|
||||
/* If no DMA speed was available then let dma_check hit pio */
|
||||
if (!speed) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cs5535_set_drive(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
static int cs5535_dma_check(ide_drive_t *drive)
|
||||
{
|
||||
u8 speed;
|
||||
|
||||
drive->init_speed = 0;
|
||||
|
||||
if (ide_use_dma(drive) && cs5535_config_drive_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
if (ide_use_fast_pio(drive)) {
|
||||
speed = ide_get_best_pio_mode(drive, 255, 4, NULL);
|
||||
cs5535_set_drive(drive, speed);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static u8 __devinit cs5535_cable_detect(struct pci_dev *dev)
|
||||
{
|
||||
u8 bit;
|
||||
|
||||
/* if a 80 wire cable was detected */
|
||||
pci_read_config_byte(dev, CS5535_CABLE_DETECT, &bit);
|
||||
return (bit & 1);
|
||||
}
|
||||
|
||||
/****
|
||||
* init_hwif_cs5535 - Initialize one ide cannel
|
||||
* @hwif: Channel descriptor
|
||||
*
|
||||
* This gets invoked by the IDE driver once for each channel. It
|
||||
* performs channel-specific pre-initialization before drive probing.
|
||||
*
|
||||
*/
|
||||
static void __devinit init_hwif_cs5535(ide_hwif_t *hwif)
|
||||
{
|
||||
int i;
|
||||
|
||||
hwif->autodma = 0;
|
||||
|
||||
hwif->tuneproc = &cs5535_tuneproc;
|
||||
hwif->speedproc = &cs5535_set_drive;
|
||||
hwif->ide_dma_check = &cs5535_dma_check;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x1F;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
|
||||
|
||||
hwif->udma_four = cs5535_cable_detect(hwif->pci_dev);
|
||||
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
|
||||
/* just setting autotune and not worrying about bios timings */
|
||||
for (i = 0; i < 2; i++) {
|
||||
hwif->drives[i].autotune = 1;
|
||||
hwif->drives[i].autodma = hwif->autodma;
|
||||
}
|
||||
}
|
||||
|
||||
static ide_pci_device_t cs5535_chipset __devinitdata = {
|
||||
.name = "CS5535",
|
||||
.init_hwif = init_hwif_cs5535,
|
||||
.channels = 1,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
};
|
||||
|
||||
static int __devinit cs5535_init_one(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &cs5535_chipset);
|
||||
}
|
||||
|
||||
static struct pci_device_id cs5535_pci_tbl[] =
|
||||
{
|
||||
{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_IDE, PCI_ANY_ID,
|
||||
PCI_ANY_ID, 0, 0, 0},
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, cs5535_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "CS5535_IDE",
|
||||
.id_table = cs5535_pci_tbl,
|
||||
.probe = cs5535_init_one,
|
||||
};
|
||||
|
||||
static int __init cs5535_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(cs5535_ide_init);
|
||||
|
||||
MODULE_AUTHOR("AMD");
|
||||
MODULE_DESCRIPTION("PCI driver module for AMD/NS CS5535 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
528
drivers/ide/pci/cy82c693.c
Normal file
528
drivers/ide/pci/cy82c693.c
Normal file
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/cy82c693.c Version 0.40 Sep. 10, 2002
|
||||
*
|
||||
* Copyright (C) 1998-2000 Andreas S. Krebs (akrebs@altavista.net), Maintainer
|
||||
* Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org>, Integrator
|
||||
*
|
||||
* CYPRESS CY82C693 chipset IDE controller
|
||||
*
|
||||
* The CY82C693 chipset is used on Digital's PC-Alpha 164SX boards.
|
||||
* Writing the driver was quite simple, since most of the job is
|
||||
* done by the generic pci-ide support.
|
||||
* The hard part was finding the CY82C693's datasheet on Cypress's
|
||||
* web page :-(. But Altavista solved this problem :-).
|
||||
*
|
||||
*
|
||||
* Notes:
|
||||
* - I recently got a 16.8G IBM DTTA, so I was able to test it with
|
||||
* a large and fast disk - the results look great, so I'd say the
|
||||
* driver is working fine :-)
|
||||
* hdparm -t reports 8.17 MB/sec at about 6% CPU usage for the DTTA
|
||||
* - this is my first linux driver, so there's probably a lot of room
|
||||
* for optimizations and bug fixing, so feel free to do it.
|
||||
* - use idebus=xx parameter to set PCI bus speed - needed to calc
|
||||
* timings for PIO modes (default will be 40)
|
||||
* - if using PIO mode it's a good idea to set the PIO mode and
|
||||
* 32-bit I/O support (if possible), e.g. hdparm -p2 -c1 /dev/hda
|
||||
* - I had some problems with my IBM DHEA with PIO modes < 2
|
||||
* (lost interrupts) ?????
|
||||
* - first tests with DMA look okay, they seem to work, but there is a
|
||||
* problem with sound - the BusMaster IDE TimeOut should fixed this
|
||||
*
|
||||
* Ancient History:
|
||||
* AMH@1999-08-24: v0.34 init_cy82c693_chip moved to pci_init_cy82c693
|
||||
* ASK@1999-01-23: v0.33 made a few minor code clean ups
|
||||
* removed DMA clock speed setting by default
|
||||
* added boot message
|
||||
* ASK@1998-11-01: v0.32 added support to set BusMaster IDE TimeOut
|
||||
* added support to set DMA Controller Clock Speed
|
||||
* ASK@1998-10-31: v0.31 fixed problem with setting to high DMA modes
|
||||
* on some drives.
|
||||
* ASK@1998-10-29: v0.3 added support to set DMA modes
|
||||
* ASK@1998-10-28: v0.2 added support to set PIO modes
|
||||
* ASK@1998-10-27: v0.1 first version - chipset detection
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
/* the current version */
|
||||
#define CY82_VERSION "CY82C693U driver v0.34 99-13-12 Andreas S. Krebs (akrebs@altavista.net)"
|
||||
|
||||
/*
|
||||
* The following are used to debug the driver.
|
||||
*/
|
||||
#define CY82C693_DEBUG_LOGS 0
|
||||
#define CY82C693_DEBUG_INFO 0
|
||||
|
||||
/* define CY82C693_SETDMA_CLOCK to set DMA Controller Clock Speed to ATCLK */
|
||||
#undef CY82C693_SETDMA_CLOCK
|
||||
|
||||
/*
|
||||
* NOTE: the value for busmaster timeout is tricky and I got it by
|
||||
* trial and error! By using a to low value will cause DMA timeouts
|
||||
* and drop IDE performance, and by using a to high value will cause
|
||||
* audio playback to scatter.
|
||||
* If you know a better value or how to calc it, please let me know.
|
||||
*/
|
||||
|
||||
/* twice the value written in cy82c693ub datasheet */
|
||||
#define BUSMASTER_TIMEOUT 0x50
|
||||
/*
|
||||
* the value above was tested on my machine and it seems to work okay
|
||||
*/
|
||||
|
||||
/* here are the offset definitions for the registers */
|
||||
#define CY82_IDE_CMDREG 0x04
|
||||
#define CY82_IDE_ADDRSETUP 0x48
|
||||
#define CY82_IDE_MASTER_IOR 0x4C
|
||||
#define CY82_IDE_MASTER_IOW 0x4D
|
||||
#define CY82_IDE_SLAVE_IOR 0x4E
|
||||
#define CY82_IDE_SLAVE_IOW 0x4F
|
||||
#define CY82_IDE_MASTER_8BIT 0x50
|
||||
#define CY82_IDE_SLAVE_8BIT 0x51
|
||||
|
||||
#define CY82_INDEX_PORT 0x22
|
||||
#define CY82_DATA_PORT 0x23
|
||||
|
||||
#define CY82_INDEX_CTRLREG1 0x01
|
||||
#define CY82_INDEX_CHANNEL0 0x30
|
||||
#define CY82_INDEX_CHANNEL1 0x31
|
||||
#define CY82_INDEX_TIMEOUT 0x32
|
||||
|
||||
/* the max PIO mode - from datasheet */
|
||||
#define CY82C693_MAX_PIO 4
|
||||
|
||||
/* the min and max PCI bus speed in MHz - from datasheet */
|
||||
#define CY82C963_MIN_BUS_SPEED 25
|
||||
#define CY82C963_MAX_BUS_SPEED 33
|
||||
|
||||
/* the struct for the PIO mode timings */
|
||||
typedef struct pio_clocks_s {
|
||||
u8 address_time; /* Address setup (clocks) */
|
||||
u8 time_16r; /* clocks for 16bit IOR (0xF0=Active/data, 0x0F=Recovery) */
|
||||
u8 time_16w; /* clocks for 16bit IOW (0xF0=Active/data, 0x0F=Recovery) */
|
||||
u8 time_8; /* clocks for 8bit (0xF0=Active/data, 0x0F=Recovery) */
|
||||
} pio_clocks_t;
|
||||
|
||||
/*
|
||||
* calc clocks using bus_speed
|
||||
* returns (rounded up) time in bus clocks for time in ns
|
||||
*/
|
||||
static int calc_clk (int time, int bus_speed)
|
||||
{
|
||||
int clocks;
|
||||
|
||||
clocks = (time*bus_speed+999)/1000 -1;
|
||||
|
||||
if (clocks < 0)
|
||||
clocks = 0;
|
||||
|
||||
if (clocks > 0x0F)
|
||||
clocks = 0x0F;
|
||||
|
||||
return clocks;
|
||||
}
|
||||
|
||||
/*
|
||||
* compute the values for the clock registers for PIO
|
||||
* mode and pci_clk [MHz] speed
|
||||
*
|
||||
* NOTE: for mode 0,1 and 2 drives 8-bit IDE command control registers are used
|
||||
* for mode 3 and 4 drives 8 and 16-bit timings are the same
|
||||
*
|
||||
*/
|
||||
static void compute_clocks (u8 pio, pio_clocks_t *p_pclk)
|
||||
{
|
||||
int clk1, clk2;
|
||||
int bus_speed = system_bus_clock(); /* get speed of PCI bus */
|
||||
|
||||
/* we don't check against CY82C693's min and max speed,
|
||||
* so you can play with the idebus=xx parameter
|
||||
*/
|
||||
|
||||
if (pio > CY82C693_MAX_PIO)
|
||||
pio = CY82C693_MAX_PIO;
|
||||
|
||||
/* let's calc the address setup time clocks */
|
||||
p_pclk->address_time = (u8)calc_clk(ide_pio_timings[pio].setup_time, bus_speed);
|
||||
|
||||
/* let's calc the active and recovery time clocks */
|
||||
clk1 = calc_clk(ide_pio_timings[pio].active_time, bus_speed);
|
||||
|
||||
/* calc recovery timing */
|
||||
clk2 = ide_pio_timings[pio].cycle_time -
|
||||
ide_pio_timings[pio].active_time -
|
||||
ide_pio_timings[pio].setup_time;
|
||||
|
||||
clk2 = calc_clk(clk2, bus_speed);
|
||||
|
||||
clk1 = (clk1<<4)|clk2; /* combine active and recovery clocks */
|
||||
|
||||
/* note: we use the same values for 16bit IOR and IOW
|
||||
* those are all the same, since I don't have other
|
||||
* timings than those from ide-lib.c
|
||||
*/
|
||||
|
||||
p_pclk->time_16r = (u8)clk1;
|
||||
p_pclk->time_16w = (u8)clk1;
|
||||
|
||||
/* what are good values for 8bit ?? */
|
||||
p_pclk->time_8 = (u8)clk1;
|
||||
}
|
||||
|
||||
/*
|
||||
* set DMA mode a specific channel for CY82C693
|
||||
*/
|
||||
|
||||
static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single)
|
||||
{
|
||||
u8 index = 0, data = 0;
|
||||
|
||||
if (mode>2) /* make sure we set a valid mode */
|
||||
mode = 2;
|
||||
|
||||
if (mode > drive->id->tDMA) /* to be absolutly sure we have a valid mode */
|
||||
mode = drive->id->tDMA;
|
||||
|
||||
index = (HWIF(drive)->channel==0) ? CY82_INDEX_CHANNEL0 : CY82_INDEX_CHANNEL1;
|
||||
|
||||
#if CY82C693_DEBUG_LOGS
|
||||
/* for debug let's show the previous values */
|
||||
|
||||
outb(index, CY82_INDEX_PORT);
|
||||
data = inb(CY82_DATA_PORT);
|
||||
|
||||
printk (KERN_INFO "%s (ch=%d, dev=%d): DMA mode is %d (single=%d)\n",
|
||||
drive->name, HWIF(drive)->channel, drive->select.b.unit,
|
||||
(data&0x3), ((data>>2)&1));
|
||||
#endif /* CY82C693_DEBUG_LOGS */
|
||||
|
||||
data = (u8)mode|(u8)(single<<2);
|
||||
|
||||
outb(index, CY82_INDEX_PORT);
|
||||
outb(data, CY82_DATA_PORT);
|
||||
|
||||
#if CY82C693_DEBUG_INFO
|
||||
printk(KERN_INFO "%s (ch=%d, dev=%d): set DMA mode to %d (single=%d)\n",
|
||||
drive->name, HWIF(drive)->channel, drive->select.b.unit,
|
||||
mode, single);
|
||||
#endif /* CY82C693_DEBUG_INFO */
|
||||
|
||||
/*
|
||||
* note: below we set the value for Bus Master IDE TimeOut Register
|
||||
* I'm not absolutly sure what this does, but it solved my problem
|
||||
* with IDE DMA and sound, so I now can play sound and work with
|
||||
* my IDE driver at the same time :-)
|
||||
*
|
||||
* If you know the correct (best) value for this register please
|
||||
* let me know - ASK
|
||||
*/
|
||||
|
||||
data = BUSMASTER_TIMEOUT;
|
||||
outb(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT);
|
||||
outb(data, CY82_DATA_PORT);
|
||||
|
||||
#if CY82C693_DEBUG_INFO
|
||||
printk (KERN_INFO "%s: Set IDE Bus Master TimeOut Register to 0x%X\n",
|
||||
drive->name, data);
|
||||
#endif /* CY82C693_DEBUG_INFO */
|
||||
}
|
||||
|
||||
/*
|
||||
* used to set DMA mode for CY82C693 (single and multi modes)
|
||||
*/
|
||||
static int cy82c693_ide_dma_on (ide_drive_t *drive)
|
||||
{
|
||||
struct hd_driveid *id = drive->id;
|
||||
|
||||
#if CY82C693_DEBUG_INFO
|
||||
printk (KERN_INFO "dma_on: %s\n", drive->name);
|
||||
#endif /* CY82C693_DEBUG_INFO */
|
||||
|
||||
if (id != NULL) {
|
||||
/* Enable DMA on any drive that has DMA
|
||||
* (multi or single) enabled
|
||||
*/
|
||||
if (id->field_valid & 2) { /* regular DMA */
|
||||
int mmode, smode;
|
||||
|
||||
mmode = id->dma_mword & (id->dma_mword >> 8);
|
||||
smode = id->dma_1word & (id->dma_1word >> 8);
|
||||
|
||||
if (mmode != 0) {
|
||||
/* enable multi */
|
||||
cy82c693_dma_enable(drive, (mmode >> 1), 0);
|
||||
} else if (smode != 0) {
|
||||
/* enable single */
|
||||
cy82c693_dma_enable(drive, (smode >> 1), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return __ide_dma_on(drive);
|
||||
}
|
||||
|
||||
/*
|
||||
* tune ide drive - set PIO mode
|
||||
*/
|
||||
static void cy82c693_tune_drive (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
pio_clocks_t pclk;
|
||||
unsigned int addrCtrl;
|
||||
|
||||
/* select primary or secondary channel */
|
||||
if (hwif->index > 0) { /* drive is on the secondary channel */
|
||||
dev = pci_get_slot(dev->bus, dev->devfn+1);
|
||||
if (!dev) {
|
||||
printk(KERN_ERR "%s: tune_drive: "
|
||||
"Cannot find secondary interface!\n",
|
||||
drive->name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#if CY82C693_DEBUG_LOGS
|
||||
/* for debug let's show the register values */
|
||||
|
||||
if (drive->select.b.unit == 0) {
|
||||
/*
|
||||
* get master drive registers
|
||||
* address setup control register
|
||||
* is 32 bit !!!
|
||||
*/
|
||||
pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
|
||||
addrCtrl &= 0x0F;
|
||||
|
||||
/* now let's get the remaining registers */
|
||||
pci_read_config_byte(dev, CY82_IDE_MASTER_IOR, &pclk.time_16r);
|
||||
pci_read_config_byte(dev, CY82_IDE_MASTER_IOW, &pclk.time_16w);
|
||||
pci_read_config_byte(dev, CY82_IDE_MASTER_8BIT, &pclk.time_8);
|
||||
} else {
|
||||
/*
|
||||
* set slave drive registers
|
||||
* address setup control register
|
||||
* is 32 bit !!!
|
||||
*/
|
||||
pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
|
||||
|
||||
addrCtrl &= 0xF0;
|
||||
addrCtrl >>= 4;
|
||||
|
||||
/* now let's get the remaining registers */
|
||||
pci_read_config_byte(dev, CY82_IDE_SLAVE_IOR, &pclk.time_16r);
|
||||
pci_read_config_byte(dev, CY82_IDE_SLAVE_IOW, &pclk.time_16w);
|
||||
pci_read_config_byte(dev, CY82_IDE_SLAVE_8BIT, &pclk.time_8);
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s (ch=%d, dev=%d): PIO timing is "
|
||||
"(addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n",
|
||||
drive->name, hwif->channel, drive->select.b.unit,
|
||||
addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
|
||||
#endif /* CY82C693_DEBUG_LOGS */
|
||||
|
||||
/* first let's calc the pio modes */
|
||||
pio = ide_get_best_pio_mode(drive, pio, CY82C693_MAX_PIO, NULL);
|
||||
|
||||
#if CY82C693_DEBUG_INFO
|
||||
printk (KERN_INFO "%s: Selected PIO mode %d\n", drive->name, pio);
|
||||
#endif /* CY82C693_DEBUG_INFO */
|
||||
|
||||
/* let's calc the values for this PIO mode */
|
||||
compute_clocks(pio, &pclk);
|
||||
|
||||
/* now let's write the clocks registers */
|
||||
if (drive->select.b.unit == 0) {
|
||||
/*
|
||||
* set master drive
|
||||
* address setup control register
|
||||
* is 32 bit !!!
|
||||
*/
|
||||
pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
|
||||
|
||||
addrCtrl &= (~0xF);
|
||||
addrCtrl |= (unsigned int)pclk.address_time;
|
||||
pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl);
|
||||
|
||||
/* now let's set the remaining registers */
|
||||
pci_write_config_byte(dev, CY82_IDE_MASTER_IOR, pclk.time_16r);
|
||||
pci_write_config_byte(dev, CY82_IDE_MASTER_IOW, pclk.time_16w);
|
||||
pci_write_config_byte(dev, CY82_IDE_MASTER_8BIT, pclk.time_8);
|
||||
|
||||
addrCtrl &= 0xF;
|
||||
} else {
|
||||
/*
|
||||
* set slave drive
|
||||
* address setup control register
|
||||
* is 32 bit !!!
|
||||
*/
|
||||
pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
|
||||
|
||||
addrCtrl &= (~0xF0);
|
||||
addrCtrl |= ((unsigned int)pclk.address_time<<4);
|
||||
pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl);
|
||||
|
||||
/* now let's set the remaining registers */
|
||||
pci_write_config_byte(dev, CY82_IDE_SLAVE_IOR, pclk.time_16r);
|
||||
pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, pclk.time_16w);
|
||||
pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, pclk.time_8);
|
||||
|
||||
addrCtrl >>= 4;
|
||||
addrCtrl &= 0xF;
|
||||
}
|
||||
|
||||
#if CY82C693_DEBUG_INFO
|
||||
printk(KERN_INFO "%s (ch=%d, dev=%d): set PIO timing to "
|
||||
"(addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n",
|
||||
drive->name, hwif->channel, drive->select.b.unit,
|
||||
addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
|
||||
#endif /* CY82C693_DEBUG_INFO */
|
||||
}
|
||||
|
||||
/*
|
||||
* this function is called during init and is used to setup the cy82c693 chip
|
||||
*/
|
||||
static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const char *name)
|
||||
{
|
||||
if (PCI_FUNC(dev->devfn) != 1)
|
||||
return 0;
|
||||
|
||||
#ifdef CY82C693_SETDMA_CLOCK
|
||||
u8 data = 0;
|
||||
#endif /* CY82C693_SETDMA_CLOCK */
|
||||
|
||||
/* write info about this verion of the driver */
|
||||
printk(KERN_INFO CY82_VERSION "\n");
|
||||
|
||||
#ifdef CY82C693_SETDMA_CLOCK
|
||||
/* okay let's set the DMA clock speed */
|
||||
|
||||
outb(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT);
|
||||
data = inb(CY82_DATA_PORT);
|
||||
|
||||
#if CY82C693_DEBUG_INFO
|
||||
printk(KERN_INFO "%s: Peripheral Configuration Register: 0x%X\n",
|
||||
name, data);
|
||||
#endif /* CY82C693_DEBUG_INFO */
|
||||
|
||||
/*
|
||||
* for some reason sometimes the DMA controller
|
||||
* speed is set to ATCLK/2 ???? - we fix this here
|
||||
*
|
||||
* note: i don't know what causes this strange behaviour,
|
||||
* but even changing the dma speed doesn't solve it :-(
|
||||
* the ide performance is still only half the normal speed
|
||||
*
|
||||
* if anybody knows what goes wrong with my machine, please
|
||||
* let me know - ASK
|
||||
*/
|
||||
|
||||
data |= 0x03;
|
||||
|
||||
outb(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT);
|
||||
outb(data, CY82_DATA_PORT);
|
||||
|
||||
#if CY82C693_DEBUG_INFO
|
||||
printk (KERN_INFO "%s: New Peripheral Configuration Register: 0x%X\n",
|
||||
name, data);
|
||||
#endif /* CY82C693_DEBUG_INFO */
|
||||
|
||||
#endif /* CY82C693_SETDMA_CLOCK */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* the init function - called for each ide channel once
|
||||
*/
|
||||
static void __devinit init_hwif_cy82c693(ide_hwif_t *hwif)
|
||||
{
|
||||
hwif->autodma = 0;
|
||||
|
||||
hwif->chipset = ide_cy82c693;
|
||||
hwif->tuneproc = &cy82c693_tune_drive;
|
||||
|
||||
if (!hwif->dma_base) {
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].autotune = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->mwdma_mask = 0x04;
|
||||
hwif->swdma_mask = 0x04;
|
||||
|
||||
hwif->ide_dma_on = &cy82c693_ide_dma_on;
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
static __devinitdata ide_hwif_t *primary;
|
||||
|
||||
static void __devinit init_iops_cy82c693(ide_hwif_t *hwif)
|
||||
{
|
||||
if (PCI_FUNC(hwif->pci_dev->devfn) == 1)
|
||||
primary = hwif;
|
||||
else {
|
||||
hwif->mate = primary;
|
||||
hwif->channel = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static ide_pci_device_t cy82c693_chipset __devinitdata = {
|
||||
.name = "CY82C693",
|
||||
.init_chipset = init_chipset_cy82c693,
|
||||
.init_iops = init_iops_cy82c693,
|
||||
.init_hwif = init_hwif_cy82c693,
|
||||
.channels = 1,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
};
|
||||
|
||||
static int __devinit cy82c693_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct pci_dev *dev2;
|
||||
int ret = -ENODEV;
|
||||
|
||||
/* CY82C693 is more than only a IDE controller.
|
||||
Function 1 is primary IDE channel, function 2 - secondary. */
|
||||
if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE &&
|
||||
PCI_FUNC(dev->devfn) == 1) {
|
||||
dev2 = pci_get_slot(dev->bus, dev->devfn + 1);
|
||||
ret = ide_setup_pci_devices(dev, dev2, &cy82c693_chipset);
|
||||
/* We leak pci refs here but thats ok - we can't be unloaded */
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct pci_device_id cy82c693_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, cy82c693_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "Cypress_IDE",
|
||||
.id_table = cy82c693_pci_tbl,
|
||||
.probe = cy82c693_init_one,
|
||||
};
|
||||
|
||||
static int __init cy82c693_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(cy82c693_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Andreas Krebs, Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for the Cypress CY82C693 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
141
drivers/ide/pci/delkin_cb.c
Normal file
141
drivers/ide/pci/delkin_cb.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/delkin_cb.c
|
||||
*
|
||||
* Created 20 Oct 2004 by Mark Lord
|
||||
*
|
||||
* Basic support for Delkin/ASKA/Workbit Cardbus CompactFlash adapter
|
||||
*
|
||||
* Modeled after the 16-bit PCMCIA driver: ide-cs.c
|
||||
*
|
||||
* This is slightly peculiar, in that it is a PCI driver,
|
||||
* but is NOT an IDE PCI driver -- the IDE layer does not directly
|
||||
* support hot insertion/removal of PCI interfaces, so this driver
|
||||
* is unable to use the IDE PCI interfaces. Instead, it uses the
|
||||
* same interfaces as the ide-cs (PCMCIA) driver uses.
|
||||
* On the plus side, the driver is also smaller/simpler this way.
|
||||
*
|
||||
* 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/autoconf.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/*
|
||||
* No chip documentation has yet been found,
|
||||
* so these configuration values were pulled from
|
||||
* a running Win98 system using "debug".
|
||||
* This gives around 3MByte/second read performance,
|
||||
* which is about 2/3 of what the chip is capable of.
|
||||
*
|
||||
* There is also a 4KByte mmio region on the card,
|
||||
* but its purpose has yet to be reverse-engineered.
|
||||
*/
|
||||
static const u8 setup[] = {
|
||||
0x00, 0x05, 0xbe, 0x01, 0x20, 0x8f, 0x00, 0x00,
|
||||
0xa4, 0x1f, 0xb3, 0x1b, 0x00, 0x00, 0x00, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xa4, 0x83, 0x02, 0x13,
|
||||
};
|
||||
|
||||
static int __devinit
|
||||
delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
unsigned long base;
|
||||
hw_regs_t hw;
|
||||
ide_hwif_t *hwif = NULL;
|
||||
ide_drive_t *drive;
|
||||
int i, rc;
|
||||
|
||||
rc = pci_enable_device(dev);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "delkin_cb: pci_enable_device failed (%d)\n", rc);
|
||||
return rc;
|
||||
}
|
||||
rc = pci_request_regions(dev, "delkin_cb");
|
||||
if (rc) {
|
||||
printk(KERN_ERR "delkin_cb: pci_request_regions failed (%d)\n", rc);
|
||||
pci_disable_device(dev);
|
||||
return rc;
|
||||
}
|
||||
base = pci_resource_start(dev, 0);
|
||||
outb(0x02, base + 0x1e); /* set nIEN to block interrupts */
|
||||
inb(base + 0x17); /* read status to clear interrupts */
|
||||
for (i = 0; i < sizeof(setup); ++i) {
|
||||
if (setup[i])
|
||||
outb(setup[i], base + i);
|
||||
}
|
||||
pci_release_regions(dev); /* IDE layer handles regions itself */
|
||||
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
ide_std_init_ports(&hw, base + 0x10, base + 0x1e);
|
||||
hw.irq = dev->irq;
|
||||
hw.chipset = ide_pci; /* this enables IRQ sharing */
|
||||
|
||||
rc = ide_register_hw_with_fixup(&hw, &hwif, ide_undecoded_slave);
|
||||
if (rc < 0) {
|
||||
printk(KERN_ERR "delkin_cb: ide_register_hw failed (%d)\n", rc);
|
||||
pci_disable_device(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
pci_set_drvdata(dev, hwif);
|
||||
hwif->pci_dev = dev;
|
||||
drive = &hwif->drives[0];
|
||||
if (drive->present) {
|
||||
drive->io_32bit = 1;
|
||||
drive->unmask = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
delkin_cb_remove (struct pci_dev *dev)
|
||||
{
|
||||
ide_hwif_t *hwif = pci_get_drvdata(dev);
|
||||
|
||||
if (hwif)
|
||||
ide_unregister(hwif->index);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
|
||||
static struct pci_device_id delkin_cb_pci_tbl[] __devinitdata = {
|
||||
{ 0x1145, 0xf021, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ 0x1145, 0xf024, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, delkin_cb_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "Delkin-ASKA-Workbit Cardbus IDE",
|
||||
.id_table = delkin_cb_pci_tbl,
|
||||
.probe = delkin_cb_probe,
|
||||
.remove = delkin_cb_remove,
|
||||
};
|
||||
|
||||
static int
|
||||
delkin_cb_init (void)
|
||||
{
|
||||
return pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
static void
|
||||
delkin_cb_exit (void)
|
||||
{
|
||||
pci_unregister_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(delkin_cb_init);
|
||||
module_exit(delkin_cb_exit);
|
||||
|
||||
MODULE_AUTHOR("Mark Lord");
|
||||
MODULE_DESCRIPTION("Basic support for Delkin/ASKA/Workbit Cardbus IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
273
drivers/ide/pci/generic.c
Normal file
273
drivers/ide/pci/generic.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/generic.c Version 0.11 December 30, 2002
|
||||
*
|
||||
* Copyright (C) 2001-2002 Andre Hedrick <andre@linux-ide.org>
|
||||
* Portions (C) Copyright 2002 Red Hat Inc <alan@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* For the avoidance of doubt the "preferred form" of this code is one which
|
||||
* is in an open non patent encumbered format. Where cryptographic key signing
|
||||
* forms part of the process of creating an executable the information
|
||||
* including keys needed to generate an equivalently functional executable
|
||||
* are deemed to be part of the source code.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.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/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
static int ide_generic_all; /* Set to claim all devices */
|
||||
|
||||
/*
|
||||
* the module_param_named() was added for the modular case
|
||||
* the __setup() is left as compatibility for existing setups
|
||||
*/
|
||||
#ifndef MODULE
|
||||
static int __init ide_generic_all_on(char *unused)
|
||||
{
|
||||
ide_generic_all = 1;
|
||||
printk(KERN_INFO "IDE generic will claim all unknown PCI IDE storage controllers.\n");
|
||||
return 1;
|
||||
}
|
||||
__setup("all-generic-ide", ide_generic_all_on);
|
||||
#endif
|
||||
module_param_named(all_generic_ide, ide_generic_all, bool, 0444);
|
||||
MODULE_PARM_DESC(all_generic_ide, "IDE generic will claim all unknown PCI IDE storage controllers.");
|
||||
|
||||
static void __devinit init_hwif_generic (ide_hwif_t *hwif)
|
||||
{
|
||||
switch(hwif->pci_dev->device) {
|
||||
case PCI_DEVICE_ID_UMC_UM8673F:
|
||||
case PCI_DEVICE_ID_UMC_UM8886A:
|
||||
case PCI_DEVICE_ID_UMC_UM8886BF:
|
||||
hwif->irq = hwif->channel ? 15 : 14;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(hwif->dma_base))
|
||||
return;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x7f;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
hwif->swdma_mask = 0x07;
|
||||
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Logic to add back later on */
|
||||
|
||||
if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
|
||||
ide_pci_device_t *unknown = unknown_chipset;
|
||||
init_setup_unknown(dev, unknown);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
static ide_pci_device_t generic_chipsets[] __devinitdata = {
|
||||
{ /* 0 */
|
||||
.name = "Unknown",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 1 */
|
||||
.name = "NS87410",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.enablebits = {{0x43,0x08,0x08}, {0x47,0x08,0x08}},
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 2 */
|
||||
.name = "SAMURAI",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 3 */
|
||||
.name = "HT6565",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 4 */
|
||||
.name = "UM8673F",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = NODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 5 */
|
||||
.name = "UM8886A",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = NODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 6 */
|
||||
.name = "UM8886BF",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = NODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 7 */
|
||||
.name = "HINT_IDE",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 8 */
|
||||
.name = "VIA_IDE",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = NOAUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 9 */
|
||||
.name = "OPTI621V",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = NOAUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 10 */
|
||||
.name = "VIA8237SATA",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
},{ /* 11 */
|
||||
.name = "Piccolo0102",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = NOAUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 12 */
|
||||
.name = "Piccolo0103",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = NOAUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 13 */
|
||||
.name = "Piccolo0105",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = NOAUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 14 */
|
||||
.name = "Revolution",
|
||||
.init_hwif = init_hwif_generic,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* generic_init_one - called when a PIIX is found
|
||||
* @dev: the generic device
|
||||
* @id: the matching pci id
|
||||
*
|
||||
* Called when the PCI registration layer (or the IDE initialization)
|
||||
* finds a device matching our IDE device tables.
|
||||
*/
|
||||
|
||||
static int __devinit generic_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
ide_pci_device_t *d = &generic_chipsets[id->driver_data];
|
||||
u16 command;
|
||||
int ret = -ENODEV;
|
||||
|
||||
/* Don't use the generic entry unless instructed to do so */
|
||||
if (id->driver_data == 0 && ide_generic_all == 0)
|
||||
goto out;
|
||||
|
||||
if (dev->vendor == PCI_VENDOR_ID_UMC &&
|
||||
dev->device == PCI_DEVICE_ID_UMC_UM8886A &&
|
||||
(!(PCI_FUNC(dev->devfn) & 1)))
|
||||
goto out; /* UM8886A/BF pair */
|
||||
|
||||
if (dev->vendor == PCI_VENDOR_ID_OPTI &&
|
||||
dev->device == PCI_DEVICE_ID_OPTI_82C558 &&
|
||||
(!(PCI_FUNC(dev->devfn) & 1)))
|
||||
goto out;
|
||||
|
||||
if (dev->vendor == PCI_VENDOR_ID_JMICRON) {
|
||||
if (dev->device != PCI_DEVICE_ID_JMICRON_JMB368 && PCI_FUNC(dev->devfn) != 1)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dev->vendor != PCI_VENDOR_ID_JMICRON) {
|
||||
pci_read_config_word(dev, PCI_COMMAND, &command);
|
||||
if (!(command & PCI_COMMAND_IO)) {
|
||||
printk(KERN_INFO "Skipping disabled %s IDE controller.\n", d->name);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
ret = ide_setup_pci_device(dev, d);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct pci_device_id generic_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
|
||||
{ PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2},
|
||||
{ PCI_VENDOR_ID_HOLTEK, PCI_DEVICE_ID_HOLTEK_6565, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3},
|
||||
{ PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8673F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
|
||||
{ PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5},
|
||||
{ PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6},
|
||||
{ PCI_VENDOR_ID_HINT, PCI_DEVICE_ID_HINT_VXPROII_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7},
|
||||
{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8},
|
||||
{ PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9},
|
||||
#ifdef CONFIG_BLK_DEV_IDE_SATA
|
||||
{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10},
|
||||
#endif
|
||||
{ PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11},
|
||||
{ PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12},
|
||||
{ PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13},
|
||||
{ PCI_VENDOR_ID_NETCELL,PCI_DEVICE_ID_REVOLUTION, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 14},
|
||||
/* Must come last. If you add entries adjust this table appropriately and the init_one code */
|
||||
{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE << 8, 0xFFFFFF00UL, 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, generic_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "PCI_IDE",
|
||||
.id_table = generic_pci_tbl,
|
||||
.probe = generic_init_one,
|
||||
};
|
||||
|
||||
static int __init generic_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(generic_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for generic PCI IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
249
drivers/ide/pci/hpt34x.c
Normal file
249
drivers/ide/pci/hpt34x.c
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/hpt34x.c Version 0.40 Sept 10, 2002
|
||||
*
|
||||
* Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*
|
||||
*
|
||||
* 00:12.0 Unknown mass storage controller:
|
||||
* Triones Technologies, Inc.
|
||||
* Unknown device 0003 (rev 01)
|
||||
*
|
||||
* hde: UDMA 2 (0x0000 0x0002) (0x0000 0x0010)
|
||||
* hdf: UDMA 2 (0x0002 0x0012) (0x0010 0x0030)
|
||||
* hde: DMA 2 (0x0000 0x0002) (0x0000 0x0010)
|
||||
* hdf: DMA 2 (0x0002 0x0012) (0x0010 0x0030)
|
||||
* hdg: DMA 1 (0x0012 0x0052) (0x0030 0x0070)
|
||||
* hdh: DMA 1 (0x0052 0x0252) (0x0070 0x00f0)
|
||||
*
|
||||
* ide-pci.c reference
|
||||
*
|
||||
* Since there are two cards that report almost identically,
|
||||
* the only discernable difference is the values reported in pcicmd.
|
||||
* Booting-BIOS card or HPT363 :: pcicmd == 0x07
|
||||
* Non-bootable card or HPT343 :: pcicmd == 0x05
|
||||
*/
|
||||
|
||||
#include <linux/module.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/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#define HPT343_DEBUG_DRIVE_INFO 0
|
||||
|
||||
static u8 hpt34x_ratemask (ide_drive_t *drive)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int hpt34x_tune_chipset (ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
struct pci_dev *dev = HWIF(drive)->pci_dev;
|
||||
u8 speed = ide_rate_filter(hpt34x_ratemask(drive), xferspeed);
|
||||
u32 reg1= 0, tmp1 = 0, reg2 = 0, tmp2 = 0;
|
||||
u8 hi_speed, lo_speed;
|
||||
|
||||
hi_speed = speed >> 4;
|
||||
lo_speed = speed & 0x0f;
|
||||
|
||||
if (hi_speed & 7) {
|
||||
hi_speed = (hi_speed & 4) ? 0x01 : 0x10;
|
||||
} else {
|
||||
lo_speed <<= 5;
|
||||
lo_speed >>= 5;
|
||||
}
|
||||
|
||||
pci_read_config_dword(dev, 0x44, ®1);
|
||||
pci_read_config_dword(dev, 0x48, ®2);
|
||||
tmp1 = ((lo_speed << (3*drive->dn)) | (reg1 & ~(7 << (3*drive->dn))));
|
||||
tmp2 = ((hi_speed << drive->dn) | (reg2 & ~(0x11 << drive->dn)));
|
||||
pci_write_config_dword(dev, 0x44, tmp1);
|
||||
pci_write_config_dword(dev, 0x48, tmp2);
|
||||
|
||||
#if HPT343_DEBUG_DRIVE_INFO
|
||||
printk("%s: %s drive%d (0x%04x 0x%04x) (0x%04x 0x%04x)" \
|
||||
" (0x%02x 0x%02x)\n",
|
||||
drive->name, ide_xfer_verbose(speed),
|
||||
drive->dn, reg1, tmp1, reg2, tmp2,
|
||||
hi_speed, lo_speed);
|
||||
#endif /* HPT343_DEBUG_DRIVE_INFO */
|
||||
|
||||
return(ide_config_drive_speed(drive, speed));
|
||||
}
|
||||
|
||||
static void hpt34x_tune_drive (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
|
||||
(void) hpt34x_tune_chipset(drive, (XFER_PIO_0 + pio));
|
||||
}
|
||||
|
||||
/*
|
||||
* This allows the configuration of ide_pci chipset registers
|
||||
* for cards that learn about the drive's UDMA, DMA, PIO capabilities
|
||||
* after the drive is reported by the OS. Initially for designed for
|
||||
* HPT343 UDMA chipset by HighPoint|Triones Technologies, Inc.
|
||||
*/
|
||||
|
||||
static int config_chipset_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, hpt34x_ratemask(drive));
|
||||
|
||||
if (!(speed))
|
||||
return 0;
|
||||
|
||||
(void) hpt34x_tune_chipset(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
static int hpt34x_config_drive_xfer_rate (ide_drive_t *drive)
|
||||
{
|
||||
drive->init_speed = 0;
|
||||
|
||||
if (ide_use_dma(drive) && config_chipset_for_dma(drive))
|
||||
#ifndef CONFIG_HPT34X_AUTODMA
|
||||
return -1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
if (ide_use_fast_pio(drive))
|
||||
hpt34x_tune_drive(drive, 255);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the BIOS does not set the IO base addaress to XX00, 343 will fail.
|
||||
*/
|
||||
#define HPT34X_PCI_INIT_REG 0x80
|
||||
|
||||
static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev, const char *name)
|
||||
{
|
||||
int i = 0;
|
||||
unsigned long hpt34xIoBase = pci_resource_start(dev, 4);
|
||||
unsigned long hpt_addr[4] = { 0x20, 0x34, 0x28, 0x3c };
|
||||
unsigned long hpt_addr_len[4] = { 7, 3, 7, 3 };
|
||||
u16 cmd;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
pci_write_config_byte(dev, HPT34X_PCI_INIT_REG, 0x00);
|
||||
pci_read_config_word(dev, PCI_COMMAND, &cmd);
|
||||
|
||||
if (cmd & PCI_COMMAND_MEMORY) {
|
||||
if (pci_resource_start(dev, PCI_ROM_RESOURCE)) {
|
||||
pci_write_config_dword(dev, PCI_ROM_ADDRESS,
|
||||
dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
|
||||
printk(KERN_INFO "HPT345: ROM enabled at 0x%08lx\n",
|
||||
(unsigned long)dev->resource[PCI_ROM_RESOURCE].start);
|
||||
}
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xF0);
|
||||
} else {
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
|
||||
}
|
||||
|
||||
/*
|
||||
* Since 20-23 can be assigned and are R/W, we correct them.
|
||||
*/
|
||||
pci_write_config_word(dev, PCI_COMMAND, cmd & ~PCI_COMMAND_IO);
|
||||
for(i=0; i<4; i++) {
|
||||
dev->resource[i].start = (hpt34xIoBase + hpt_addr[i]);
|
||||
dev->resource[i].end = dev->resource[i].start + hpt_addr_len[i];
|
||||
dev->resource[i].flags = IORESOURCE_IO;
|
||||
pci_write_config_dword(dev,
|
||||
(PCI_BASE_ADDRESS_0 + (i * 4)),
|
||||
dev->resource[i].start);
|
||||
}
|
||||
pci_write_config_word(dev, PCI_COMMAND, cmd);
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
return dev->irq;
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_hpt34x(ide_hwif_t *hwif)
|
||||
{
|
||||
u16 pcicmd = 0;
|
||||
|
||||
hwif->autodma = 0;
|
||||
|
||||
hwif->tuneproc = &hpt34x_tune_drive;
|
||||
hwif->speedproc = &hpt34x_tune_chipset;
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].autotune = 1;
|
||||
|
||||
pci_read_config_word(hwif->pci_dev, PCI_COMMAND, &pcicmd);
|
||||
|
||||
if (!hwif->dma_base)
|
||||
return;
|
||||
|
||||
hwif->ultra_mask = 0x07;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
hwif->swdma_mask = 0x07;
|
||||
|
||||
hwif->ide_dma_check = &hpt34x_config_drive_xfer_rate;
|
||||
if (!noautodma)
|
||||
hwif->autodma = (pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
static ide_pci_device_t hpt34x_chipset __devinitdata = {
|
||||
.name = "HPT34X",
|
||||
.init_chipset = init_chipset_hpt34x,
|
||||
.init_hwif = init_hwif_hpt34x,
|
||||
.channels = 2,
|
||||
.autodma = NOAUTODMA,
|
||||
.bootable = NEVER_BOARD,
|
||||
.extra = 16
|
||||
};
|
||||
|
||||
static int __devinit hpt34x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
ide_pci_device_t *d = &hpt34x_chipset;
|
||||
static char *chipset_names[] = {"HPT343", "HPT345"};
|
||||
u16 pcicmd = 0;
|
||||
|
||||
pci_read_config_word(dev, PCI_COMMAND, &pcicmd);
|
||||
|
||||
d->name = chipset_names[(pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0];
|
||||
d->bootable = (pcicmd & PCI_COMMAND_MEMORY) ? OFF_BOARD : NEVER_BOARD;
|
||||
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
static struct pci_device_id hpt34x_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, hpt34x_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "HPT34x_IDE",
|
||||
.id_table = hpt34x_pci_tbl,
|
||||
.probe = hpt34x_init_one,
|
||||
};
|
||||
|
||||
static int __init hpt34x_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(hpt34x_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for Highpoint 34x IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
1674
drivers/ide/pci/hpt366.c
Normal file
1674
drivers/ide/pci/hpt366.c
Normal file
File diff suppressed because it is too large
Load Diff
360
drivers/ide/pci/it8213.c
Normal file
360
drivers/ide/pci/it8213.c
Normal file
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* ITE 8213 IDE driver
|
||||
*
|
||||
* Copyright (C) 2006 Jack Lee
|
||||
* Copyright (C) 2006 Alan Cox
|
||||
* Copyright (C) 2007 Bartlomiej Zolnierkiewicz
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
/*
|
||||
* it8213_ratemask - Compute available modes
|
||||
* @drive: IDE drive
|
||||
*
|
||||
* Compute the available speeds for the devices on the interface. This
|
||||
* is all modes to ATA133 clipped by drive cable setup.
|
||||
*/
|
||||
|
||||
static u8 it8213_ratemask (ide_drive_t *drive)
|
||||
{
|
||||
u8 mode = 4;
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min_t(u8, mode, 1);
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* it8213_dma_2_pio - return the PIO mode matching DMA
|
||||
* @xfer_rate: transfer speed
|
||||
*
|
||||
* Returns the nearest equivalent PIO timing for the PIO or DMA
|
||||
* mode requested by the controller.
|
||||
*/
|
||||
|
||||
static u8 it8213_dma_2_pio (u8 xfer_rate) {
|
||||
switch(xfer_rate) {
|
||||
case XFER_UDMA_6:
|
||||
case XFER_UDMA_5:
|
||||
case XFER_UDMA_4:
|
||||
case XFER_UDMA_3:
|
||||
case XFER_UDMA_2:
|
||||
case XFER_UDMA_1:
|
||||
case XFER_UDMA_0:
|
||||
case XFER_MW_DMA_2:
|
||||
case XFER_PIO_4:
|
||||
return 4;
|
||||
case XFER_MW_DMA_1:
|
||||
case XFER_PIO_3:
|
||||
return 3;
|
||||
case XFER_SW_DMA_2:
|
||||
case XFER_PIO_2:
|
||||
return 2;
|
||||
case XFER_MW_DMA_0:
|
||||
case XFER_SW_DMA_1:
|
||||
case XFER_SW_DMA_0:
|
||||
case XFER_PIO_1:
|
||||
case XFER_PIO_0:
|
||||
case XFER_PIO_SLOW:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* it8213_tuneproc - tune a drive
|
||||
* @drive: drive to tune
|
||||
* @pio: desired PIO mode
|
||||
*
|
||||
* Set the interface PIO mode.
|
||||
*/
|
||||
|
||||
static void it8213_tuneproc (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
int is_slave = drive->dn & 1;
|
||||
int master_port = 0x40;
|
||||
int slave_port = 0x44;
|
||||
unsigned long flags;
|
||||
u16 master_data;
|
||||
u8 slave_data;
|
||||
static DEFINE_SPINLOCK(tune_lock);
|
||||
int control = 0;
|
||||
|
||||
static const u8 timings[][2]= {
|
||||
{ 0, 0 },
|
||||
{ 0, 0 },
|
||||
{ 1, 0 },
|
||||
{ 2, 1 },
|
||||
{ 2, 3 }, };
|
||||
|
||||
pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
|
||||
|
||||
spin_lock_irqsave(&tune_lock, flags);
|
||||
pci_read_config_word(dev, master_port, &master_data);
|
||||
|
||||
if (pio > 1)
|
||||
control |= 1; /* Programmable timing on */
|
||||
if (drive->media != ide_disk)
|
||||
control |= 4; /* ATAPI */
|
||||
if (pio > 2)
|
||||
control |= 2; /* IORDY */
|
||||
if (is_slave) {
|
||||
master_data |= 0x4000;
|
||||
master_data &= ~0x0070;
|
||||
if (pio > 1)
|
||||
master_data = master_data | (control << 4);
|
||||
pci_read_config_byte(dev, slave_port, &slave_data);
|
||||
slave_data = slave_data & 0xf0;
|
||||
slave_data = slave_data | (timings[pio][0] << 2) | timings[pio][1];
|
||||
} else {
|
||||
master_data &= ~0x3307;
|
||||
if (pio > 1)
|
||||
master_data = master_data | control;
|
||||
master_data = master_data | (timings[pio][0] << 12) | (timings[pio][1] << 8);
|
||||
}
|
||||
pci_write_config_word(dev, master_port, master_data);
|
||||
if (is_slave)
|
||||
pci_write_config_byte(dev, slave_port, slave_data);
|
||||
spin_unlock_irqrestore(&tune_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* it8213_tune_chipset - set controller timings
|
||||
* @drive: Drive to set up
|
||||
* @xferspeed: speed we want to achieve
|
||||
*
|
||||
* Tune the ITE chipset for the desired mode. If we can't achieve
|
||||
* the desired mode then tune for a lower one, but ultimately
|
||||
* make the thing work.
|
||||
*/
|
||||
|
||||
static int it8213_tune_chipset (ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u8 maslave = 0x40;
|
||||
u8 speed = ide_rate_filter(it8213_ratemask(drive), xferspeed);
|
||||
int a_speed = 3 << (drive->dn * 4);
|
||||
int u_flag = 1 << drive->dn;
|
||||
int v_flag = 0x01 << drive->dn;
|
||||
int w_flag = 0x10 << drive->dn;
|
||||
int u_speed = 0;
|
||||
u16 reg4042, reg4a;
|
||||
u8 reg48, reg54, reg55;
|
||||
|
||||
pci_read_config_word(dev, maslave, ®4042);
|
||||
pci_read_config_byte(dev, 0x48, ®48);
|
||||
pci_read_config_word(dev, 0x4a, ®4a);
|
||||
pci_read_config_byte(dev, 0x54, ®54);
|
||||
pci_read_config_byte(dev, 0x55, ®55);
|
||||
|
||||
switch(speed) {
|
||||
case XFER_UDMA_6:
|
||||
case XFER_UDMA_4:
|
||||
case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break;
|
||||
case XFER_UDMA_5:
|
||||
case XFER_UDMA_3:
|
||||
case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break;
|
||||
case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break;
|
||||
break;
|
||||
case XFER_MW_DMA_2:
|
||||
case XFER_MW_DMA_1:
|
||||
case XFER_SW_DMA_2:
|
||||
break;
|
||||
case XFER_PIO_4:
|
||||
case XFER_PIO_3:
|
||||
case XFER_PIO_2:
|
||||
case XFER_PIO_1:
|
||||
case XFER_PIO_0:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (speed >= XFER_UDMA_0) {
|
||||
if (!(reg48 & u_flag))
|
||||
pci_write_config_byte(dev, 0x48, reg48 | u_flag);
|
||||
if (speed >= XFER_UDMA_5) {
|
||||
pci_write_config_byte(dev, 0x55, (u8) reg55|w_flag);
|
||||
} else {
|
||||
pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
|
||||
}
|
||||
|
||||
if ((reg4a & a_speed) != u_speed)
|
||||
pci_write_config_word(dev, 0x4a, (reg4a & ~a_speed) | u_speed);
|
||||
if (speed > XFER_UDMA_2) {
|
||||
if (!(reg54 & v_flag))
|
||||
pci_write_config_byte(dev, 0x54, reg54 | v_flag);
|
||||
} else
|
||||
pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
|
||||
} else {
|
||||
if (reg48 & u_flag)
|
||||
pci_write_config_byte(dev, 0x48, reg48 & ~u_flag);
|
||||
if (reg4a & a_speed)
|
||||
pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
|
||||
if (reg54 & v_flag)
|
||||
pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
|
||||
if (reg55 & w_flag)
|
||||
pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
|
||||
}
|
||||
it8213_tuneproc(drive, it8213_dma_2_pio(speed));
|
||||
return ide_config_drive_speed(drive, speed);
|
||||
}
|
||||
|
||||
/*
|
||||
* config_chipset_for_dma - configure for DMA
|
||||
* @drive: drive to configure
|
||||
*
|
||||
* Called by the IDE layer when it wants the timings set up.
|
||||
*/
|
||||
|
||||
static int config_chipset_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, it8213_ratemask(drive));
|
||||
|
||||
if (!speed)
|
||||
return 0;
|
||||
|
||||
it8213_tune_chipset(drive, speed);
|
||||
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
/**
|
||||
* it8213_configure_drive_for_dma - set up for DMA transfers
|
||||
* @drive: drive we are going to set up
|
||||
*
|
||||
* Set up the drive for DMA, tune the controller and drive as
|
||||
* required. If the drive isn't suitable for DMA or we hit
|
||||
* other problems then we will drop down to PIO and set up
|
||||
* PIO appropriately
|
||||
*/
|
||||
|
||||
static int it8213_config_drive_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
u8 pio;
|
||||
|
||||
if (ide_use_dma(drive) && config_chipset_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
pio = ide_get_best_pio_mode(drive, 255, 4, NULL);
|
||||
it8213_tune_chipset(drive, XFER_PIO_0 + pio);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_hwif_it8213 - set up hwif structs
|
||||
* @hwif: interface to set up
|
||||
*
|
||||
* We do the basic set up of the interface structure. The IT8212
|
||||
* requires several custom handlers so we override the default
|
||||
* ide DMA handlers appropriately
|
||||
*/
|
||||
|
||||
static void __devinit init_hwif_it8213(ide_hwif_t *hwif)
|
||||
{
|
||||
u8 reg42h = 0, ata66 = 0;
|
||||
|
||||
hwif->speedproc = &it8213_tune_chipset;
|
||||
hwif->tuneproc = &it8213_tuneproc;
|
||||
|
||||
hwif->autodma = 0;
|
||||
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].autotune = 1;
|
||||
|
||||
if (!hwif->dma_base)
|
||||
return;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x7f;
|
||||
hwif->mwdma_mask = 0x06;
|
||||
hwif->swdma_mask = 0x04;
|
||||
|
||||
pci_read_config_byte(hwif->pci_dev, 0x42, ®42h);
|
||||
ata66 = (reg42h & 0x02) ? 0 : 1;
|
||||
|
||||
hwif->ide_dma_check = &it8213_config_drive_for_dma;
|
||||
if (!(hwif->udma_four))
|
||||
hwif->udma_four = ata66;
|
||||
|
||||
/*
|
||||
* The BIOS often doesn't set up DMA on this controller
|
||||
* so we always do it.
|
||||
*/
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
|
||||
#define DECLARE_ITE_DEV(name_str) \
|
||||
{ \
|
||||
.name = name_str, \
|
||||
.init_hwif = init_hwif_it8213, \
|
||||
.channels = 1, \
|
||||
.autodma = AUTODMA, \
|
||||
.enablebits = {{0x41,0x80,0x80}}, \
|
||||
.bootable = ON_BOARD, \
|
||||
}
|
||||
|
||||
static ide_pci_device_t it8213_chipsets[] __devinitdata = {
|
||||
/* 0 */ DECLARE_ITE_DEV("IT8213"),
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* it8213_init_one - pci layer discovery entry
|
||||
* @dev: PCI device
|
||||
* @id: ident table entry
|
||||
*
|
||||
* Called by the PCI code when it finds an ITE8213 controller. As
|
||||
* this device follows the standard interfaces we can use the
|
||||
* standard helper functions to do almost all the work for us.
|
||||
*/
|
||||
|
||||
static int __devinit it8213_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
ide_setup_pci_device(dev, &it8213_chipsets[id->driver_data]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct pci_device_id it8213_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_ITE, 0x8213, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, it8213_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "ITE8213_IDE",
|
||||
.id_table = it8213_pci_tbl,
|
||||
.probe = it8213_init_one,
|
||||
};
|
||||
|
||||
static int __init it8213_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(it8213_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Jack Lee, Alan Cox");
|
||||
MODULE_DESCRIPTION("PCI driver module for the ITE 8213");
|
||||
MODULE_LICENSE("GPL");
|
||||
815
drivers/ide/pci/it821x.c
Normal file
815
drivers/ide/pci/it821x.c
Normal file
@@ -0,0 +1,815 @@
|
||||
|
||||
/*
|
||||
* linux/drivers/ide/pci/it821x.c Version 0.09 December 2004
|
||||
*
|
||||
* Copyright (C) 2004 Red Hat <alan@redhat.com>
|
||||
*
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
* Based in part on the ITE vendor provided SCSI driver.
|
||||
*
|
||||
* Documentation available from
|
||||
* http://www.ite.com.tw/pc/IT8212F_V04.pdf
|
||||
* Some other documents are NDA.
|
||||
*
|
||||
* The ITE8212 isn't exactly a standard IDE controller. It has two
|
||||
* modes. In pass through mode then it is an IDE controller. In its smart
|
||||
* mode its actually quite a capable hardware raid controller disguised
|
||||
* as an IDE controller. Smart mode only understands DMA read/write and
|
||||
* identify, none of the fancier commands apply. The IT8211 is identical
|
||||
* in other respects but lacks the raid mode.
|
||||
*
|
||||
* Errata:
|
||||
* o Rev 0x10 also requires master/slave hold the same DMA timings and
|
||||
* cannot do ATAPI MWDMA.
|
||||
* o The identify data for raid volumes lacks CHS info (technically ok)
|
||||
* but also fails to set the LBA28 and other bits. We fix these in
|
||||
* the IDE probe quirk code.
|
||||
* o If you write LBA48 sized I/O's (ie > 256 sector) in smart mode
|
||||
* raid then the controller firmware dies
|
||||
* o Smart mode without RAID doesn't clear all the necessary identify
|
||||
* bits to reduce the command set to the one used
|
||||
*
|
||||
* This has a few impacts on the driver
|
||||
* - In pass through mode we do all the work you would expect
|
||||
* - In smart mode the clocking set up is done by the controller generally
|
||||
* but we must watch the other limits and filter.
|
||||
* - There are a few extra vendor commands that actually talk to the
|
||||
* controller but only work PIO with no IRQ.
|
||||
*
|
||||
* Vendor areas of the identify block in smart mode are used for the
|
||||
* timing and policy set up. Each HDD in raid mode also has a serial
|
||||
* block on the disk. The hardware extra commands are get/set chip status,
|
||||
* rebuild, get rebuild status.
|
||||
*
|
||||
* In Linux the driver supports pass through mode as if the device was
|
||||
* just another IDE controller. If the smart mode is running then
|
||||
* volumes are managed by the controller firmware and each IDE "disk"
|
||||
* is a raid volume. Even more cute - the controller can do automated
|
||||
* hotplug and rebuild.
|
||||
*
|
||||
* The pass through controller itself is a little demented. It has a
|
||||
* flaw that it has a single set of PIO/MWDMA timings per channel so
|
||||
* non UDMA devices restrict each others performance. It also has a
|
||||
* single clock source per channel so mixed UDMA100/133 performance
|
||||
* isn't perfect and we have to pick a clock. Thankfully none of this
|
||||
* matters in smart mode. ATAPI DMA is not currently supported.
|
||||
*
|
||||
* It seems the smart mode is a win for RAID1/RAID10 but otherwise not.
|
||||
*
|
||||
* TODO
|
||||
* - ATAPI UDMA is ok but not MWDMA it seems
|
||||
* - RAID configuration ioctls
|
||||
* - Move to libata once it grows up
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
struct it821x_dev
|
||||
{
|
||||
unsigned int smart:1, /* Are we in smart raid mode */
|
||||
timing10:1; /* Rev 0x10 */
|
||||
u8 clock_mode; /* 0, ATA_50 or ATA_66 */
|
||||
u8 want[2][2]; /* Mode/Pri log for master slave */
|
||||
/* We need these for switching the clock when DMA goes on/off
|
||||
The high byte is the 66Mhz timing */
|
||||
u16 pio[2]; /* Cached PIO values */
|
||||
u16 mwdma[2]; /* Cached MWDMA values */
|
||||
u16 udma[2]; /* Cached UDMA values (per drive) */
|
||||
};
|
||||
|
||||
#define ATA_66 0
|
||||
#define ATA_50 1
|
||||
#define ATA_ANY 2
|
||||
|
||||
#define UDMA_OFF 0
|
||||
#define MWDMA_OFF 0
|
||||
|
||||
/*
|
||||
* We allow users to force the card into non raid mode without
|
||||
* flashing the alternative BIOS. This is also neccessary right now
|
||||
* for embedded platforms that cannot run a PC BIOS but are using this
|
||||
* device.
|
||||
*/
|
||||
|
||||
static int it8212_noraid;
|
||||
|
||||
/**
|
||||
* it821x_program - program the PIO/MWDMA registers
|
||||
* @drive: drive to tune
|
||||
*
|
||||
* Program the PIO/MWDMA timing for this channel according to the
|
||||
* current clock.
|
||||
*/
|
||||
|
||||
static void it821x_program(ide_drive_t *drive, u16 timing)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
int channel = hwif->channel;
|
||||
u8 conf;
|
||||
|
||||
/* Program PIO/MWDMA timing bits */
|
||||
if(itdev->clock_mode == ATA_66)
|
||||
conf = timing >> 8;
|
||||
else
|
||||
conf = timing & 0xFF;
|
||||
pci_write_config_byte(hwif->pci_dev, 0x54 + 4 * channel, conf);
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_program_udma - program the UDMA registers
|
||||
* @drive: drive to tune
|
||||
*
|
||||
* Program the UDMA timing for this drive according to the
|
||||
* current clock.
|
||||
*/
|
||||
|
||||
static void it821x_program_udma(ide_drive_t *drive, u16 timing)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
int channel = hwif->channel;
|
||||
int unit = drive->select.b.unit;
|
||||
u8 conf;
|
||||
|
||||
/* Program UDMA timing bits */
|
||||
if(itdev->clock_mode == ATA_66)
|
||||
conf = timing >> 8;
|
||||
else
|
||||
conf = timing & 0xFF;
|
||||
if(itdev->timing10 == 0)
|
||||
pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel + unit, conf);
|
||||
else {
|
||||
pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel, conf);
|
||||
pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel + 1, conf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* it821x_clock_strategy
|
||||
* @hwif: hardware interface
|
||||
*
|
||||
* Select between the 50 and 66Mhz base clocks to get the best
|
||||
* results for this interface.
|
||||
*/
|
||||
|
||||
static void it821x_clock_strategy(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
|
||||
u8 unit = drive->select.b.unit;
|
||||
ide_drive_t *pair = &hwif->drives[1-unit];
|
||||
|
||||
int clock, altclock;
|
||||
u8 v;
|
||||
int sel = 0;
|
||||
|
||||
if(itdev->want[0][0] > itdev->want[1][0]) {
|
||||
clock = itdev->want[0][1];
|
||||
altclock = itdev->want[1][1];
|
||||
} else {
|
||||
clock = itdev->want[1][1];
|
||||
altclock = itdev->want[0][1];
|
||||
}
|
||||
|
||||
/* Master doesn't care does the slave ? */
|
||||
if(clock == ATA_ANY)
|
||||
clock = altclock;
|
||||
|
||||
/* Nobody cares - keep the same clock */
|
||||
if(clock == ATA_ANY)
|
||||
return;
|
||||
/* No change */
|
||||
if(clock == itdev->clock_mode)
|
||||
return;
|
||||
|
||||
/* Load this into the controller ? */
|
||||
if(clock == ATA_66)
|
||||
itdev->clock_mode = ATA_66;
|
||||
else {
|
||||
itdev->clock_mode = ATA_50;
|
||||
sel = 1;
|
||||
}
|
||||
pci_read_config_byte(hwif->pci_dev, 0x50, &v);
|
||||
v &= ~(1 << (1 + hwif->channel));
|
||||
v |= sel << (1 + hwif->channel);
|
||||
pci_write_config_byte(hwif->pci_dev, 0x50, v);
|
||||
|
||||
/*
|
||||
* Reprogram the UDMA/PIO of the pair drive for the switch
|
||||
* MWDMA will be dealt with by the dma switcher
|
||||
*/
|
||||
if(pair && itdev->udma[1-unit] != UDMA_OFF) {
|
||||
it821x_program_udma(pair, itdev->udma[1-unit]);
|
||||
it821x_program(pair, itdev->pio[1-unit]);
|
||||
}
|
||||
/*
|
||||
* Reprogram the UDMA/PIO of our drive for the switch.
|
||||
* MWDMA will be dealt with by the dma switcher
|
||||
*/
|
||||
if(itdev->udma[unit] != UDMA_OFF) {
|
||||
it821x_program_udma(drive, itdev->udma[unit]);
|
||||
it821x_program(drive, itdev->pio[unit]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_ratemask - Compute available modes
|
||||
* @drive: IDE drive
|
||||
*
|
||||
* Compute the available speeds for the devices on the interface. This
|
||||
* is all modes to ATA133 clipped by drive cable setup.
|
||||
*/
|
||||
|
||||
static u8 it821x_ratemask (ide_drive_t *drive)
|
||||
{
|
||||
u8 mode = 4;
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min(mode, (u8)1);
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_tuneproc - tune a drive
|
||||
* @drive: drive to tune
|
||||
* @mode_wanted: the target operating mode
|
||||
*
|
||||
* Load the timing settings for this device mode into the
|
||||
* controller. By the time we are called the mode has been
|
||||
* modified as neccessary to handle the absence of seperate
|
||||
* master/slave timers for MWDMA/PIO.
|
||||
*
|
||||
* This code is only used in pass through mode.
|
||||
*/
|
||||
|
||||
static void it821x_tuneproc (ide_drive_t *drive, byte mode_wanted)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
int unit = drive->select.b.unit;
|
||||
|
||||
/* Spec says 89 ref driver uses 88 */
|
||||
static u16 pio[] = { 0xAA88, 0xA382, 0xA181, 0x3332, 0x3121 };
|
||||
static u8 pio_want[] = { ATA_66, ATA_66, ATA_66, ATA_66, ATA_ANY };
|
||||
|
||||
if(itdev->smart)
|
||||
return;
|
||||
|
||||
/* We prefer 66Mhz clock for PIO 0-3, don't care for PIO4 */
|
||||
itdev->want[unit][1] = pio_want[mode_wanted];
|
||||
itdev->want[unit][0] = 1; /* PIO is lowest priority */
|
||||
itdev->pio[unit] = pio[mode_wanted];
|
||||
it821x_clock_strategy(drive);
|
||||
it821x_program(drive, itdev->pio[unit]);
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_tune_mwdma - tune a channel for MWDMA
|
||||
* @drive: drive to set up
|
||||
* @mode_wanted: the target operating mode
|
||||
*
|
||||
* Load the timing settings for this device mode into the
|
||||
* controller when doing MWDMA in pass through mode. The caller
|
||||
* must manage the whole lack of per device MWDMA/PIO timings and
|
||||
* the shared MWDMA/PIO timing register.
|
||||
*/
|
||||
|
||||
static void it821x_tune_mwdma (ide_drive_t *drive, byte mode_wanted)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct it821x_dev *itdev = (void *)ide_get_hwifdata(hwif);
|
||||
int unit = drive->select.b.unit;
|
||||
int channel = hwif->channel;
|
||||
u8 conf;
|
||||
|
||||
static u16 dma[] = { 0x8866, 0x3222, 0x3121 };
|
||||
static u8 mwdma_want[] = { ATA_ANY, ATA_66, ATA_ANY };
|
||||
|
||||
itdev->want[unit][1] = mwdma_want[mode_wanted];
|
||||
itdev->want[unit][0] = 2; /* MWDMA is low priority */
|
||||
itdev->mwdma[unit] = dma[mode_wanted];
|
||||
itdev->udma[unit] = UDMA_OFF;
|
||||
|
||||
/* UDMA bits off - Revision 0x10 do them in pairs */
|
||||
pci_read_config_byte(hwif->pci_dev, 0x50, &conf);
|
||||
if(itdev->timing10)
|
||||
conf |= channel ? 0x60: 0x18;
|
||||
else
|
||||
conf |= 1 << (3 + 2 * channel + unit);
|
||||
pci_write_config_byte(hwif->pci_dev, 0x50, conf);
|
||||
|
||||
it821x_clock_strategy(drive);
|
||||
/* FIXME: do we need to program this ? */
|
||||
/* it821x_program(drive, itdev->mwdma[unit]); */
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_tune_udma - tune a channel for UDMA
|
||||
* @drive: drive to set up
|
||||
* @mode_wanted: the target operating mode
|
||||
*
|
||||
* Load the timing settings for this device mode into the
|
||||
* controller when doing UDMA modes in pass through.
|
||||
*/
|
||||
|
||||
static void it821x_tune_udma (ide_drive_t *drive, byte mode_wanted)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
int unit = drive->select.b.unit;
|
||||
int channel = hwif->channel;
|
||||
u8 conf;
|
||||
|
||||
static u16 udma[] = { 0x4433, 0x4231, 0x3121, 0x2121, 0x1111, 0x2211, 0x1111 };
|
||||
static u8 udma_want[] = { ATA_ANY, ATA_50, ATA_ANY, ATA_66, ATA_66, ATA_50, ATA_66 };
|
||||
|
||||
itdev->want[unit][1] = udma_want[mode_wanted];
|
||||
itdev->want[unit][0] = 3; /* UDMA is high priority */
|
||||
itdev->mwdma[unit] = MWDMA_OFF;
|
||||
itdev->udma[unit] = udma[mode_wanted];
|
||||
if(mode_wanted >= 5)
|
||||
itdev->udma[unit] |= 0x8080; /* UDMA 5/6 select on */
|
||||
|
||||
/* UDMA on. Again revision 0x10 must do the pair */
|
||||
pci_read_config_byte(hwif->pci_dev, 0x50, &conf);
|
||||
if(itdev->timing10)
|
||||
conf &= channel ? 0x9F: 0xE7;
|
||||
else
|
||||
conf &= ~ (1 << (3 + 2 * channel + unit));
|
||||
pci_write_config_byte(hwif->pci_dev, 0x50, conf);
|
||||
|
||||
it821x_clock_strategy(drive);
|
||||
it821x_program_udma(drive, itdev->udma[unit]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* config_it821x_chipset_for_pio - set drive timings
|
||||
* @drive: drive to tune
|
||||
* @speed we want
|
||||
*
|
||||
* Compute the best pio mode we can for a given device. We must
|
||||
* pick a speed that does not cause problems with the other device
|
||||
* on the cable.
|
||||
*/
|
||||
|
||||
static void config_it821x_chipset_for_pio (ide_drive_t *drive, byte set_speed)
|
||||
{
|
||||
u8 unit = drive->select.b.unit;
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
ide_drive_t *pair = &hwif->drives[1-unit];
|
||||
u8 speed = 0, set_pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
|
||||
u8 pair_pio;
|
||||
|
||||
/* We have to deal with this mess in pairs */
|
||||
if(pair != NULL) {
|
||||
pair_pio = ide_get_best_pio_mode(pair, 255, 5, NULL);
|
||||
/* Trim PIO to the slowest of the master/slave */
|
||||
if(pair_pio < set_pio)
|
||||
set_pio = pair_pio;
|
||||
}
|
||||
it821x_tuneproc(drive, set_pio);
|
||||
speed = XFER_PIO_0 + set_pio;
|
||||
/* XXX - We trim to the lowest of the pair so the other drive
|
||||
will always be fine at this point until we do hotplug passthru */
|
||||
|
||||
if (set_speed)
|
||||
(void) ide_config_drive_speed(drive, speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_dma_read - DMA hook
|
||||
* @drive: drive for DMA
|
||||
*
|
||||
* The IT821x has a single timing register for MWDMA and for PIO
|
||||
* operations. As we flip back and forth we have to reload the
|
||||
* clock. In addition the rev 0x10 device only works if the same
|
||||
* timing value is loaded into the master and slave UDMA clock
|
||||
* so we must also reload that.
|
||||
*
|
||||
* FIXME: we could figure out in advance if we need to do reloads
|
||||
*/
|
||||
|
||||
static void it821x_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
int unit = drive->select.b.unit;
|
||||
if(itdev->mwdma[unit] != MWDMA_OFF)
|
||||
it821x_program(drive, itdev->mwdma[unit]);
|
||||
else if(itdev->udma[unit] != UDMA_OFF && itdev->timing10)
|
||||
it821x_program_udma(drive, itdev->udma[unit]);
|
||||
ide_dma_start(drive);
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_dma_write - DMA hook
|
||||
* @drive: drive for DMA stop
|
||||
*
|
||||
* The IT821x has a single timing register for MWDMA and for PIO
|
||||
* operations. As we flip back and forth we have to reload the
|
||||
* clock.
|
||||
*/
|
||||
|
||||
static int it821x_dma_end(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
int unit = drive->select.b.unit;
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
int ret = __ide_dma_end(drive);
|
||||
if(itdev->mwdma[unit] != MWDMA_OFF)
|
||||
it821x_program(drive, itdev->pio[unit]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* it821x_tune_chipset - set controller timings
|
||||
* @drive: Drive to set up
|
||||
* @xferspeed: speed we want to achieve
|
||||
*
|
||||
* Tune the ITE chipset for the desired mode. If we can't achieve
|
||||
* the desired mode then tune for a lower one, but ultimately
|
||||
* make the thing work.
|
||||
*/
|
||||
|
||||
static int it821x_tune_chipset (ide_drive_t *drive, byte xferspeed)
|
||||
{
|
||||
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
u8 speed = ide_rate_filter(it821x_ratemask(drive), xferspeed);
|
||||
|
||||
if(!itdev->smart) {
|
||||
switch(speed) {
|
||||
case XFER_PIO_4:
|
||||
case XFER_PIO_3:
|
||||
case XFER_PIO_2:
|
||||
case XFER_PIO_1:
|
||||
case XFER_PIO_0:
|
||||
it821x_tuneproc(drive, (speed - XFER_PIO_0));
|
||||
break;
|
||||
/* MWDMA tuning is really hard because our MWDMA and PIO
|
||||
timings are kept in the same place. We can switch in the
|
||||
host dma on/off callbacks */
|
||||
case XFER_MW_DMA_2:
|
||||
case XFER_MW_DMA_1:
|
||||
case XFER_MW_DMA_0:
|
||||
it821x_tune_mwdma(drive, (speed - XFER_MW_DMA_0));
|
||||
break;
|
||||
case XFER_UDMA_6:
|
||||
case XFER_UDMA_5:
|
||||
case XFER_UDMA_4:
|
||||
case XFER_UDMA_3:
|
||||
case XFER_UDMA_2:
|
||||
case XFER_UDMA_1:
|
||||
case XFER_UDMA_0:
|
||||
it821x_tune_udma(drive, (speed - XFER_UDMA_0));
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* In smart mode the clocking is done by the host controller
|
||||
* snooping the mode we picked. The rest of it is not our problem
|
||||
*/
|
||||
return ide_config_drive_speed(drive, speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* config_chipset_for_dma - configure for DMA
|
||||
* @drive: drive to configure
|
||||
*
|
||||
* Called by the IDE layer when it wants the timings set up.
|
||||
*/
|
||||
|
||||
static int config_chipset_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, it821x_ratemask(drive));
|
||||
|
||||
if (speed) {
|
||||
config_it821x_chipset_for_pio(drive, 0);
|
||||
it821x_tune_chipset(drive, speed);
|
||||
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_configure_drive_for_dma - set up for DMA transfers
|
||||
* @drive: drive we are going to set up
|
||||
*
|
||||
* Set up the drive for DMA, tune the controller and drive as
|
||||
* required. If the drive isn't suitable for DMA or we hit
|
||||
* other problems then we will drop down to PIO and set up
|
||||
* PIO appropriately
|
||||
*/
|
||||
|
||||
static int it821x_config_drive_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
if (ide_use_dma(drive) && config_chipset_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
config_it821x_chipset_for_pio(drive, 1);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ata66_it821x - check for 80 pin cable
|
||||
* @hwif: interface to check
|
||||
*
|
||||
* Check for the presence of an ATA66 capable cable on the
|
||||
* interface. Problematic as it seems some cards don't have
|
||||
* the needed logic onboard.
|
||||
*/
|
||||
|
||||
static unsigned int __devinit ata66_it821x(ide_hwif_t *hwif)
|
||||
{
|
||||
/* The reference driver also only does disk side */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_fixup - post init callback
|
||||
* @hwif: interface
|
||||
*
|
||||
* This callback is run after the drives have been probed but
|
||||
* before anything gets attached. It allows drivers to do any
|
||||
* final tuning that is needed, or fixups to work around bugs.
|
||||
*/
|
||||
|
||||
static void __devinit it821x_fixups(ide_hwif_t *hwif)
|
||||
{
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
int i;
|
||||
|
||||
if(!itdev->smart) {
|
||||
/*
|
||||
* If we are in pass through mode then not much
|
||||
* needs to be done, but we do bother to clear the
|
||||
* IRQ mask as we may well be in PIO (eg rev 0x10)
|
||||
* for now and we know unmasking is safe on this chipset.
|
||||
*/
|
||||
for (i = 0; i < 2; i++) {
|
||||
ide_drive_t *drive = &hwif->drives[i];
|
||||
if(drive->present)
|
||||
drive->unmask = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Perform fixups on smart mode. We need to "lose" some
|
||||
* capabilities the firmware lacks but does not filter, and
|
||||
* also patch up some capability bits that it forgets to set
|
||||
* in RAID mode.
|
||||
*/
|
||||
|
||||
for(i = 0; i < 2; i++) {
|
||||
ide_drive_t *drive = &hwif->drives[i];
|
||||
struct hd_driveid *id;
|
||||
u16 *idbits;
|
||||
|
||||
if(!drive->present)
|
||||
continue;
|
||||
id = drive->id;
|
||||
idbits = (u16 *)drive->id;
|
||||
|
||||
/* Check for RAID v native */
|
||||
if(strstr(id->model, "Integrated Technology Express")) {
|
||||
/* In raid mode the ident block is slightly buggy
|
||||
We need to set the bits so that the IDE layer knows
|
||||
LBA28. LBA48 and DMA ar valid */
|
||||
id->capability |= 3; /* LBA28, DMA */
|
||||
id->command_set_2 |= 0x0400; /* LBA48 valid */
|
||||
id->cfs_enable_2 |= 0x0400; /* LBA48 on */
|
||||
/* Reporting logic */
|
||||
printk(KERN_INFO "%s: IT8212 %sRAID %d volume",
|
||||
drive->name,
|
||||
idbits[147] ? "Bootable ":"",
|
||||
idbits[129]);
|
||||
if(idbits[129] != 1)
|
||||
printk("(%dK stripe)", idbits[146]);
|
||||
printk(".\n");
|
||||
/* Now the core code will have wrongly decided no DMA
|
||||
so we need to fix this */
|
||||
hwif->dma_off_quietly(drive);
|
||||
#ifdef CONFIG_IDEDMA_ONLYDISK
|
||||
if (drive->media == ide_disk)
|
||||
#endif
|
||||
ide_set_dma(drive);
|
||||
} else {
|
||||
/* Non RAID volume. Fixups to stop the core code
|
||||
doing unsupported things */
|
||||
id->field_valid &= 1;
|
||||
id->queue_depth = 0;
|
||||
id->command_set_1 = 0;
|
||||
id->command_set_2 &= 0xC400;
|
||||
id->cfsse &= 0xC000;
|
||||
id->cfs_enable_1 = 0;
|
||||
id->cfs_enable_2 &= 0xC400;
|
||||
id->csf_default &= 0xC000;
|
||||
id->word127 = 0;
|
||||
id->dlf = 0;
|
||||
id->csfo = 0;
|
||||
id->cfa_power = 0;
|
||||
printk(KERN_INFO "%s: Performing identify fixups.\n",
|
||||
drive->name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* init_hwif_it821x - set up hwif structs
|
||||
* @hwif: interface to set up
|
||||
*
|
||||
* We do the basic set up of the interface structure. The IT8212
|
||||
* requires several custom handlers so we override the default
|
||||
* ide DMA handlers appropriately
|
||||
*/
|
||||
|
||||
static void __devinit init_hwif_it821x(ide_hwif_t *hwif)
|
||||
{
|
||||
struct it821x_dev *idev = kzalloc(sizeof(struct it821x_dev), GFP_KERNEL);
|
||||
u8 conf;
|
||||
|
||||
if(idev == NULL) {
|
||||
printk(KERN_ERR "it821x: out of memory, falling back to legacy behaviour.\n");
|
||||
goto fallback;
|
||||
}
|
||||
ide_set_hwifdata(hwif, idev);
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
|
||||
pci_read_config_byte(hwif->pci_dev, 0x50, &conf);
|
||||
if(conf & 1) {
|
||||
idev->smart = 1;
|
||||
hwif->atapi_dma = 0;
|
||||
/* Long I/O's although allowed in LBA48 space cause the
|
||||
onboard firmware to enter the twighlight zone */
|
||||
hwif->rqsize = 256;
|
||||
}
|
||||
|
||||
/* Pull the current clocks from 0x50 also */
|
||||
if (conf & (1 << (1 + hwif->channel)))
|
||||
idev->clock_mode = ATA_50;
|
||||
else
|
||||
idev->clock_mode = ATA_66;
|
||||
|
||||
idev->want[0][1] = ATA_ANY;
|
||||
idev->want[1][1] = ATA_ANY;
|
||||
|
||||
/*
|
||||
* Not in the docs but according to the reference driver
|
||||
* this is neccessary.
|
||||
*/
|
||||
|
||||
pci_read_config_byte(hwif->pci_dev, 0x08, &conf);
|
||||
if(conf == 0x10) {
|
||||
idev->timing10 = 1;
|
||||
hwif->atapi_dma = 0;
|
||||
if(!idev->smart)
|
||||
printk(KERN_WARNING "it821x: Revision 0x10, workarounds activated.\n");
|
||||
}
|
||||
|
||||
hwif->speedproc = &it821x_tune_chipset;
|
||||
hwif->tuneproc = &it821x_tuneproc;
|
||||
|
||||
/* MWDMA/PIO clock switching for pass through mode */
|
||||
if(!idev->smart) {
|
||||
hwif->dma_start = &it821x_dma_start;
|
||||
hwif->ide_dma_end = &it821x_dma_end;
|
||||
}
|
||||
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].autotune = 1;
|
||||
|
||||
if (!hwif->dma_base)
|
||||
goto fallback;
|
||||
|
||||
hwif->ultra_mask = 0x7f;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
hwif->swdma_mask = 0x07;
|
||||
|
||||
hwif->ide_dma_check = &it821x_config_drive_for_dma;
|
||||
if (!(hwif->udma_four))
|
||||
hwif->udma_four = ata66_it821x(hwif);
|
||||
|
||||
/*
|
||||
* The BIOS often doesn't set up DMA on this controller
|
||||
* so we always do it.
|
||||
*/
|
||||
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
return;
|
||||
fallback:
|
||||
hwif->autodma = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
static void __devinit it8212_disable_raid(struct pci_dev *dev)
|
||||
{
|
||||
/* Reset local CPU, and set BIOS not ready */
|
||||
pci_write_config_byte(dev, 0x5E, 0x01);
|
||||
|
||||
/* Set to bypass mode, and reset PCI bus */
|
||||
pci_write_config_byte(dev, 0x50, 0x00);
|
||||
pci_write_config_word(dev, PCI_COMMAND,
|
||||
PCI_COMMAND_PARITY | PCI_COMMAND_IO |
|
||||
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||
pci_write_config_word(dev, 0x40, 0xA0F3);
|
||||
|
||||
pci_write_config_dword(dev,0x4C, 0x02040204);
|
||||
pci_write_config_byte(dev, 0x42, 0x36);
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
|
||||
}
|
||||
|
||||
static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev, const char *name)
|
||||
{
|
||||
u8 conf;
|
||||
static char *mode[2] = { "pass through", "smart" };
|
||||
|
||||
/* Force the card into bypass mode if so requested */
|
||||
if (it8212_noraid) {
|
||||
printk(KERN_INFO "it8212: forcing bypass mode.\n");
|
||||
it8212_disable_raid(dev);
|
||||
}
|
||||
pci_read_config_byte(dev, 0x50, &conf);
|
||||
printk(KERN_INFO "it821x: controller in %s mode.\n", mode[conf & 1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define DECLARE_ITE_DEV(name_str) \
|
||||
{ \
|
||||
.name = name_str, \
|
||||
.init_chipset = init_chipset_it821x, \
|
||||
.init_hwif = init_hwif_it821x, \
|
||||
.channels = 2, \
|
||||
.autodma = AUTODMA, \
|
||||
.bootable = ON_BOARD, \
|
||||
.fixup = it821x_fixups \
|
||||
}
|
||||
|
||||
static ide_pci_device_t it821x_chipsets[] __devinitdata = {
|
||||
/* 0 */ DECLARE_ITE_DEV("IT8212"),
|
||||
};
|
||||
|
||||
/**
|
||||
* it821x_init_one - pci layer discovery entry
|
||||
* @dev: PCI device
|
||||
* @id: ident table entry
|
||||
*
|
||||
* Called by the PCI code when it finds an ITE821x controller.
|
||||
* We then use the IDE PCI generic helper to do most of the work.
|
||||
*/
|
||||
|
||||
static int __devinit it821x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
ide_setup_pci_device(dev, &it821x_chipsets[id->driver_data]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pci_device_id it821x_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8212, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, it821x_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "ITE821x IDE",
|
||||
.id_table = it821x_pci_tbl,
|
||||
.probe = it821x_init_one,
|
||||
};
|
||||
|
||||
static int __init it821x_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(it821x_ide_init);
|
||||
|
||||
module_param_named(noraid, it8212_noraid, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(it8212_noraid, "Force card into bypass mode");
|
||||
|
||||
MODULE_AUTHOR("Alan Cox");
|
||||
MODULE_DESCRIPTION("PCI driver module for the ITE 821x");
|
||||
MODULE_LICENSE("GPL");
|
||||
288
drivers/ide/pci/jmicron.c
Normal file
288
drivers/ide/pci/jmicron.c
Normal file
@@ -0,0 +1,288 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006 Red Hat <alan@redhat.com>
|
||||
*
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
typedef enum {
|
||||
PORT_PATA0 = 0,
|
||||
PORT_PATA1 = 1,
|
||||
PORT_SATA = 2,
|
||||
} port_type;
|
||||
|
||||
/**
|
||||
* jmicron_ratemask - Compute available modes
|
||||
* @drive: IDE drive
|
||||
*
|
||||
* Compute the available speeds for the devices on the interface. This
|
||||
* is all modes to ATA133 clipped by drive cable setup.
|
||||
*/
|
||||
|
||||
static u8 jmicron_ratemask(ide_drive_t *drive)
|
||||
{
|
||||
u8 mode = 4;
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min(mode, (u8)1);
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* ata66_jmicron - Cable check
|
||||
* @hwif: IDE port
|
||||
*
|
||||
* Return 1 if the cable is 80pin
|
||||
*/
|
||||
|
||||
static int __devinit ata66_jmicron(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *pdev = hwif->pci_dev;
|
||||
|
||||
u32 control;
|
||||
u32 control5;
|
||||
|
||||
int port = hwif->channel;
|
||||
port_type port_map[2];
|
||||
|
||||
pci_read_config_dword(pdev, 0x40, &control);
|
||||
|
||||
/* There are two basic mappings. One has the two SATA ports merged
|
||||
as master/slave and the secondary as PATA, the other has only the
|
||||
SATA port mapped */
|
||||
if (control & (1 << 23)) {
|
||||
port_map[0] = PORT_SATA;
|
||||
port_map[1] = PORT_PATA0;
|
||||
} else {
|
||||
port_map[0] = PORT_SATA;
|
||||
port_map[1] = PORT_SATA;
|
||||
}
|
||||
|
||||
/* The 365/366 may have this bit set to map the second PATA port
|
||||
as the internal primary channel */
|
||||
pci_read_config_dword(pdev, 0x80, &control5);
|
||||
if (control5 & (1<<24))
|
||||
port_map[0] = PORT_PATA1;
|
||||
|
||||
/* The two ports may then be logically swapped by the firmware */
|
||||
if (control & (1 << 22))
|
||||
port = port ^ 1;
|
||||
|
||||
/*
|
||||
* Now we know which physical port we are talking about we can
|
||||
* actually do our cable checking etc. Thankfully we don't need
|
||||
* to do the plumbing for other cases.
|
||||
*/
|
||||
switch (port_map[port])
|
||||
{
|
||||
case PORT_PATA0:
|
||||
if (control & (1 << 3)) /* 40/80 pin primary */
|
||||
return 0;
|
||||
return 1;
|
||||
case PORT_PATA1:
|
||||
if (control5 & (1 << 19)) /* 40/80 pin secondary */
|
||||
return 0;
|
||||
return 1;
|
||||
case PORT_SATA:
|
||||
break;
|
||||
}
|
||||
return 1; /* Avoid bogus "control reaches end of non-void function" */
|
||||
}
|
||||
|
||||
static void jmicron_tuneproc (ide_drive_t *drive, byte mode_wanted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* config_jmicron_chipset_for_pio - set drive timings
|
||||
* @drive: drive to tune
|
||||
* @speed we want
|
||||
*
|
||||
*/
|
||||
|
||||
static void config_jmicron_chipset_for_pio (ide_drive_t *drive, byte set_speed)
|
||||
{
|
||||
u8 speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
|
||||
if (set_speed)
|
||||
(void) ide_config_drive_speed(drive, speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* jmicron_tune_chipset - set controller timings
|
||||
* @drive: Drive to set up
|
||||
* @xferspeed: speed we want to achieve
|
||||
*
|
||||
* As the JMicron snoops for timings all we actually need to do is
|
||||
* make sure we don't set an invalid mode. We do need to honour
|
||||
* the cable detect here.
|
||||
*/
|
||||
|
||||
static int jmicron_tune_chipset (ide_drive_t *drive, byte xferspeed)
|
||||
{
|
||||
|
||||
u8 speed = ide_rate_filter(jmicron_ratemask(drive), xferspeed);
|
||||
|
||||
return ide_config_drive_speed(drive, speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* config_chipset_for_dma - configure for DMA
|
||||
* @drive: drive to configure
|
||||
*
|
||||
* As the JMicron snoops for timings all we actually need to do is
|
||||
* make sure we don't set an invalid mode.
|
||||
*/
|
||||
|
||||
static int config_chipset_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, jmicron_ratemask(drive));
|
||||
|
||||
if (!speed)
|
||||
return 0;
|
||||
|
||||
jmicron_tune_chipset(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
/**
|
||||
* jmicron_configure_drive_for_dma - set up for DMA transfers
|
||||
* @drive: drive we are going to set up
|
||||
*
|
||||
* As the JMicron snoops for timings all we actually need to do is
|
||||
* make sure we don't set an invalid mode.
|
||||
*/
|
||||
|
||||
static int jmicron_config_drive_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
if (ide_use_dma(drive) && config_chipset_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
config_jmicron_chipset_for_pio(drive, 1);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_hwif_jmicron - set up hwif structs
|
||||
* @hwif: interface to set up
|
||||
*
|
||||
* Minimal set up is required for the Jmicron hardware.
|
||||
*/
|
||||
|
||||
static void __devinit init_hwif_jmicron(ide_hwif_t *hwif)
|
||||
{
|
||||
hwif->speedproc = &jmicron_tune_chipset;
|
||||
hwif->tuneproc = &jmicron_tuneproc;
|
||||
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].autotune = 1;
|
||||
|
||||
if (!hwif->dma_base)
|
||||
goto fallback;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x7f;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
|
||||
hwif->ide_dma_check = &jmicron_config_drive_for_dma;
|
||||
if (!(hwif->udma_four))
|
||||
hwif->udma_four = ata66_jmicron(hwif);
|
||||
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
return;
|
||||
fallback:
|
||||
hwif->autodma = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
#define DECLARE_JMB_DEV(name_str) \
|
||||
{ \
|
||||
.name = name_str, \
|
||||
.init_hwif = init_hwif_jmicron, \
|
||||
.channels = 2, \
|
||||
.autodma = AUTODMA, \
|
||||
.bootable = ON_BOARD, \
|
||||
.enablebits = { {0x40, 1, 1}, {0x40, 0x10, 0x10} }, \
|
||||
}
|
||||
|
||||
static ide_pci_device_t jmicron_chipsets[] __devinitdata = {
|
||||
/* 0 */ DECLARE_JMB_DEV("JMB361"),
|
||||
/* 1 */ DECLARE_JMB_DEV("JMB363"),
|
||||
/* 2 */ DECLARE_JMB_DEV("JMB365"),
|
||||
/* 3 */ DECLARE_JMB_DEV("JMB366"),
|
||||
/* 4 */ DECLARE_JMB_DEV("JMB368"),
|
||||
};
|
||||
|
||||
/**
|
||||
* jmicron_init_one - pci layer discovery entry
|
||||
* @dev: PCI device
|
||||
* @id: ident table entry
|
||||
*
|
||||
* Called by the PCI code when it finds a Jmicron controller.
|
||||
* We then use the IDE PCI generic helper to do most of the work.
|
||||
*/
|
||||
|
||||
static int __devinit jmicron_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
ide_setup_pci_device(dev, &jmicron_chipsets[id->driver_data]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If libata is configured, jmicron PCI quirk will configure it such
|
||||
* that the SATA ports are in AHCI function while the PATA ports are
|
||||
* in a separate IDE function. In such cases, match device class and
|
||||
* attach only to IDE. If libata isn't configured, keep the old
|
||||
* behavior for backward compatibility.
|
||||
*/
|
||||
#if defined(CONFIG_ATA) || defined(CONFIG_ATA_MODULE)
|
||||
#define JMB_CLASS PCI_CLASS_STORAGE_IDE << 8
|
||||
#define JMB_CLASS_MASK 0xffff00
|
||||
#else
|
||||
#define JMB_CLASS 0
|
||||
#define JMB_CLASS_MASK 0
|
||||
#endif
|
||||
|
||||
static struct pci_device_id jmicron_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361,
|
||||
PCI_ANY_ID, PCI_ANY_ID, JMB_CLASS, JMB_CLASS_MASK, 0},
|
||||
{ PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363,
|
||||
PCI_ANY_ID, PCI_ANY_ID, JMB_CLASS, JMB_CLASS_MASK, 1},
|
||||
{ PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365,
|
||||
PCI_ANY_ID, PCI_ANY_ID, JMB_CLASS, JMB_CLASS_MASK, 2},
|
||||
{ PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366,
|
||||
PCI_ANY_ID, PCI_ANY_ID, JMB_CLASS, JMB_CLASS_MASK, 3},
|
||||
{ PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368,
|
||||
PCI_ANY_ID, PCI_ANY_ID, JMB_CLASS, JMB_CLASS_MASK, 4},
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, jmicron_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "JMicron IDE",
|
||||
.id_table = jmicron_pci_tbl,
|
||||
.probe = jmicron_init_one,
|
||||
};
|
||||
|
||||
static int __init jmicron_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(jmicron_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Alan Cox");
|
||||
MODULE_DESCRIPTION("PCI driver module for the JMicron in legacy modes");
|
||||
MODULE_LICENSE("GPL");
|
||||
315
drivers/ide/pci/ns87415.c
Normal file
315
drivers/ide/pci/ns87415.c
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/ns87415.c Version 2.00 Sep. 10, 2002
|
||||
*
|
||||
* Copyright (C) 1997-1998 Mark Lord <mlord@pobox.com>
|
||||
* Copyright (C) 1998 Eddie C. Dost <ecd@skynet.be>
|
||||
* Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2004 Grant Grundler <grundler at parisc-linux.org>
|
||||
*
|
||||
* Inspired by an earlier effort from David S. Miller <davem@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#ifdef CONFIG_SUPERIO
|
||||
/* SUPERIO 87560 is a PoS chip that NatSem denies exists.
|
||||
* Unfortunately, it's built-in on all Astro-based PA-RISC workstations
|
||||
* which use the integrated NS87514 cell for CD-ROM support.
|
||||
* i.e we have to support for CD-ROM installs.
|
||||
* See drivers/parisc/superio.c for more gory details.
|
||||
*/
|
||||
#include <asm/superio.h>
|
||||
|
||||
static unsigned long superio_ide_status[2];
|
||||
static unsigned long superio_ide_select[2];
|
||||
static unsigned long superio_ide_dma_status[2];
|
||||
|
||||
#define SUPERIO_IDE_MAX_RETRIES 25
|
||||
|
||||
/* Because of a defect in Super I/O, all reads of the PCI DMA status
|
||||
* registers, IDE status register and the IDE select register need to be
|
||||
* retried
|
||||
*/
|
||||
static u8 superio_ide_inb (unsigned long port)
|
||||
{
|
||||
if (port == superio_ide_status[0] ||
|
||||
port == superio_ide_status[1] ||
|
||||
port == superio_ide_select[0] ||
|
||||
port == superio_ide_select[1] ||
|
||||
port == superio_ide_dma_status[0] ||
|
||||
port == superio_ide_dma_status[1]) {
|
||||
u8 tmp;
|
||||
int retries = SUPERIO_IDE_MAX_RETRIES;
|
||||
|
||||
/* printk(" [ reading port 0x%x with retry ] ", port); */
|
||||
|
||||
do {
|
||||
tmp = inb(port);
|
||||
if (tmp == 0)
|
||||
udelay(50);
|
||||
} while (tmp == 0 && retries-- > 0);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
return inb(port);
|
||||
}
|
||||
|
||||
static void __devinit superio_ide_init_iops (struct hwif_s *hwif)
|
||||
{
|
||||
u32 base, dmabase;
|
||||
u8 tmp;
|
||||
struct pci_dev *pdev = hwif->pci_dev;
|
||||
u8 port = hwif->channel;
|
||||
|
||||
base = pci_resource_start(pdev, port * 2) & ~3;
|
||||
dmabase = pci_resource_start(pdev, 4) & ~3;
|
||||
|
||||
superio_ide_status[port] = base + IDE_STATUS_OFFSET;
|
||||
superio_ide_select[port] = base + IDE_SELECT_OFFSET;
|
||||
superio_ide_dma_status[port] = dmabase + (!port ? 2 : 0xa);
|
||||
|
||||
/* Clear error/interrupt, enable dma */
|
||||
tmp = superio_ide_inb(superio_ide_dma_status[port]);
|
||||
outb(tmp | 0x66, superio_ide_dma_status[port]);
|
||||
|
||||
/* We need to override inb to workaround a SuperIO errata */
|
||||
hwif->INB = superio_ide_inb;
|
||||
}
|
||||
|
||||
static void __devinit init_iops_ns87415(ide_hwif_t *hwif)
|
||||
{
|
||||
if (PCI_SLOT(hwif->pci_dev->devfn) == 0xE) {
|
||||
/* Built-in - assume it's under superio. */
|
||||
superio_ide_init_iops(hwif);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = { 0 };
|
||||
|
||||
/*
|
||||
* This routine either enables/disables (according to drive->present)
|
||||
* the IRQ associated with the port (HWIF(drive)),
|
||||
* and selects either PIO or DMA handshaking for the next I/O operation.
|
||||
*/
|
||||
static void ns87415_prepare_drive (ide_drive_t *drive, unsigned int use_dma)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned int bit, other, new, *old = (unsigned int *) hwif->select_data;
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
new = *old;
|
||||
|
||||
/* Adjust IRQ enable bit */
|
||||
bit = 1 << (8 + hwif->channel);
|
||||
new = drive->present ? (new & ~bit) : (new | bit);
|
||||
|
||||
/* Select PIO or DMA, DMA may only be selected for one drive/channel. */
|
||||
bit = 1 << (20 + drive->select.b.unit + (hwif->channel << 1));
|
||||
other = 1 << (20 + (1 - drive->select.b.unit) + (hwif->channel << 1));
|
||||
new = use_dma ? ((new & ~other) | bit) : (new & ~bit);
|
||||
|
||||
if (new != *old) {
|
||||
unsigned char stat;
|
||||
|
||||
/*
|
||||
* Don't change DMA engine settings while Write Buffers
|
||||
* are busy.
|
||||
*/
|
||||
(void) pci_read_config_byte(dev, 0x43, &stat);
|
||||
while (stat & 0x03) {
|
||||
udelay(1);
|
||||
(void) pci_read_config_byte(dev, 0x43, &stat);
|
||||
}
|
||||
|
||||
*old = new;
|
||||
(void) pci_write_config_dword(dev, 0x40, new);
|
||||
|
||||
/*
|
||||
* And let things settle...
|
||||
*/
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void ns87415_selectproc (ide_drive_t *drive)
|
||||
{
|
||||
ns87415_prepare_drive (drive, drive->using_dma);
|
||||
}
|
||||
|
||||
static int ns87415_ide_dma_end (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 dma_stat = 0, dma_cmd = 0;
|
||||
|
||||
drive->waiting_for_dma = 0;
|
||||
dma_stat = hwif->INB(hwif->dma_status);
|
||||
/* get dma command mode */
|
||||
dma_cmd = hwif->INB(hwif->dma_command);
|
||||
/* stop DMA */
|
||||
outb(dma_cmd & ~1, hwif->dma_command);
|
||||
/* from ERRATA: clear the INTR & ERROR bits */
|
||||
dma_cmd = hwif->INB(hwif->dma_command);
|
||||
outb(dma_cmd | 6, hwif->dma_command);
|
||||
/* and free any DMA resources */
|
||||
ide_destroy_dmatable(drive);
|
||||
/* verify good DMA status */
|
||||
return (dma_stat & 7) != 4;
|
||||
}
|
||||
|
||||
static int ns87415_ide_dma_setup(ide_drive_t *drive)
|
||||
{
|
||||
/* select DMA xfer */
|
||||
ns87415_prepare_drive(drive, 1);
|
||||
if (!ide_dma_setup(drive))
|
||||
return 0;
|
||||
/* DMA failed: select PIO xfer */
|
||||
ns87415_prepare_drive(drive, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ns87415_ide_dma_check (ide_drive_t *drive)
|
||||
{
|
||||
if (drive->media != ide_disk)
|
||||
return -1;
|
||||
|
||||
return __ide_dma_check(drive);
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
unsigned int ctrl, using_inta;
|
||||
u8 progif;
|
||||
#ifdef __sparc_v9__
|
||||
int timeout;
|
||||
u8 stat;
|
||||
#endif
|
||||
|
||||
hwif->autodma = 0;
|
||||
hwif->selectproc = &ns87415_selectproc;
|
||||
|
||||
/*
|
||||
* We cannot probe for IRQ: both ports share common IRQ on INTA.
|
||||
* Also, leave IRQ masked during drive probing, to prevent infinite
|
||||
* interrupts from a potentially floating INTA..
|
||||
*
|
||||
* IRQs get unmasked in selectproc when drive is first used.
|
||||
*/
|
||||
(void) pci_read_config_dword(dev, 0x40, &ctrl);
|
||||
(void) pci_read_config_byte(dev, 0x09, &progif);
|
||||
/* is irq in "native" mode? */
|
||||
using_inta = progif & (1 << (hwif->channel << 1));
|
||||
if (!using_inta)
|
||||
using_inta = ctrl & (1 << (4 + hwif->channel));
|
||||
if (hwif->mate) {
|
||||
hwif->select_data = hwif->mate->select_data;
|
||||
} else {
|
||||
hwif->select_data = (unsigned long)
|
||||
&ns87415_control[ns87415_count++];
|
||||
ctrl |= (1 << 8) | (1 << 9); /* mask both IRQs */
|
||||
if (using_inta)
|
||||
ctrl &= ~(1 << 6); /* unmask INTA */
|
||||
*((unsigned int *)hwif->select_data) = ctrl;
|
||||
(void) pci_write_config_dword(dev, 0x40, ctrl);
|
||||
|
||||
/*
|
||||
* Set prefetch size to 512 bytes for both ports,
|
||||
* but don't turn on/off prefetching here.
|
||||
*/
|
||||
pci_write_config_byte(dev, 0x55, 0xee);
|
||||
|
||||
#ifdef __sparc_v9__
|
||||
/*
|
||||
* XXX: Reset the device, if we don't it will not respond
|
||||
* to SELECT_DRIVE() properly during first probe_hwif().
|
||||
*/
|
||||
timeout = 10000;
|
||||
outb(12, hwif->io_ports[IDE_CONTROL_OFFSET]);
|
||||
udelay(10);
|
||||
outb(8, hwif->io_ports[IDE_CONTROL_OFFSET]);
|
||||
do {
|
||||
udelay(50);
|
||||
stat = hwif->INB(hwif->io_ports[IDE_STATUS_OFFSET]);
|
||||
if (stat == 0xff)
|
||||
break;
|
||||
} while ((stat & BUSY_STAT) && --timeout);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!using_inta)
|
||||
hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET]);
|
||||
else if (!hwif->irq && hwif->mate && hwif->mate->irq)
|
||||
hwif->irq = hwif->mate->irq; /* share IRQ with mate */
|
||||
|
||||
if (!hwif->dma_base)
|
||||
return;
|
||||
|
||||
outb(0x60, hwif->dma_status);
|
||||
hwif->dma_setup = &ns87415_ide_dma_setup;
|
||||
hwif->ide_dma_check = &ns87415_ide_dma_check;
|
||||
hwif->ide_dma_end = &ns87415_ide_dma_end;
|
||||
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
static ide_pci_device_t ns87415_chipset __devinitdata = {
|
||||
.name = "NS87415",
|
||||
#ifdef CONFIG_SUPERIO
|
||||
.init_iops = init_iops_ns87415,
|
||||
#endif
|
||||
.init_hwif = init_hwif_ns87415,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
};
|
||||
|
||||
static int __devinit ns87415_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &ns87415_chipset);
|
||||
}
|
||||
|
||||
static struct pci_device_id ns87415_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ns87415_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "NS87415_IDE",
|
||||
.id_table = ns87415_pci_tbl,
|
||||
.probe = ns87415_init_one,
|
||||
};
|
||||
|
||||
static int __init ns87415_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(ns87415_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Mark Lord, Eddie Dost, Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for NS87415 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
394
drivers/ide/pci/opti621.c
Normal file
394
drivers/ide/pci/opti621.c
Normal file
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/opti621.c Version 0.7 Sept 10, 2002
|
||||
*
|
||||
* Copyright (C) 1996-1998 Linus Torvalds & authors (see below)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Authors:
|
||||
* Jaromir Koutek <miri@punknet.cz>,
|
||||
* Jan Harkes <jaharkes@cwi.nl>,
|
||||
* Mark Lord <mlord@pobox.com>
|
||||
* Some parts of code are from ali14xx.c and from rz1000.c.
|
||||
*
|
||||
* OPTi is trademark of OPTi, Octek is trademark of Octek.
|
||||
*
|
||||
* I used docs from OPTi databook, from ftp.opti.com, file 9123-0002.ps
|
||||
* and disassembled/traced setupvic.exe (DOS program).
|
||||
* It increases kernel code about 2 kB.
|
||||
* I don't have this card no more, but I hope I can get some in case
|
||||
* of needed development.
|
||||
* My card is Octek PIDE 1.01 (on card) or OPTiViC (program).
|
||||
* It has a place for a secondary connector in circuit, but nothing
|
||||
* is there. Also BIOS says no address for
|
||||
* secondary controller (see bellow in ide_init_opti621).
|
||||
* I've only tested this on my system, which only has one disk.
|
||||
* It's Western Digital WDAC2850, with PIO mode 3. The PCI bus
|
||||
* is at 20 MHz (I have DX2/80, I tried PCI at 40, but I got random
|
||||
* lockups). I tried the OCTEK double speed CD-ROM and
|
||||
* it does not work! But I can't boot DOS also, so it's probably
|
||||
* hardware fault. I have connected Conner 80MB, the Seagate 850MB (no
|
||||
* problems) and Seagate 1GB (as slave, WD as master). My experiences
|
||||
* with the third, 1GB drive: I got 3MB/s (hdparm), but sometimes
|
||||
* it slows to about 100kB/s! I don't know why and I have
|
||||
* not this drive now, so I can't try it again.
|
||||
* I write this driver because I lost the paper ("manual") with
|
||||
* settings of jumpers on the card and I have to boot Linux with
|
||||
* Loadlin except LILO, cause I have to run the setupvic.exe program
|
||||
* already or I get disk errors (my test: rpm -Vf
|
||||
* /usr/X11R6/bin/XF86_SVGA - or any big file).
|
||||
* Some numbers from hdparm -t /dev/hda:
|
||||
* Timing buffer-cache reads: 32 MB in 3.02 seconds =10.60 MB/sec
|
||||
* Timing buffered disk reads: 16 MB in 5.52 seconds = 2.90 MB/sec
|
||||
* I have 4 Megs/s before, but I don't know why (maybe changes
|
||||
* in hdparm test).
|
||||
* After release of 0.1, I got some successful reports, so it might work.
|
||||
*
|
||||
* The main problem with OPTi is that some timings for master
|
||||
* and slave must be the same. For example, if you have master
|
||||
* PIO 3 and slave PIO 0, driver have to set some timings of
|
||||
* master for PIO 0. Second problem is that opti621_tune_drive
|
||||
* got only one drive to set, but have to set both drives.
|
||||
* This is solved in compute_pios. If you don't set
|
||||
* the second drive, compute_pios use ide_get_best_pio_mode
|
||||
* for autoselect mode (you can change it to PIO 0, if you want).
|
||||
* If you then set the second drive to another PIO, the old value
|
||||
* (automatically selected) will be overrided by yours.
|
||||
* There is a 25/33MHz switch in configuration
|
||||
* register, but driver is written for use at any frequency which get
|
||||
* (use idebus=xx to select PCI bus speed).
|
||||
* Use hda=autotune and hdb=autotune for automatical tune of the PIO modes.
|
||||
* If you get strange results, do not use this and set PIO manually
|
||||
* by hdparm.
|
||||
*
|
||||
* Version 0.1, Nov 8, 1996
|
||||
* by Jaromir Koutek, for 2.1.8.
|
||||
* Initial version of driver.
|
||||
*
|
||||
* Version 0.2
|
||||
* Number 0.2 skipped.
|
||||
*
|
||||
* Version 0.3, Nov 29, 1997
|
||||
* by Mark Lord (probably), for 2.1.68
|
||||
* Updates for use with new IDE block driver.
|
||||
*
|
||||
* Version 0.4, Dec 14, 1997
|
||||
* by Jan Harkes
|
||||
* Fixed some errors and cleaned the code.
|
||||
*
|
||||
* Version 0.5, Jan 2, 1998
|
||||
* by Jaromir Koutek
|
||||
* Updates for use with (again) new IDE block driver.
|
||||
* Update of documentation.
|
||||
*
|
||||
* Version 0.6, Jan 2, 1999
|
||||
* by Jaromir Koutek
|
||||
* Reversed to version 0.3 of the driver, because
|
||||
* 0.5 doesn't work.
|
||||
*/
|
||||
|
||||
#define OPTI621_DEBUG /* define for debug messages */
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.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/pci.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define OPTI621_MAX_PIO 3
|
||||
/* In fact, I do not have any PIO 4 drive
|
||||
* (address: 25 ns, data: 70 ns, recovery: 35 ns),
|
||||
* but OPTi 82C621 is programmable and it can do (minimal values):
|
||||
* on 40MHz PCI bus (pulse 25 ns):
|
||||
* address: 25 ns, data: 25 ns, recovery: 50 ns;
|
||||
* on 20MHz PCI bus (pulse 50 ns):
|
||||
* address: 50 ns, data: 50 ns, recovery: 100 ns.
|
||||
*/
|
||||
|
||||
/* #define READ_PREFETCH 0 */
|
||||
/* Uncomment for disable read prefetch.
|
||||
* There is some readprefetch capatibility in hdparm,
|
||||
* but when I type hdparm -P 1 /dev/hda, I got errors
|
||||
* and till reset drive is inaccessible.
|
||||
* This (hw) read prefetch is safe on my drive.
|
||||
*/
|
||||
|
||||
#ifndef READ_PREFETCH
|
||||
#define READ_PREFETCH 0x40 /* read prefetch is enabled */
|
||||
#endif /* else read prefetch is disabled */
|
||||
|
||||
#define READ_REG 0 /* index of Read cycle timing register */
|
||||
#define WRITE_REG 1 /* index of Write cycle timing register */
|
||||
#define CNTRL_REG 3 /* index of Control register */
|
||||
#define STRAP_REG 5 /* index of Strap register */
|
||||
#define MISC_REG 6 /* index of Miscellaneous register */
|
||||
|
||||
static int reg_base;
|
||||
|
||||
#define PIO_NOT_EXIST 254
|
||||
#define PIO_DONT_KNOW 255
|
||||
|
||||
/* there are stored pio numbers from other calls of opti621_tune_drive */
|
||||
static void compute_pios(ide_drive_t *drive, u8 pio)
|
||||
/* Store values into drive->drive_data
|
||||
* second_contr - 0 for primary controller, 1 for secondary
|
||||
* slave_drive - 0 -> pio is for master, 1 -> pio is for slave
|
||||
* pio - PIO mode for selected drive (for other we don't know)
|
||||
*/
|
||||
{
|
||||
int d;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
|
||||
drive->drive_data = ide_get_best_pio_mode(drive, pio, OPTI621_MAX_PIO, NULL);
|
||||
for (d = 0; d < 2; ++d) {
|
||||
drive = &hwif->drives[d];
|
||||
if (drive->present) {
|
||||
if (drive->drive_data == PIO_DONT_KNOW)
|
||||
drive->drive_data = ide_get_best_pio_mode(drive, 255, OPTI621_MAX_PIO, NULL);
|
||||
#ifdef OPTI621_DEBUG
|
||||
printk("%s: Selected PIO mode %d\n",
|
||||
drive->name, drive->drive_data);
|
||||
#endif
|
||||
} else {
|
||||
drive->drive_data = PIO_NOT_EXIST;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int cmpt_clk(int time, int bus_speed)
|
||||
/* Returns (rounded up) time in clocks for time in ns,
|
||||
* with bus_speed in MHz.
|
||||
* Example: bus_speed = 40 MHz, time = 80 ns
|
||||
* 1000/40 = 25 ns (clk value),
|
||||
* 80/25 = 3.2, rounded up to 4 (I hope ;-)).
|
||||
* Use idebus=xx to select right frequency.
|
||||
*/
|
||||
{
|
||||
return ((time*bus_speed+999)/1000);
|
||||
}
|
||||
|
||||
/* Write value to register reg, base of register
|
||||
* is at reg_base (0x1f0 primary, 0x170 secondary,
|
||||
* if not changed by PCI configuration).
|
||||
* This is from setupvic.exe program.
|
||||
*/
|
||||
static void write_reg(u8 value, int reg)
|
||||
{
|
||||
inw(reg_base + 1);
|
||||
inw(reg_base + 1);
|
||||
outb(3, reg_base + 2);
|
||||
outb(value, reg_base + reg);
|
||||
outb(0x83, reg_base + 2);
|
||||
}
|
||||
|
||||
/* Read value from register reg, base of register
|
||||
* is at reg_base (0x1f0 primary, 0x170 secondary,
|
||||
* if not changed by PCI configuration).
|
||||
* This is from setupvic.exe program.
|
||||
*/
|
||||
static u8 read_reg(int reg)
|
||||
{
|
||||
u8 ret = 0;
|
||||
|
||||
inw(reg_base + 1);
|
||||
inw(reg_base + 1);
|
||||
outb(3, reg_base + 2);
|
||||
ret = inb(reg_base + reg);
|
||||
outb(0x83, reg_base + 2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct pio_clocks_s {
|
||||
int address_time; /* Address setup (clocks) */
|
||||
int data_time; /* Active/data pulse (clocks) */
|
||||
int recovery_time; /* Recovery time (clocks) */
|
||||
} pio_clocks_t;
|
||||
|
||||
static void compute_clocks(int pio, pio_clocks_t *clks)
|
||||
{
|
||||
if (pio != PIO_NOT_EXIST) {
|
||||
int adr_setup, data_pls;
|
||||
int bus_speed = system_bus_clock();
|
||||
|
||||
adr_setup = ide_pio_timings[pio].setup_time;
|
||||
data_pls = ide_pio_timings[pio].active_time;
|
||||
clks->address_time = cmpt_clk(adr_setup, bus_speed);
|
||||
clks->data_time = cmpt_clk(data_pls, bus_speed);
|
||||
clks->recovery_time = cmpt_clk(ide_pio_timings[pio].cycle_time
|
||||
- adr_setup-data_pls, bus_speed);
|
||||
if (clks->address_time<1) clks->address_time = 1;
|
||||
if (clks->address_time>4) clks->address_time = 4;
|
||||
if (clks->data_time<1) clks->data_time = 1;
|
||||
if (clks->data_time>16) clks->data_time = 16;
|
||||
if (clks->recovery_time<2) clks->recovery_time = 2;
|
||||
if (clks->recovery_time>17) clks->recovery_time = 17;
|
||||
} else {
|
||||
clks->address_time = 1;
|
||||
clks->data_time = 1;
|
||||
clks->recovery_time = 2;
|
||||
/* minimal values */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Main tune procedure, called from tuneproc. */
|
||||
static void opti621_tune_drive (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
/* primary and secondary drives share some registers,
|
||||
* so we have to program both drives
|
||||
*/
|
||||
unsigned long flags;
|
||||
u8 pio1 = 0, pio2 = 0;
|
||||
pio_clocks_t first, second;
|
||||
int ax, drdy;
|
||||
u8 cycle1, cycle2, misc;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
|
||||
/* sets drive->drive_data for both drives */
|
||||
compute_pios(drive, pio);
|
||||
pio1 = hwif->drives[0].drive_data;
|
||||
pio2 = hwif->drives[1].drive_data;
|
||||
|
||||
compute_clocks(pio1, &first);
|
||||
compute_clocks(pio2, &second);
|
||||
|
||||
/* ax = max(a1,a2) */
|
||||
ax = (first.address_time < second.address_time) ? second.address_time : first.address_time;
|
||||
|
||||
drdy = 2; /* DRDY is default 2 (by OPTi Databook) */
|
||||
|
||||
cycle1 = ((first.data_time-1)<<4) | (first.recovery_time-2);
|
||||
cycle2 = ((second.data_time-1)<<4) | (second.recovery_time-2);
|
||||
misc = READ_PREFETCH | ((ax-1)<<4) | ((drdy-2)<<1);
|
||||
|
||||
#ifdef OPTI621_DEBUG
|
||||
printk("%s: master: address: %d, data: %d, "
|
||||
"recovery: %d, drdy: %d [clk]\n",
|
||||
hwif->name, ax, first.data_time,
|
||||
first.recovery_time, drdy);
|
||||
printk("%s: slave: address: %d, data: %d, "
|
||||
"recovery: %d, drdy: %d [clk]\n",
|
||||
hwif->name, ax, second.data_time,
|
||||
second.recovery_time, drdy);
|
||||
#endif
|
||||
|
||||
spin_lock_irqsave(&ide_lock, flags);
|
||||
|
||||
reg_base = hwif->io_ports[IDE_DATA_OFFSET];
|
||||
|
||||
/* allow Register-B */
|
||||
outb(0xc0, reg_base + CNTRL_REG);
|
||||
/* hmm, setupvic.exe does this ;-) */
|
||||
outb(0xff, reg_base + 5);
|
||||
/* if reads 0xff, adapter not exist? */
|
||||
(void)inb(reg_base + CNTRL_REG);
|
||||
/* if reads 0xc0, no interface exist? */
|
||||
read_reg(CNTRL_REG);
|
||||
/* read version, probably 0 */
|
||||
read_reg(STRAP_REG);
|
||||
|
||||
/* program primary drive */
|
||||
/* select Index-0 for Register-A */
|
||||
write_reg(0, MISC_REG);
|
||||
/* set read cycle timings */
|
||||
write_reg(cycle1, READ_REG);
|
||||
/* set write cycle timings */
|
||||
write_reg(cycle1, WRITE_REG);
|
||||
|
||||
/* program secondary drive */
|
||||
/* select Index-1 for Register-B */
|
||||
write_reg(1, MISC_REG);
|
||||
/* set read cycle timings */
|
||||
write_reg(cycle2, READ_REG);
|
||||
/* set write cycle timings */
|
||||
write_reg(cycle2, WRITE_REG);
|
||||
|
||||
/* use Register-A for drive 0 */
|
||||
/* use Register-B for drive 1 */
|
||||
write_reg(0x85, CNTRL_REG);
|
||||
|
||||
/* set address setup, DRDY timings, */
|
||||
/* and read prefetch for both drives */
|
||||
write_reg(misc, MISC_REG);
|
||||
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* init_hwif_opti621() is called once for each hwif found at boot.
|
||||
*/
|
||||
static void __devinit init_hwif_opti621 (ide_hwif_t *hwif)
|
||||
{
|
||||
hwif->autodma = 0;
|
||||
hwif->drives[0].drive_data = PIO_DONT_KNOW;
|
||||
hwif->drives[1].drive_data = PIO_DONT_KNOW;
|
||||
hwif->tuneproc = &opti621_tune_drive;
|
||||
|
||||
if (!(hwif->dma_base))
|
||||
return;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
hwif->swdma_mask = 0x07;
|
||||
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
static ide_pci_device_t opti621_chipsets[] __devinitdata = {
|
||||
{ /* 0 */
|
||||
.name = "OPTI621",
|
||||
.init_hwif = init_hwif_opti621,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.enablebits = {{0x45,0x80,0x00}, {0x40,0x08,0x00}},
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 1 */
|
||||
.name = "OPTI621X",
|
||||
.init_hwif = init_hwif_opti621,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.enablebits = {{0x45,0x80,0x00}, {0x40,0x08,0x00}},
|
||||
.bootable = ON_BOARD,
|
||||
}
|
||||
};
|
||||
|
||||
static int __devinit opti621_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &opti621_chipsets[id->driver_data]);
|
||||
}
|
||||
|
||||
static struct pci_device_id opti621_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, opti621_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "Opti621_IDE",
|
||||
.id_table = opti621_pci_tbl,
|
||||
.probe = opti621_init_one,
|
||||
};
|
||||
|
||||
static int __init opti621_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(opti621_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Jaromir Koutek, Jan Harkes, Mark Lord");
|
||||
MODULE_DESCRIPTION("PCI driver module for Opti621 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
719
drivers/ide/pci/pdc202xx_new.c
Normal file
719
drivers/ide/pci/pdc202xx_new.c
Normal file
@@ -0,0 +1,719 @@
|
||||
/*
|
||||
* Promise TX2/TX4/TX2000/133 IDE driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* Split from:
|
||||
* linux/drivers/ide/pdc202xx.c Version 0.35 Mar. 30, 2002
|
||||
* Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2005-2006 MontaVista Software, Inc.
|
||||
* Portions Copyright (C) 1999 Promise Technology, Inc.
|
||||
* Author: Frank Tiernan (frankt@promise.com)
|
||||
* Released under terms of General Public License
|
||||
*/
|
||||
|
||||
#include <linux/module.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/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
#include <asm/prom.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#endif
|
||||
|
||||
#define PDC202_DEBUG_CABLE 0
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fmt, args...) printk("%s: " fmt, __FUNCTION__, ## args)
|
||||
#else
|
||||
#define DBG(fmt, args...)
|
||||
#endif
|
||||
|
||||
static const char *pdc_quirk_drives[] = {
|
||||
"QUANTUM FIREBALLlct08 08",
|
||||
"QUANTUM FIREBALLP KA6.4",
|
||||
"QUANTUM FIREBALLP KA9.1",
|
||||
"QUANTUM FIREBALLP LM20.4",
|
||||
"QUANTUM FIREBALLP KX13.6",
|
||||
"QUANTUM FIREBALLP KX20.5",
|
||||
"QUANTUM FIREBALLP KX27.3",
|
||||
"QUANTUM FIREBALLP LM20.5",
|
||||
NULL
|
||||
};
|
||||
|
||||
static u8 max_dma_rate(struct pci_dev *pdev)
|
||||
{
|
||||
u8 mode;
|
||||
|
||||
switch(pdev->device) {
|
||||
case PCI_DEVICE_ID_PROMISE_20277:
|
||||
case PCI_DEVICE_ID_PROMISE_20276:
|
||||
case PCI_DEVICE_ID_PROMISE_20275:
|
||||
case PCI_DEVICE_ID_PROMISE_20271:
|
||||
case PCI_DEVICE_ID_PROMISE_20269:
|
||||
mode = 4;
|
||||
break;
|
||||
case PCI_DEVICE_ID_PROMISE_20270:
|
||||
case PCI_DEVICE_ID_PROMISE_20268:
|
||||
mode = 3;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static u8 pdcnew_ratemask(ide_drive_t *drive)
|
||||
{
|
||||
u8 mode = max_dma_rate(HWIF(drive)->pci_dev);
|
||||
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min_t(u8, mode, 1);
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_indexed_reg - Get indexed register
|
||||
* @hwif: for the port address
|
||||
* @index: index of the indexed register
|
||||
*/
|
||||
static u8 get_indexed_reg(ide_hwif_t *hwif, u8 index)
|
||||
{
|
||||
u8 value;
|
||||
|
||||
outb(index, hwif->dma_vendor1);
|
||||
value = inb(hwif->dma_vendor3);
|
||||
|
||||
DBG("index[%02X] value[%02X]\n", index, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* set_indexed_reg - Set indexed register
|
||||
* @hwif: for the port address
|
||||
* @index: index of the indexed register
|
||||
*/
|
||||
static void set_indexed_reg(ide_hwif_t *hwif, u8 index, u8 value)
|
||||
{
|
||||
outb(index, hwif->dma_vendor1);
|
||||
outb(value, hwif->dma_vendor3);
|
||||
DBG("index[%02X] value[%02X]\n", index, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* ATA Timing Tables based on 133 MHz PLL output clock.
|
||||
*
|
||||
* If the PLL outputs 100 MHz clock, the ASIC hardware will set
|
||||
* the timing registers automatically when "set features" command is
|
||||
* issued to the device. However, if the PLL output clock is 133 MHz,
|
||||
* the following tables must be used.
|
||||
*/
|
||||
static struct pio_timing {
|
||||
u8 reg0c, reg0d, reg13;
|
||||
} pio_timings [] = {
|
||||
{ 0xfb, 0x2b, 0xac }, /* PIO mode 0, IORDY off, Prefetch off */
|
||||
{ 0x46, 0x29, 0xa4 }, /* PIO mode 1, IORDY off, Prefetch off */
|
||||
{ 0x23, 0x26, 0x64 }, /* PIO mode 2, IORDY off, Prefetch off */
|
||||
{ 0x27, 0x0d, 0x35 }, /* PIO mode 3, IORDY on, Prefetch off */
|
||||
{ 0x23, 0x09, 0x25 }, /* PIO mode 4, IORDY on, Prefetch off */
|
||||
};
|
||||
|
||||
static struct mwdma_timing {
|
||||
u8 reg0e, reg0f;
|
||||
} mwdma_timings [] = {
|
||||
{ 0xdf, 0x5f }, /* MWDMA mode 0 */
|
||||
{ 0x6b, 0x27 }, /* MWDMA mode 1 */
|
||||
{ 0x69, 0x25 }, /* MWDMA mode 2 */
|
||||
};
|
||||
|
||||
static struct udma_timing {
|
||||
u8 reg10, reg11, reg12;
|
||||
} udma_timings [] = {
|
||||
{ 0x4a, 0x0f, 0xd5 }, /* UDMA mode 0 */
|
||||
{ 0x3a, 0x0a, 0xd0 }, /* UDMA mode 1 */
|
||||
{ 0x2a, 0x07, 0xcd }, /* UDMA mode 2 */
|
||||
{ 0x1a, 0x05, 0xcd }, /* UDMA mode 3 */
|
||||
{ 0x1a, 0x03, 0xcd }, /* UDMA mode 4 */
|
||||
{ 0x1a, 0x02, 0xcb }, /* UDMA mode 5 */
|
||||
{ 0x1a, 0x01, 0xcb }, /* UDMA mode 6 */
|
||||
};
|
||||
|
||||
static int pdcnew_tune_chipset(ide_drive_t *drive, u8 speed)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 adj = (drive->dn & 1) ? 0x08 : 0x00;
|
||||
int err;
|
||||
|
||||
speed = ide_rate_filter(pdcnew_ratemask(drive), speed);
|
||||
|
||||
/*
|
||||
* Issue SETFEATURES_XFER to the drive first. PDC202xx hardware will
|
||||
* automatically set the timing registers based on 100 MHz PLL output.
|
||||
*/
|
||||
err = ide_config_drive_speed(drive, speed);
|
||||
|
||||
/*
|
||||
* As we set up the PLL to output 133 MHz for UltraDMA/133 capable
|
||||
* chips, we must override the default register settings...
|
||||
*/
|
||||
if (max_dma_rate(hwif->pci_dev) == 4) {
|
||||
u8 mode = speed & 0x07;
|
||||
|
||||
switch (speed) {
|
||||
case XFER_UDMA_6:
|
||||
case XFER_UDMA_5:
|
||||
case XFER_UDMA_4:
|
||||
case XFER_UDMA_3:
|
||||
case XFER_UDMA_2:
|
||||
case XFER_UDMA_1:
|
||||
case XFER_UDMA_0:
|
||||
set_indexed_reg(hwif, 0x10 + adj,
|
||||
udma_timings[mode].reg10);
|
||||
set_indexed_reg(hwif, 0x11 + adj,
|
||||
udma_timings[mode].reg11);
|
||||
set_indexed_reg(hwif, 0x12 + adj,
|
||||
udma_timings[mode].reg12);
|
||||
break;
|
||||
|
||||
case XFER_MW_DMA_2:
|
||||
case XFER_MW_DMA_1:
|
||||
case XFER_MW_DMA_0:
|
||||
set_indexed_reg(hwif, 0x0e + adj,
|
||||
mwdma_timings[mode].reg0e);
|
||||
set_indexed_reg(hwif, 0x0f + adj,
|
||||
mwdma_timings[mode].reg0f);
|
||||
break;
|
||||
case XFER_PIO_4:
|
||||
case XFER_PIO_3:
|
||||
case XFER_PIO_2:
|
||||
case XFER_PIO_1:
|
||||
case XFER_PIO_0:
|
||||
set_indexed_reg(hwif, 0x0c + adj,
|
||||
pio_timings[mode].reg0c);
|
||||
set_indexed_reg(hwif, 0x0d + adj,
|
||||
pio_timings[mode].reg0d);
|
||||
set_indexed_reg(hwif, 0x13 + adj,
|
||||
pio_timings[mode].reg13);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "pdc202xx_new: "
|
||||
"Unknown speed %d ignored\n", speed);
|
||||
}
|
||||
} else if (speed == XFER_UDMA_2) {
|
||||
/* Set tHOLD bit to 0 if using UDMA mode 2 */
|
||||
u8 tmp = get_indexed_reg(hwif, 0x10 + adj);
|
||||
|
||||
set_indexed_reg(hwif, 0x10 + adj, tmp & 0x7f);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void pdcnew_tune_drive(ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
|
||||
(void)pdcnew_tune_chipset(drive, XFER_PIO_0 + pio);
|
||||
}
|
||||
|
||||
static u8 pdcnew_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
return get_indexed_reg(hwif, 0x0b) & 0x04;
|
||||
}
|
||||
|
||||
static int config_chipset_for_dma(ide_drive_t *drive)
|
||||
{
|
||||
struct hd_driveid *id = drive->id;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 ultra_66 = (id->dma_ultra & 0x0078) ? 1 : 0;
|
||||
u8 cable = pdcnew_cable_detect(hwif);
|
||||
u8 speed;
|
||||
|
||||
if (ultra_66 && cable) {
|
||||
printk(KERN_WARNING "Warning: %s channel "
|
||||
"requires an 80-pin cable for operation.\n",
|
||||
hwif->channel ? "Secondary" : "Primary");
|
||||
printk(KERN_WARNING "%s reduced to Ultra33 mode.\n", drive->name);
|
||||
}
|
||||
|
||||
if (drive->media != ide_disk && drive->media != ide_cdrom)
|
||||
return 0;
|
||||
|
||||
if (id->capability & 4) {
|
||||
/*
|
||||
* Set IORDY_EN & PREFETCH_EN (this seems to have
|
||||
* NO real effect since this register is reloaded
|
||||
* by hardware when the transfer mode is selected)
|
||||
*/
|
||||
u8 tmp, adj = (drive->dn & 1) ? 0x08 : 0x00;
|
||||
|
||||
tmp = get_indexed_reg(hwif, 0x13 + adj);
|
||||
set_indexed_reg(hwif, 0x13 + adj, tmp | 0x03);
|
||||
}
|
||||
|
||||
speed = ide_dma_speed(drive, pdcnew_ratemask(drive));
|
||||
|
||||
if (!speed)
|
||||
return 0;
|
||||
|
||||
(void) hwif->speedproc(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
static int pdcnew_config_drive_xfer_rate(ide_drive_t *drive)
|
||||
{
|
||||
drive->init_speed = 0;
|
||||
|
||||
if (ide_use_dma(drive) && config_chipset_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
if (ide_use_fast_pio(drive))
|
||||
pdcnew_tune_drive(drive, 255);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pdcnew_quirkproc(ide_drive_t *drive)
|
||||
{
|
||||
const char **list, *model = drive->id->model;
|
||||
|
||||
for (list = pdc_quirk_drives; *list != NULL; list++)
|
||||
if (strstr(model, *list) != NULL)
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pdcnew_reset(ide_drive_t *drive)
|
||||
{
|
||||
/*
|
||||
* Deleted this because it is redundant from the caller.
|
||||
*/
|
||||
printk(KERN_WARNING "pdc202xx_new: %s channel reset.\n",
|
||||
HWIF(drive)->channel ? "Secondary" : "Primary");
|
||||
}
|
||||
|
||||
/**
|
||||
* read_counter - Read the byte count registers
|
||||
* @dma_base: for the port address
|
||||
*/
|
||||
static long __devinit read_counter(u32 dma_base)
|
||||
{
|
||||
u32 pri_dma_base = dma_base, sec_dma_base = dma_base + 0x08;
|
||||
u8 cnt0, cnt1, cnt2, cnt3;
|
||||
long count = 0, last;
|
||||
int retry = 3;
|
||||
|
||||
do {
|
||||
last = count;
|
||||
|
||||
/* Read the current count */
|
||||
outb(0x20, pri_dma_base + 0x01);
|
||||
cnt0 = inb(pri_dma_base + 0x03);
|
||||
outb(0x21, pri_dma_base + 0x01);
|
||||
cnt1 = inb(pri_dma_base + 0x03);
|
||||
outb(0x20, sec_dma_base + 0x01);
|
||||
cnt2 = inb(sec_dma_base + 0x03);
|
||||
outb(0x21, sec_dma_base + 0x01);
|
||||
cnt3 = inb(sec_dma_base + 0x03);
|
||||
|
||||
count = (cnt3 << 23) | (cnt2 << 15) | (cnt1 << 8) | cnt0;
|
||||
|
||||
/*
|
||||
* The 30-bit decrementing counter is read in 4 pieces.
|
||||
* Incorrect value may be read when the most significant bytes
|
||||
* are changing...
|
||||
*/
|
||||
} while (retry-- && (((last ^ count) & 0x3fff8000) || last < count));
|
||||
|
||||
DBG("cnt0[%02X] cnt1[%02X] cnt2[%02X] cnt3[%02X]\n",
|
||||
cnt0, cnt1, cnt2, cnt3);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* detect_pll_input_clock - Detect the PLL input clock in Hz.
|
||||
* @dma_base: for the port address
|
||||
* E.g. 16949000 on 33 MHz PCI bus, i.e. half of the PCI clock.
|
||||
*/
|
||||
static long __devinit detect_pll_input_clock(unsigned long dma_base)
|
||||
{
|
||||
long start_count, end_count;
|
||||
long pll_input;
|
||||
u8 scr1;
|
||||
|
||||
start_count = read_counter(dma_base);
|
||||
|
||||
/* Start the test mode */
|
||||
outb(0x01, dma_base + 0x01);
|
||||
scr1 = inb(dma_base + 0x03);
|
||||
DBG("scr1[%02X]\n", scr1);
|
||||
outb(scr1 | 0x40, dma_base + 0x03);
|
||||
|
||||
/* Let the counter run for 10 ms. */
|
||||
mdelay(10);
|
||||
|
||||
end_count = read_counter(dma_base);
|
||||
|
||||
/* Stop the test mode */
|
||||
outb(0x01, dma_base + 0x01);
|
||||
scr1 = inb(dma_base + 0x03);
|
||||
DBG("scr1[%02X]\n", scr1);
|
||||
outb(scr1 & ~0x40, dma_base + 0x03);
|
||||
|
||||
/*
|
||||
* Calculate the input clock in Hz
|
||||
* (the clock counter is 30 bit wide and counts down)
|
||||
*/
|
||||
pll_input = ((start_count - end_count) & 0x3ffffff) * 100;
|
||||
|
||||
DBG("start[%ld] end[%ld]\n", start_count, end_count);
|
||||
|
||||
return pll_input;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
static void __devinit apple_kiwi_init(struct pci_dev *pdev)
|
||||
{
|
||||
struct device_node *np = pci_device_to_OF_node(pdev);
|
||||
unsigned int class_rev = 0;
|
||||
u8 conf;
|
||||
|
||||
if (np == NULL || !device_is_compatible(np, "kiwi-root"))
|
||||
return;
|
||||
|
||||
pci_read_config_dword(pdev, PCI_CLASS_REVISION, &class_rev);
|
||||
class_rev &= 0xff;
|
||||
|
||||
if (class_rev >= 0x03) {
|
||||
/* Setup chip magic config stuff (from darwin) */
|
||||
pci_read_config_byte (pdev, 0x40, &conf);
|
||||
pci_write_config_byte(pdev, 0x40, (conf | 0x01));
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_PPC_PMAC */
|
||||
|
||||
static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const char *name)
|
||||
{
|
||||
unsigned long dma_base = pci_resource_start(dev, 4);
|
||||
unsigned long sec_dma_base = dma_base + 0x08;
|
||||
long pll_input, pll_output, ratio;
|
||||
int f, r;
|
||||
u8 pll_ctl0, pll_ctl1;
|
||||
|
||||
if (dev->resource[PCI_ROM_RESOURCE].start) {
|
||||
pci_write_config_dword(dev, PCI_ROM_ADDRESS,
|
||||
dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
|
||||
printk(KERN_INFO "%s: ROM enabled at 0x%08lx\n", name,
|
||||
(unsigned long)dev->resource[PCI_ROM_RESOURCE].start);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
apple_kiwi_init(dev);
|
||||
#endif
|
||||
|
||||
/* Calculate the required PLL output frequency */
|
||||
switch(max_dma_rate(dev)) {
|
||||
case 4: /* it's 133 MHz for Ultra133 chips */
|
||||
pll_output = 133333333;
|
||||
break;
|
||||
case 3: /* and 100 MHz for Ultra100 chips */
|
||||
default:
|
||||
pll_output = 100000000;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect PLL input clock.
|
||||
* On some systems, where PCI bus is running at non-standard clock rate
|
||||
* (e.g. 25 or 40 MHz), we have to adjust the cycle time.
|
||||
* PDC20268 and newer chips employ PLL circuit to help correct timing
|
||||
* registers setting.
|
||||
*/
|
||||
pll_input = detect_pll_input_clock(dma_base);
|
||||
printk("%s: PLL input clock is %ld kHz\n", name, pll_input / 1000);
|
||||
|
||||
/* Sanity check */
|
||||
if (unlikely(pll_input < 5000000L || pll_input > 70000000L)) {
|
||||
printk(KERN_ERR "%s: Bad PLL input clock %ld Hz, giving up!\n",
|
||||
name, pll_input);
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
DBG("pll_output is %ld Hz\n", pll_output);
|
||||
|
||||
/* Show the current clock value of PLL control register
|
||||
* (maybe already configured by the BIOS)
|
||||
*/
|
||||
outb(0x02, sec_dma_base + 0x01);
|
||||
pll_ctl0 = inb(sec_dma_base + 0x03);
|
||||
outb(0x03, sec_dma_base + 0x01);
|
||||
pll_ctl1 = inb(sec_dma_base + 0x03);
|
||||
|
||||
DBG("pll_ctl[%02X][%02X]\n", pll_ctl0, pll_ctl1);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Calculate the ratio of F, R and NO
|
||||
* POUT = (F + 2) / (( R + 2) * NO)
|
||||
*/
|
||||
ratio = pll_output / (pll_input / 1000);
|
||||
if (ratio < 8600L) { /* 8.6x */
|
||||
/* Using NO = 0x01, R = 0x0d */
|
||||
r = 0x0d;
|
||||
} else if (ratio < 12900L) { /* 12.9x */
|
||||
/* Using NO = 0x01, R = 0x08 */
|
||||
r = 0x08;
|
||||
} else if (ratio < 16100L) { /* 16.1x */
|
||||
/* Using NO = 0x01, R = 0x06 */
|
||||
r = 0x06;
|
||||
} else if (ratio < 64000L) { /* 64x */
|
||||
r = 0x00;
|
||||
} else {
|
||||
/* Invalid ratio */
|
||||
printk(KERN_ERR "%s: Bad ratio %ld, giving up!\n", name, ratio);
|
||||
goto out;
|
||||
}
|
||||
|
||||
f = (ratio * (r + 2)) / 1000 - 2;
|
||||
|
||||
DBG("F[%d] R[%d] ratio*1000[%ld]\n", f, r, ratio);
|
||||
|
||||
if (unlikely(f < 0 || f > 127)) {
|
||||
/* Invalid F */
|
||||
printk(KERN_ERR "%s: F[%d] invalid!\n", name, f);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pll_ctl0 = (u8) f;
|
||||
pll_ctl1 = (u8) r;
|
||||
|
||||
DBG("Writing pll_ctl[%02X][%02X]\n", pll_ctl0, pll_ctl1);
|
||||
|
||||
outb(0x02, sec_dma_base + 0x01);
|
||||
outb(pll_ctl0, sec_dma_base + 0x03);
|
||||
outb(0x03, sec_dma_base + 0x01);
|
||||
outb(pll_ctl1, sec_dma_base + 0x03);
|
||||
|
||||
/* Wait the PLL circuit to be stable */
|
||||
mdelay(30);
|
||||
|
||||
#ifdef DEBUG
|
||||
/*
|
||||
* Show the current clock value of PLL control register
|
||||
*/
|
||||
outb(0x02, sec_dma_base + 0x01);
|
||||
pll_ctl0 = inb(sec_dma_base + 0x03);
|
||||
outb(0x03, sec_dma_base + 0x01);
|
||||
pll_ctl1 = inb(sec_dma_base + 0x03);
|
||||
|
||||
DBG("pll_ctl[%02X][%02X]\n", pll_ctl0, pll_ctl1);
|
||||
#endif
|
||||
|
||||
out:
|
||||
return dev->irq;
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_pdc202new(ide_hwif_t *hwif)
|
||||
{
|
||||
hwif->autodma = 0;
|
||||
|
||||
hwif->tuneproc = &pdcnew_tune_drive;
|
||||
hwif->quirkproc = &pdcnew_quirkproc;
|
||||
hwif->speedproc = &pdcnew_tune_chipset;
|
||||
hwif->resetproc = &pdcnew_reset;
|
||||
|
||||
hwif->drives[0].autotune = hwif->drives[1].autotune = 1;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x7f;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
|
||||
hwif->err_stops_fifo = 1;
|
||||
|
||||
hwif->ide_dma_check = &pdcnew_config_drive_xfer_rate;
|
||||
|
||||
if (!hwif->udma_four)
|
||||
hwif->udma_four = pdcnew_cable_detect(hwif) ? 0 : 1;
|
||||
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->drives[1].autodma = hwif->autodma;
|
||||
|
||||
#if PDC202_DEBUG_CABLE
|
||||
printk(KERN_DEBUG "%s: %s-pin cable\n",
|
||||
hwif->name, hwif->udma_four ? "80" : "40");
|
||||
#endif /* PDC202_DEBUG_CABLE */
|
||||
}
|
||||
|
||||
static int __devinit init_setup_pdcnew(struct pci_dev *dev, ide_pci_device_t *d)
|
||||
{
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
static int __devinit init_setup_pdc20270(struct pci_dev *dev,
|
||||
ide_pci_device_t *d)
|
||||
{
|
||||
struct pci_dev *findev = NULL;
|
||||
int ret;
|
||||
|
||||
if ((dev->bus->self &&
|
||||
dev->bus->self->vendor == PCI_VENDOR_ID_DEC) &&
|
||||
(dev->bus->self->device == PCI_DEVICE_ID_DEC_21150)) {
|
||||
if (PCI_SLOT(dev->devfn) & 2)
|
||||
return -ENODEV;
|
||||
d->extra = 0;
|
||||
while ((findev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, findev)) != NULL) {
|
||||
if ((findev->vendor == dev->vendor) &&
|
||||
(findev->device == dev->device) &&
|
||||
(PCI_SLOT(findev->devfn) & 2)) {
|
||||
if (findev->irq != dev->irq) {
|
||||
findev->irq = dev->irq;
|
||||
}
|
||||
ret = ide_setup_pci_devices(dev, findev, d);
|
||||
pci_dev_put(findev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
static int __devinit init_setup_pdc20276(struct pci_dev *dev,
|
||||
ide_pci_device_t *d)
|
||||
{
|
||||
if ((dev->bus->self) &&
|
||||
(dev->bus->self->vendor == PCI_VENDOR_ID_INTEL) &&
|
||||
((dev->bus->self->device == PCI_DEVICE_ID_INTEL_I960) ||
|
||||
(dev->bus->self->device == PCI_DEVICE_ID_INTEL_I960RM))) {
|
||||
printk(KERN_INFO "ide: Skipping Promise PDC20276 "
|
||||
"attached to I2O RAID controller.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
static ide_pci_device_t pdcnew_chipsets[] __devinitdata = {
|
||||
{ /* 0 */
|
||||
.name = "PDC20268",
|
||||
.init_setup = init_setup_pdcnew,
|
||||
.init_chipset = init_chipset_pdcnew,
|
||||
.init_hwif = init_hwif_pdc202new,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
},{ /* 1 */
|
||||
.name = "PDC20269",
|
||||
.init_setup = init_setup_pdcnew,
|
||||
.init_chipset = init_chipset_pdcnew,
|
||||
.init_hwif = init_hwif_pdc202new,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
},{ /* 2 */
|
||||
.name = "PDC20270",
|
||||
.init_setup = init_setup_pdc20270,
|
||||
.init_chipset = init_chipset_pdcnew,
|
||||
.init_hwif = init_hwif_pdc202new,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
},{ /* 3 */
|
||||
.name = "PDC20271",
|
||||
.init_setup = init_setup_pdcnew,
|
||||
.init_chipset = init_chipset_pdcnew,
|
||||
.init_hwif = init_hwif_pdc202new,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
},{ /* 4 */
|
||||
.name = "PDC20275",
|
||||
.init_setup = init_setup_pdcnew,
|
||||
.init_chipset = init_chipset_pdcnew,
|
||||
.init_hwif = init_hwif_pdc202new,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
},{ /* 5 */
|
||||
.name = "PDC20276",
|
||||
.init_setup = init_setup_pdc20276,
|
||||
.init_chipset = init_chipset_pdcnew,
|
||||
.init_hwif = init_hwif_pdc202new,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
},{ /* 6 */
|
||||
.name = "PDC20277",
|
||||
.init_setup = init_setup_pdcnew,
|
||||
.init_chipset = init_chipset_pdcnew,
|
||||
.init_hwif = init_hwif_pdc202new,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* pdc202new_init_one - called when a pdc202xx is found
|
||||
* @dev: the pdc202new device
|
||||
* @id: the matching pci id
|
||||
*
|
||||
* Called when the PCI registration layer (or the IDE initialization)
|
||||
* finds a device matching our IDE device tables.
|
||||
*/
|
||||
|
||||
static int __devinit pdc202new_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
ide_pci_device_t *d = &pdcnew_chipsets[id->driver_data];
|
||||
|
||||
return d->init_setup(dev, d);
|
||||
}
|
||||
|
||||
static struct pci_device_id pdc202new_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20269, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
|
||||
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20270, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2},
|
||||
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20271, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3},
|
||||
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20275, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
|
||||
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20276, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5},
|
||||
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20277, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pdc202new_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "Promise_IDE",
|
||||
.id_table = pdc202new_pci_tbl,
|
||||
.probe = pdc202new_init_one,
|
||||
};
|
||||
|
||||
static int __init pdc202new_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(pdc202new_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick, Frank Tiernan");
|
||||
MODULE_DESCRIPTION("PCI driver module for Promise PDC20268 and higher");
|
||||
MODULE_LICENSE("GPL");
|
||||
674
drivers/ide/pci/pdc202xx_old.c
Normal file
674
drivers/ide/pci/pdc202xx_old.c
Normal file
@@ -0,0 +1,674 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/pdc202xx_old.c Version 0.36 Sept 11, 2002
|
||||
*
|
||||
* Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2006-2007 MontaVista Software, Inc.
|
||||
*
|
||||
* Promise Ultra33 cards with BIOS v1.20 through 1.28 will need this
|
||||
* compiled into the kernel if you have more than one card installed.
|
||||
* Note that BIOS v1.29 is reported to fix the problem. Since this is
|
||||
* safe chipset tuning, including this support is harmless
|
||||
*
|
||||
* Promise Ultra66 cards with BIOS v1.11 this
|
||||
* compiled into the kernel if you have more than one card installed.
|
||||
*
|
||||
* Promise Ultra100 cards.
|
||||
*
|
||||
* The latest chipset code will support the following ::
|
||||
* Three Ultra33 controllers and 12 drives.
|
||||
* 8 are UDMA supported and 4 are limited to DMA mode 2 multi-word.
|
||||
* The 8/4 ratio is a BIOS code limit by promise.
|
||||
*
|
||||
* UNLESS you enable "CONFIG_PDC202XX_BURST"
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Portions Copyright (C) 1999 Promise Technology, Inc.
|
||||
* Author: Frank Tiernan (frankt@promise.com)
|
||||
* Released under terms of General Public License
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.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/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#define PDC202_DEBUG_CABLE 0
|
||||
#define PDC202XX_DEBUG_DRIVE_INFO 0
|
||||
|
||||
static const char *pdc_quirk_drives[] = {
|
||||
"QUANTUM FIREBALLlct08 08",
|
||||
"QUANTUM FIREBALLP KA6.4",
|
||||
"QUANTUM FIREBALLP KA9.1",
|
||||
"QUANTUM FIREBALLP LM20.4",
|
||||
"QUANTUM FIREBALLP KX13.6",
|
||||
"QUANTUM FIREBALLP KX20.5",
|
||||
"QUANTUM FIREBALLP KX27.3",
|
||||
"QUANTUM FIREBALLP LM20.5",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* A Register */
|
||||
#define SYNC_ERRDY_EN 0xC0
|
||||
|
||||
#define SYNC_IN 0x80 /* control bit, different for master vs. slave drives */
|
||||
#define ERRDY_EN 0x40 /* control bit, different for master vs. slave drives */
|
||||
#define IORDY_EN 0x20 /* PIO: IOREADY */
|
||||
#define PREFETCH_EN 0x10 /* PIO: PREFETCH */
|
||||
|
||||
#define PA3 0x08 /* PIO"A" timing */
|
||||
#define PA2 0x04 /* PIO"A" timing */
|
||||
#define PA1 0x02 /* PIO"A" timing */
|
||||
#define PA0 0x01 /* PIO"A" timing */
|
||||
|
||||
/* B Register */
|
||||
|
||||
#define MB2 0x80 /* DMA"B" timing */
|
||||
#define MB1 0x40 /* DMA"B" timing */
|
||||
#define MB0 0x20 /* DMA"B" timing */
|
||||
|
||||
#define PB4 0x10 /* PIO_FORCE 1:0 */
|
||||
|
||||
#define PB3 0x08 /* PIO"B" timing */ /* PIO flow Control mode */
|
||||
#define PB2 0x04 /* PIO"B" timing */ /* PIO 4 */
|
||||
#define PB1 0x02 /* PIO"B" timing */ /* PIO 3 half */
|
||||
#define PB0 0x01 /* PIO"B" timing */ /* PIO 3 other half */
|
||||
|
||||
/* C Register */
|
||||
#define IORDYp_NO_SPEED 0x4F
|
||||
#define SPEED_DIS 0x0F
|
||||
|
||||
#define DMARQp 0x80
|
||||
#define IORDYp 0x40
|
||||
#define DMAR_EN 0x20
|
||||
#define DMAW_EN 0x10
|
||||
|
||||
#define MC3 0x08 /* DMA"C" timing */
|
||||
#define MC2 0x04 /* DMA"C" timing */
|
||||
#define MC1 0x02 /* DMA"C" timing */
|
||||
#define MC0 0x01 /* DMA"C" timing */
|
||||
|
||||
static u8 pdc202xx_ratemask (ide_drive_t *drive)
|
||||
{
|
||||
u8 mode;
|
||||
|
||||
switch(HWIF(drive)->pci_dev->device) {
|
||||
case PCI_DEVICE_ID_PROMISE_20267:
|
||||
case PCI_DEVICE_ID_PROMISE_20265:
|
||||
mode = 3;
|
||||
break;
|
||||
case PCI_DEVICE_ID_PROMISE_20263:
|
||||
case PCI_DEVICE_ID_PROMISE_20262:
|
||||
mode = 2;
|
||||
break;
|
||||
case PCI_DEVICE_ID_PROMISE_20246:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min(mode, (u8)1);
|
||||
return mode;
|
||||
}
|
||||
|
||||
static int pdc202xx_tune_chipset (ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u8 drive_pci = 0x60 + (drive->dn << 2);
|
||||
u8 speed = ide_rate_filter(pdc202xx_ratemask(drive), xferspeed);
|
||||
|
||||
u32 drive_conf;
|
||||
u8 AP, BP, CP, DP;
|
||||
u8 TA = 0, TB = 0, TC = 0;
|
||||
|
||||
if (drive->media != ide_disk &&
|
||||
drive->media != ide_cdrom && speed < XFER_SW_DMA_0)
|
||||
return -1;
|
||||
|
||||
pci_read_config_dword(dev, drive_pci, &drive_conf);
|
||||
pci_read_config_byte(dev, (drive_pci), &AP);
|
||||
pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
|
||||
pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
|
||||
pci_read_config_byte(dev, (drive_pci)|0x03, &DP);
|
||||
|
||||
if (speed < XFER_SW_DMA_0) {
|
||||
if ((AP & 0x0F) || (BP & 0x07)) {
|
||||
/* clear PIO modes of lower 8421 bits of A Register */
|
||||
pci_write_config_byte(dev, (drive_pci), AP &~0x0F);
|
||||
pci_read_config_byte(dev, (drive_pci), &AP);
|
||||
|
||||
/* clear PIO modes of lower 421 bits of B Register */
|
||||
pci_write_config_byte(dev, (drive_pci)|0x01, BP &~0x07);
|
||||
pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
|
||||
|
||||
pci_read_config_byte(dev, (drive_pci), &AP);
|
||||
pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
|
||||
}
|
||||
} else {
|
||||
if ((BP & 0xF0) && (CP & 0x0F)) {
|
||||
/* clear DMA modes of upper 842 bits of B Register */
|
||||
/* clear PIO forced mode upper 1 bit of B Register */
|
||||
pci_write_config_byte(dev, (drive_pci)|0x01, BP &~0xF0);
|
||||
pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
|
||||
|
||||
/* clear DMA modes of lower 8421 bits of C Register */
|
||||
pci_write_config_byte(dev, (drive_pci)|0x02, CP &~0x0F);
|
||||
pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
|
||||
}
|
||||
}
|
||||
|
||||
pci_read_config_byte(dev, (drive_pci), &AP);
|
||||
pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
|
||||
pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
|
||||
|
||||
switch(speed) {
|
||||
case XFER_UDMA_6: speed = XFER_UDMA_5;
|
||||
case XFER_UDMA_5:
|
||||
case XFER_UDMA_4: TB = 0x20; TC = 0x01; break;
|
||||
case XFER_UDMA_2: TB = 0x20; TC = 0x01; break;
|
||||
case XFER_UDMA_3:
|
||||
case XFER_UDMA_1: TB = 0x40; TC = 0x02; break;
|
||||
case XFER_UDMA_0:
|
||||
case XFER_MW_DMA_2: TB = 0x60; TC = 0x03; break;
|
||||
case XFER_MW_DMA_1: TB = 0x60; TC = 0x04; break;
|
||||
case XFER_MW_DMA_0:
|
||||
case XFER_SW_DMA_2: TB = 0x60; TC = 0x05; break;
|
||||
case XFER_SW_DMA_1: TB = 0x80; TC = 0x06; break;
|
||||
case XFER_SW_DMA_0: TB = 0xC0; TC = 0x0B; break;
|
||||
case XFER_PIO_4: TA = 0x01; TB = 0x04; break;
|
||||
case XFER_PIO_3: TA = 0x02; TB = 0x06; break;
|
||||
case XFER_PIO_2: TA = 0x03; TB = 0x08; break;
|
||||
case XFER_PIO_1: TA = 0x05; TB = 0x0C; break;
|
||||
case XFER_PIO_0:
|
||||
default: TA = 0x09; TB = 0x13; break;
|
||||
}
|
||||
|
||||
if (speed < XFER_SW_DMA_0) {
|
||||
pci_write_config_byte(dev, (drive_pci), AP|TA);
|
||||
pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB);
|
||||
} else {
|
||||
pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB);
|
||||
pci_write_config_byte(dev, (drive_pci)|0x02, CP|TC);
|
||||
}
|
||||
|
||||
#if PDC202XX_DEBUG_DRIVE_INFO
|
||||
printk(KERN_DEBUG "%s: %s drive%d 0x%08x ",
|
||||
drive->name, ide_xfer_verbose(speed),
|
||||
drive->dn, drive_conf);
|
||||
pci_read_config_dword(dev, drive_pci, &drive_conf);
|
||||
printk("0x%08x\n", drive_conf);
|
||||
#endif /* PDC202XX_DEBUG_DRIVE_INFO */
|
||||
|
||||
return (ide_config_drive_speed(drive, speed));
|
||||
}
|
||||
|
||||
|
||||
static void pdc202xx_tune_drive(ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
|
||||
pdc202xx_tune_chipset(drive, XFER_PIO_0 + pio);
|
||||
}
|
||||
|
||||
static u8 pdc202xx_old_cable_detect (ide_hwif_t *hwif)
|
||||
{
|
||||
u16 CIS = 0, mask = (hwif->channel) ? (1<<11) : (1<<10);
|
||||
pci_read_config_word(hwif->pci_dev, 0x50, &CIS);
|
||||
return (CIS & mask) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the control register to use the 66MHz system
|
||||
* clock for UDMA 3/4/5 mode operation when necessary.
|
||||
*
|
||||
* It may also be possible to leave the 66MHz clock on
|
||||
* and readjust the timing parameters.
|
||||
*/
|
||||
static void pdc_old_enable_66MHz_clock(ide_hwif_t *hwif)
|
||||
{
|
||||
unsigned long clock_reg = hwif->dma_master + 0x11;
|
||||
u8 clock = inb(clock_reg);
|
||||
|
||||
outb(clock | (hwif->channel ? 0x08 : 0x02), clock_reg);
|
||||
}
|
||||
|
||||
static void pdc_old_disable_66MHz_clock(ide_hwif_t *hwif)
|
||||
{
|
||||
unsigned long clock_reg = hwif->dma_master + 0x11;
|
||||
u8 clock = inb(clock_reg);
|
||||
|
||||
outb(clock & ~(hwif->channel ? 0x08 : 0x02), clock_reg);
|
||||
}
|
||||
|
||||
static int config_chipset_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
struct hd_driveid *id = drive->id;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u32 drive_conf = 0;
|
||||
u8 drive_pci = 0x60 + (drive->dn << 2);
|
||||
u8 test1 = 0, test2 = 0, speed = -1;
|
||||
u8 AP = 0, cable = 0;
|
||||
|
||||
u8 ultra_66 = ((id->dma_ultra & 0x0010) ||
|
||||
(id->dma_ultra & 0x0008)) ? 1 : 0;
|
||||
|
||||
if (dev->device != PCI_DEVICE_ID_PROMISE_20246)
|
||||
cable = pdc202xx_old_cable_detect(hwif);
|
||||
else
|
||||
ultra_66 = 0;
|
||||
|
||||
if (ultra_66 && cable) {
|
||||
printk(KERN_WARNING "Warning: %s channel requires an 80-pin cable for operation.\n", hwif->channel ? "Secondary":"Primary");
|
||||
printk(KERN_WARNING "%s reduced to Ultra33 mode.\n", drive->name);
|
||||
}
|
||||
|
||||
if (dev->device != PCI_DEVICE_ID_PROMISE_20246)
|
||||
pdc_old_disable_66MHz_clock(drive->hwif);
|
||||
|
||||
drive_pci = 0x60 + (drive->dn << 2);
|
||||
pci_read_config_dword(dev, drive_pci, &drive_conf);
|
||||
if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
|
||||
goto chipset_is_set;
|
||||
|
||||
pci_read_config_byte(dev, drive_pci, &test1);
|
||||
if (!(test1 & SYNC_ERRDY_EN)) {
|
||||
if (drive->select.b.unit & 0x01) {
|
||||
pci_read_config_byte(dev, drive_pci - 4, &test2);
|
||||
if ((test2 & SYNC_ERRDY_EN) &&
|
||||
!(test1 & SYNC_ERRDY_EN)) {
|
||||
pci_write_config_byte(dev, drive_pci,
|
||||
test1|SYNC_ERRDY_EN);
|
||||
}
|
||||
} else {
|
||||
pci_write_config_byte(dev, drive_pci,
|
||||
test1|SYNC_ERRDY_EN);
|
||||
}
|
||||
}
|
||||
|
||||
chipset_is_set:
|
||||
|
||||
pci_read_config_byte(dev, (drive_pci), &AP);
|
||||
if (id->capability & 4) /* IORDY_EN */
|
||||
pci_write_config_byte(dev, (drive_pci), AP|IORDY_EN);
|
||||
pci_read_config_byte(dev, (drive_pci), &AP);
|
||||
if (drive->media == ide_disk) /* PREFETCH_EN */
|
||||
pci_write_config_byte(dev, (drive_pci), AP|PREFETCH_EN);
|
||||
|
||||
speed = ide_dma_speed(drive, pdc202xx_ratemask(drive));
|
||||
|
||||
if (!(speed)) {
|
||||
/* restore original pci-config space */
|
||||
pci_write_config_dword(dev, drive_pci, drive_conf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
(void) hwif->speedproc(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
static int pdc202xx_config_drive_xfer_rate (ide_drive_t *drive)
|
||||
{
|
||||
drive->init_speed = 0;
|
||||
|
||||
if (ide_use_dma(drive) && config_chipset_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
if (ide_use_fast_pio(drive))
|
||||
pdc202xx_tune_drive(drive, 255);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pdc202xx_quirkproc (ide_drive_t *drive)
|
||||
{
|
||||
const char **list, *model = drive->id->model;
|
||||
|
||||
for (list = pdc_quirk_drives; *list != NULL; list++)
|
||||
if (strstr(model, *list) != NULL)
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pdc202xx_old_ide_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
if (drive->current_speed > XFER_UDMA_2)
|
||||
pdc_old_enable_66MHz_clock(drive->hwif);
|
||||
if (drive->media != ide_disk || drive->addressing == 1) {
|
||||
struct request *rq = HWGROUP(drive)->rq;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned long high_16 = hwif->dma_master;
|
||||
unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20);
|
||||
u32 word_count = 0;
|
||||
u8 clock = inb(high_16 + 0x11);
|
||||
|
||||
outb(clock | (hwif->channel ? 0x08 : 0x02), high_16 + 0x11);
|
||||
word_count = (rq->nr_sectors << 8);
|
||||
word_count = (rq_data_dir(rq) == READ) ?
|
||||
word_count | 0x05000000 :
|
||||
word_count | 0x06000000;
|
||||
outl(word_count, atapi_reg);
|
||||
}
|
||||
ide_dma_start(drive);
|
||||
}
|
||||
|
||||
static int pdc202xx_old_ide_dma_end(ide_drive_t *drive)
|
||||
{
|
||||
if (drive->media != ide_disk || drive->addressing == 1) {
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned long high_16 = hwif->dma_master;
|
||||
unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20);
|
||||
u8 clock = 0;
|
||||
|
||||
outl(0, atapi_reg); /* zero out extra */
|
||||
clock = inb(high_16 + 0x11);
|
||||
outb(clock & ~(hwif->channel ? 0x08:0x02), high_16 + 0x11);
|
||||
}
|
||||
if (drive->current_speed > XFER_UDMA_2)
|
||||
pdc_old_disable_66MHz_clock(drive->hwif);
|
||||
return __ide_dma_end(drive);
|
||||
}
|
||||
|
||||
static int pdc202xx_old_ide_dma_test_irq(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned long high_16 = hwif->dma_master;
|
||||
u8 dma_stat = inb(hwif->dma_status);
|
||||
u8 sc1d = inb(high_16 + 0x001d);
|
||||
|
||||
if (hwif->channel) {
|
||||
/* bit7: Error, bit6: Interrupting, bit5: FIFO Full, bit4: FIFO Empty */
|
||||
if ((sc1d & 0x50) == 0x50)
|
||||
goto somebody_else;
|
||||
else if ((sc1d & 0x40) == 0x40)
|
||||
return (dma_stat & 4) == 4;
|
||||
} else {
|
||||
/* bit3: Error, bit2: Interrupting, bit1: FIFO Full, bit0: FIFO Empty */
|
||||
if ((sc1d & 0x05) == 0x05)
|
||||
goto somebody_else;
|
||||
else if ((sc1d & 0x04) == 0x04)
|
||||
return (dma_stat & 4) == 4;
|
||||
}
|
||||
somebody_else:
|
||||
return (dma_stat & 4) == 4; /* return 1 if INTR asserted */
|
||||
}
|
||||
|
||||
static int pdc202xx_ide_dma_lostirq(ide_drive_t *drive)
|
||||
{
|
||||
if (HWIF(drive)->resetproc != NULL)
|
||||
HWIF(drive)->resetproc(drive);
|
||||
return __ide_dma_lostirq(drive);
|
||||
}
|
||||
|
||||
static int pdc202xx_ide_dma_timeout(ide_drive_t *drive)
|
||||
{
|
||||
if (HWIF(drive)->resetproc != NULL)
|
||||
HWIF(drive)->resetproc(drive);
|
||||
return __ide_dma_timeout(drive);
|
||||
}
|
||||
|
||||
static void pdc202xx_reset_host (ide_hwif_t *hwif)
|
||||
{
|
||||
unsigned long high_16 = hwif->dma_master;
|
||||
u8 udma_speed_flag = inb(high_16 | 0x001f);
|
||||
|
||||
outb(udma_speed_flag | 0x10, high_16 | 0x001f);
|
||||
mdelay(100);
|
||||
outb(udma_speed_flag & ~0x10, high_16 | 0x001f);
|
||||
mdelay(2000); /* 2 seconds ?! */
|
||||
|
||||
printk(KERN_WARNING "PDC202XX: %s channel reset.\n",
|
||||
hwif->channel ? "Secondary" : "Primary");
|
||||
}
|
||||
|
||||
static void pdc202xx_reset (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
ide_hwif_t *mate = hwif->mate;
|
||||
|
||||
pdc202xx_reset_host(hwif);
|
||||
pdc202xx_reset_host(mate);
|
||||
pdc202xx_tune_drive(drive, 255);
|
||||
}
|
||||
|
||||
static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev,
|
||||
const char *name)
|
||||
{
|
||||
/* This doesn't appear needed */
|
||||
if (dev->resource[PCI_ROM_RESOURCE].start) {
|
||||
pci_write_config_dword(dev, PCI_ROM_ADDRESS,
|
||||
dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
|
||||
printk(KERN_INFO "%s: ROM enabled at 0x%08lx\n", name,
|
||||
(unsigned long)dev->resource[PCI_ROM_RESOURCE].start);
|
||||
}
|
||||
|
||||
return dev->irq;
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_pdc202xx(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
/* PDC20265 has problems with large LBA48 requests */
|
||||
if ((dev->device == PCI_DEVICE_ID_PROMISE_20267) ||
|
||||
(dev->device == PCI_DEVICE_ID_PROMISE_20265))
|
||||
hwif->rqsize = 256;
|
||||
|
||||
hwif->autodma = 0;
|
||||
hwif->tuneproc = &pdc202xx_tune_drive;
|
||||
hwif->quirkproc = &pdc202xx_quirkproc;
|
||||
|
||||
if (hwif->pci_dev->device != PCI_DEVICE_ID_PROMISE_20246)
|
||||
hwif->resetproc = &pdc202xx_reset;
|
||||
|
||||
hwif->speedproc = &pdc202xx_tune_chipset;
|
||||
|
||||
hwif->drives[0].autotune = hwif->drives[1].autotune = 1;
|
||||
|
||||
hwif->ultra_mask = 0x3f;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
hwif->swdma_mask = 0x07;
|
||||
hwif->atapi_dma = 1;
|
||||
|
||||
hwif->err_stops_fifo = 1;
|
||||
|
||||
hwif->ide_dma_check = &pdc202xx_config_drive_xfer_rate;
|
||||
hwif->ide_dma_lostirq = &pdc202xx_ide_dma_lostirq;
|
||||
hwif->ide_dma_timeout = &pdc202xx_ide_dma_timeout;
|
||||
|
||||
if (hwif->pci_dev->device != PCI_DEVICE_ID_PROMISE_20246) {
|
||||
if (!(hwif->udma_four))
|
||||
hwif->udma_four = (pdc202xx_old_cable_detect(hwif)) ? 0 : 1;
|
||||
hwif->dma_start = &pdc202xx_old_ide_dma_start;
|
||||
hwif->ide_dma_end = &pdc202xx_old_ide_dma_end;
|
||||
}
|
||||
hwif->ide_dma_test_irq = &pdc202xx_old_ide_dma_test_irq;
|
||||
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->drives[1].autodma = hwif->autodma;
|
||||
#if PDC202_DEBUG_CABLE
|
||||
printk(KERN_DEBUG "%s: %s-pin cable\n",
|
||||
hwif->name, hwif->udma_four ? "80" : "40");
|
||||
#endif /* PDC202_DEBUG_CABLE */
|
||||
}
|
||||
|
||||
static void __devinit init_dma_pdc202xx(ide_hwif_t *hwif, unsigned long dmabase)
|
||||
{
|
||||
u8 udma_speed_flag = 0, primary_mode = 0, secondary_mode = 0;
|
||||
|
||||
if (hwif->channel) {
|
||||
ide_setup_dma(hwif, dmabase, 8);
|
||||
return;
|
||||
}
|
||||
|
||||
udma_speed_flag = inb(dmabase | 0x1f);
|
||||
primary_mode = inb(dmabase | 0x1a);
|
||||
secondary_mode = inb(dmabase | 0x1b);
|
||||
printk(KERN_INFO "%s: (U)DMA Burst Bit %sABLED " \
|
||||
"Primary %s Mode " \
|
||||
"Secondary %s Mode.\n", hwif->cds->name,
|
||||
(udma_speed_flag & 1) ? "EN" : "DIS",
|
||||
(primary_mode & 1) ? "MASTER" : "PCI",
|
||||
(secondary_mode & 1) ? "MASTER" : "PCI" );
|
||||
|
||||
#ifdef CONFIG_PDC202XX_BURST
|
||||
if (!(udma_speed_flag & 1)) {
|
||||
printk(KERN_INFO "%s: FORCING BURST BIT 0x%02x->0x%02x ",
|
||||
hwif->cds->name, udma_speed_flag,
|
||||
(udma_speed_flag|1));
|
||||
outb(udma_speed_flag | 1, dmabase | 0x1f);
|
||||
printk("%sACTIVE\n", (inb(dmabase | 0x1f) & 1) ? "" : "IN");
|
||||
}
|
||||
#endif /* CONFIG_PDC202XX_BURST */
|
||||
|
||||
ide_setup_dma(hwif, dmabase, 8);
|
||||
}
|
||||
|
||||
static int __devinit init_setup_pdc202ata4(struct pci_dev *dev,
|
||||
ide_pci_device_t *d)
|
||||
{
|
||||
if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) {
|
||||
u8 irq = 0, irq2 = 0;
|
||||
pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
|
||||
/* 0xbc */
|
||||
pci_read_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, &irq2);
|
||||
if (irq != irq2) {
|
||||
pci_write_config_byte(dev,
|
||||
(PCI_INTERRUPT_LINE)|0x80, irq); /* 0xbc */
|
||||
printk(KERN_INFO "%s: pci-config space interrupt "
|
||||
"mirror fixed.\n", d->name);
|
||||
}
|
||||
}
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
static int __devinit init_setup_pdc20265(struct pci_dev *dev,
|
||||
ide_pci_device_t *d)
|
||||
{
|
||||
if ((dev->bus->self) &&
|
||||
(dev->bus->self->vendor == PCI_VENDOR_ID_INTEL) &&
|
||||
((dev->bus->self->device == PCI_DEVICE_ID_INTEL_I960) ||
|
||||
(dev->bus->self->device == PCI_DEVICE_ID_INTEL_I960RM))) {
|
||||
printk(KERN_INFO "ide: Skipping Promise PDC20265 "
|
||||
"attached to I2O RAID controller.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
static int __devinit init_setup_pdc202xx(struct pci_dev *dev,
|
||||
ide_pci_device_t *d)
|
||||
{
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
static ide_pci_device_t pdc202xx_chipsets[] __devinitdata = {
|
||||
{ /* 0 */
|
||||
.name = "PDC20246",
|
||||
.init_setup = init_setup_pdc202ata4,
|
||||
.init_chipset = init_chipset_pdc202xx,
|
||||
.init_hwif = init_hwif_pdc202xx,
|
||||
.init_dma = init_dma_pdc202xx,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
.extra = 16,
|
||||
},{ /* 1 */
|
||||
.name = "PDC20262",
|
||||
.init_setup = init_setup_pdc202ata4,
|
||||
.init_chipset = init_chipset_pdc202xx,
|
||||
.init_hwif = init_hwif_pdc202xx,
|
||||
.init_dma = init_dma_pdc202xx,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
.extra = 48,
|
||||
},{ /* 2 */
|
||||
.name = "PDC20263",
|
||||
.init_setup = init_setup_pdc202ata4,
|
||||
.init_chipset = init_chipset_pdc202xx,
|
||||
.init_hwif = init_hwif_pdc202xx,
|
||||
.init_dma = init_dma_pdc202xx,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
.extra = 48,
|
||||
},{ /* 3 */
|
||||
.name = "PDC20265",
|
||||
.init_setup = init_setup_pdc20265,
|
||||
.init_chipset = init_chipset_pdc202xx,
|
||||
.init_hwif = init_hwif_pdc202xx,
|
||||
.init_dma = init_dma_pdc202xx,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
.extra = 48,
|
||||
},{ /* 4 */
|
||||
.name = "PDC20267",
|
||||
.init_setup = init_setup_pdc202xx,
|
||||
.init_chipset = init_chipset_pdc202xx,
|
||||
.init_hwif = init_hwif_pdc202xx,
|
||||
.init_dma = init_dma_pdc202xx,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD,
|
||||
.extra = 48,
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* pdc202xx_init_one - called when a PDC202xx is found
|
||||
* @dev: the pdc202xx device
|
||||
* @id: the matching pci id
|
||||
*
|
||||
* Called when the PCI registration layer (or the IDE initialization)
|
||||
* finds a device matching our IDE device tables.
|
||||
*/
|
||||
|
||||
static int __devinit pdc202xx_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
ide_pci_device_t *d = &pdc202xx_chipsets[id->driver_data];
|
||||
|
||||
return d->init_setup(dev, d);
|
||||
}
|
||||
|
||||
static struct pci_device_id pdc202xx_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
|
||||
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20263, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2},
|
||||
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20265, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3},
|
||||
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20267, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pdc202xx_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "Promise_Old_IDE",
|
||||
.id_table = pdc202xx_pci_tbl,
|
||||
.probe = pdc202xx_init_one,
|
||||
};
|
||||
|
||||
static int __init pdc202xx_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(pdc202xx_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick, Frank Tiernan");
|
||||
MODULE_DESCRIPTION("PCI driver module for older Promise IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
709
drivers/ide/pci/piix.c
Normal file
709
drivers/ide/pci/piix.c
Normal file
@@ -0,0 +1,709 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/piix.c Version 0.47 February 8, 2007
|
||||
*
|
||||
* Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
|
||||
* Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2003 Red Hat Inc <alan@redhat.com>
|
||||
* Copyright (C) 2006-2007 MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*
|
||||
* PIO mode setting function for Intel chipsets.
|
||||
* For use instead of BIOS settings.
|
||||
*
|
||||
* 40-41
|
||||
* 42-43
|
||||
*
|
||||
* 41
|
||||
* 43
|
||||
*
|
||||
* | PIO 0 | c0 | 80 | 0 | piix_tune_drive(drive, 0);
|
||||
* | PIO 2 | SW2 | d0 | 90 | 4 | piix_tune_drive(drive, 2);
|
||||
* | PIO 3 | MW1 | e1 | a1 | 9 | piix_tune_drive(drive, 3);
|
||||
* | PIO 4 | MW2 | e3 | a3 | b | piix_tune_drive(drive, 4);
|
||||
*
|
||||
* sitre = word40 & 0x4000; primary
|
||||
* sitre = word42 & 0x4000; secondary
|
||||
*
|
||||
* 44 8421|8421 hdd|hdb
|
||||
*
|
||||
* 48 8421 hdd|hdc|hdb|hda udma enabled
|
||||
*
|
||||
* 0001 hda
|
||||
* 0010 hdb
|
||||
* 0100 hdc
|
||||
* 1000 hdd
|
||||
*
|
||||
* 4a 84|21 hdb|hda
|
||||
* 4b 84|21 hdd|hdc
|
||||
*
|
||||
* ata-33/82371AB
|
||||
* ata-33/82371EB
|
||||
* ata-33/82801AB ata-66/82801AA
|
||||
* 00|00 udma 0 00|00 reserved
|
||||
* 01|01 udma 1 01|01 udma 3
|
||||
* 10|10 udma 2 10|10 udma 4
|
||||
* 11|11 reserved 11|11 reserved
|
||||
*
|
||||
* 54 8421|8421 ata66 drive|ata66 enable
|
||||
*
|
||||
* pci_read_config_word(HWIF(drive)->pci_dev, 0x40, ®40);
|
||||
* pci_read_config_word(HWIF(drive)->pci_dev, 0x42, ®42);
|
||||
* pci_read_config_word(HWIF(drive)->pci_dev, 0x44, ®44);
|
||||
* pci_read_config_byte(HWIF(drive)->pci_dev, 0x48, ®48);
|
||||
* pci_read_config_word(HWIF(drive)->pci_dev, 0x4a, ®4a);
|
||||
* pci_read_config_byte(HWIF(drive)->pci_dev, 0x54, ®54);
|
||||
*
|
||||
* Documentation
|
||||
* Publically available from Intel web site. Errata documentation
|
||||
* is also publically available. As an aide to anyone hacking on this
|
||||
* driver the list of errata that are relevant is below.going back to
|
||||
* PIIX4. Older device documentation is now a bit tricky to find.
|
||||
*
|
||||
* Errata of note:
|
||||
*
|
||||
* Unfixable
|
||||
* PIIX4 errata #9 - Only on ultra obscure hw
|
||||
* ICH3 errata #13 - Not observed to affect real hw
|
||||
* by Intel
|
||||
*
|
||||
* Things we must deal with
|
||||
* PIIX4 errata #10 - BM IDE hang with non UDMA
|
||||
* (must stop/start dma to recover)
|
||||
* 440MX errata #15 - As PIIX4 errata #10
|
||||
* PIIX4 errata #15 - Must not read control registers
|
||||
* during a PIO transfer
|
||||
* 440MX errata #13 - As PIIX4 errata #15
|
||||
* ICH2 errata #21 - DMA mode 0 doesn't work right
|
||||
* ICH0/1 errata #55 - As ICH2 errata #21
|
||||
* ICH2 spec c #9 - Extra operations needed to handle
|
||||
* drive hotswap [NOT YET SUPPORTED]
|
||||
* ICH2 spec c #20 - IDE PRD must not cross a 64K boundary
|
||||
* and must be dword aligned
|
||||
* ICH2 spec c #24 - UDMA mode 4,5 t85/86 should be 6ns not 3.3
|
||||
*
|
||||
* Should have been BIOS fixed:
|
||||
* 450NX: errata #19 - DMA hangs on old 450NX
|
||||
* 450NX: errata #20 - DMA hangs on old 450NX
|
||||
* 450NX: errata #25 - Corruption with DMA on old 450NX
|
||||
* ICH3 errata #15 - IDE deadlock under high load
|
||||
* (BIOS must set dev 31 fn 0 bit 23)
|
||||
* ICH3 errata #18 - Don't use native mode
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
static int no_piix_dma;
|
||||
|
||||
/**
|
||||
* piix_ratemask - compute rate mask for PIIX IDE
|
||||
* @drive: IDE drive to compute for
|
||||
*
|
||||
* Returns the available modes for the PIIX IDE controller.
|
||||
*/
|
||||
|
||||
static u8 piix_ratemask (ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = HWIF(drive)->pci_dev;
|
||||
u8 mode;
|
||||
|
||||
switch(dev->device) {
|
||||
case PCI_DEVICE_ID_INTEL_82801EB_1:
|
||||
mode = 3;
|
||||
break;
|
||||
/* UDMA 100 capable */
|
||||
case PCI_DEVICE_ID_INTEL_82801BA_8:
|
||||
case PCI_DEVICE_ID_INTEL_82801BA_9:
|
||||
case PCI_DEVICE_ID_INTEL_82801CA_10:
|
||||
case PCI_DEVICE_ID_INTEL_82801CA_11:
|
||||
case PCI_DEVICE_ID_INTEL_82801E_11:
|
||||
case PCI_DEVICE_ID_INTEL_82801DB_1:
|
||||
case PCI_DEVICE_ID_INTEL_82801DB_10:
|
||||
case PCI_DEVICE_ID_INTEL_82801DB_11:
|
||||
case PCI_DEVICE_ID_INTEL_82801EB_11:
|
||||
case PCI_DEVICE_ID_INTEL_ESB_2:
|
||||
case PCI_DEVICE_ID_INTEL_ICH6_19:
|
||||
case PCI_DEVICE_ID_INTEL_ICH7_21:
|
||||
case PCI_DEVICE_ID_INTEL_ESB2_18:
|
||||
case PCI_DEVICE_ID_INTEL_ICH8_6:
|
||||
mode = 3;
|
||||
break;
|
||||
/* UDMA 66 capable */
|
||||
case PCI_DEVICE_ID_INTEL_82801AA_1:
|
||||
case PCI_DEVICE_ID_INTEL_82372FB_1:
|
||||
mode = 2;
|
||||
break;
|
||||
/* UDMA 33 capable */
|
||||
case PCI_DEVICE_ID_INTEL_82371AB:
|
||||
case PCI_DEVICE_ID_INTEL_82443MX_1:
|
||||
case PCI_DEVICE_ID_INTEL_82451NX:
|
||||
case PCI_DEVICE_ID_INTEL_82801AB_1:
|
||||
return 1;
|
||||
/* Non UDMA capable (MWDMA2) */
|
||||
case PCI_DEVICE_ID_INTEL_82371SB_1:
|
||||
case PCI_DEVICE_ID_INTEL_82371FB_1:
|
||||
case PCI_DEVICE_ID_INTEL_82371FB_0:
|
||||
case PCI_DEVICE_ID_INTEL_82371MX:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are UDMA66 capable fall back to UDMA33
|
||||
* if the drive cannot see an 80pin cable.
|
||||
*/
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min_t(u8, mode, 1);
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* piix_dma_2_pio - return the PIO mode matching DMA
|
||||
* @xfer_rate: transfer speed
|
||||
*
|
||||
* Returns the nearest equivalent PIO timing for the PIO or DMA
|
||||
* mode requested by the controller.
|
||||
*/
|
||||
|
||||
static u8 piix_dma_2_pio (u8 xfer_rate) {
|
||||
switch(xfer_rate) {
|
||||
case XFER_UDMA_6:
|
||||
case XFER_UDMA_5:
|
||||
case XFER_UDMA_4:
|
||||
case XFER_UDMA_3:
|
||||
case XFER_UDMA_2:
|
||||
case XFER_UDMA_1:
|
||||
case XFER_UDMA_0:
|
||||
case XFER_MW_DMA_2:
|
||||
case XFER_PIO_4:
|
||||
return 4;
|
||||
case XFER_MW_DMA_1:
|
||||
case XFER_PIO_3:
|
||||
return 3;
|
||||
case XFER_SW_DMA_2:
|
||||
case XFER_PIO_2:
|
||||
return 2;
|
||||
case XFER_MW_DMA_0:
|
||||
case XFER_SW_DMA_1:
|
||||
case XFER_SW_DMA_0:
|
||||
case XFER_PIO_1:
|
||||
case XFER_PIO_0:
|
||||
case XFER_PIO_SLOW:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* piix_tune_pio - tune PIIX for PIO mode
|
||||
* @drive: drive to tune
|
||||
* @pio: desired PIO mode
|
||||
*
|
||||
* Set the interface PIO mode based upon the settings done by AMI BIOS.
|
||||
*/
|
||||
static void piix_tune_pio (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
int is_slave = drive->dn & 1;
|
||||
int master_port = hwif->channel ? 0x42 : 0x40;
|
||||
int slave_port = 0x44;
|
||||
unsigned long flags;
|
||||
u16 master_data;
|
||||
u8 slave_data;
|
||||
static DEFINE_SPINLOCK(tune_lock);
|
||||
int control = 0;
|
||||
|
||||
/* ISP RTC */
|
||||
static const u8 timings[][2]= {
|
||||
{ 0, 0 },
|
||||
{ 0, 0 },
|
||||
{ 1, 0 },
|
||||
{ 2, 1 },
|
||||
{ 2, 3 }, };
|
||||
|
||||
/*
|
||||
* Master vs slave is synchronized above us but the slave register is
|
||||
* shared by the two hwifs so the corner case of two slave timeouts in
|
||||
* parallel must be locked.
|
||||
*/
|
||||
spin_lock_irqsave(&tune_lock, flags);
|
||||
pci_read_config_word(dev, master_port, &master_data);
|
||||
|
||||
if (pio > 1)
|
||||
control |= 1; /* Programmable timing on */
|
||||
if (drive->media == ide_disk)
|
||||
control |= 4; /* Prefetch, post write */
|
||||
if (pio > 2)
|
||||
control |= 2; /* IORDY */
|
||||
if (is_slave) {
|
||||
master_data |= 0x4000;
|
||||
master_data &= ~0x0070;
|
||||
if (pio > 1) {
|
||||
/* Set PPE, IE and TIME */
|
||||
master_data |= control << 4;
|
||||
}
|
||||
pci_read_config_byte(dev, slave_port, &slave_data);
|
||||
slave_data &= hwif->channel ? 0x0f : 0xf0;
|
||||
slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) <<
|
||||
(hwif->channel ? 4 : 0);
|
||||
} else {
|
||||
master_data &= ~0x3307;
|
||||
if (pio > 1) {
|
||||
/* enable PPE, IE and TIME */
|
||||
master_data |= control;
|
||||
}
|
||||
master_data |= (timings[pio][0] << 12) | (timings[pio][1] << 8);
|
||||
}
|
||||
pci_write_config_word(dev, master_port, master_data);
|
||||
if (is_slave)
|
||||
pci_write_config_byte(dev, slave_port, slave_data);
|
||||
spin_unlock_irqrestore(&tune_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* piix_tune_drive - tune a drive attached to PIIX
|
||||
* @drive: drive to tune
|
||||
* @pio: desired PIO mode
|
||||
*
|
||||
* Set the drive's PIO mode (might be useful if drive is not registered
|
||||
* in CMOS for any reason).
|
||||
*/
|
||||
static void piix_tune_drive (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
|
||||
piix_tune_pio(drive, pio);
|
||||
(void) ide_config_drive_speed(drive, XFER_PIO_0 + pio);
|
||||
}
|
||||
|
||||
/**
|
||||
* piix_tune_chipset - tune a PIIX interface
|
||||
* @drive: IDE drive to tune
|
||||
* @xferspeed: speed to configure
|
||||
*
|
||||
* Set a PIIX interface channel to the desired speeds. This involves
|
||||
* requires the right timing data into the PIIX configuration space
|
||||
* then setting the drive parameters appropriately
|
||||
*/
|
||||
|
||||
static int piix_tune_chipset (ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u8 maslave = hwif->channel ? 0x42 : 0x40;
|
||||
u8 speed = ide_rate_filter(piix_ratemask(drive), xferspeed);
|
||||
int a_speed = 3 << (drive->dn * 4);
|
||||
int u_flag = 1 << drive->dn;
|
||||
int v_flag = 0x01 << drive->dn;
|
||||
int w_flag = 0x10 << drive->dn;
|
||||
int u_speed = 0;
|
||||
int sitre;
|
||||
u16 reg4042, reg4a;
|
||||
u8 reg48, reg54, reg55;
|
||||
|
||||
pci_read_config_word(dev, maslave, ®4042);
|
||||
sitre = (reg4042 & 0x4000) ? 1 : 0;
|
||||
pci_read_config_byte(dev, 0x48, ®48);
|
||||
pci_read_config_word(dev, 0x4a, ®4a);
|
||||
pci_read_config_byte(dev, 0x54, ®54);
|
||||
pci_read_config_byte(dev, 0x55, ®55);
|
||||
|
||||
switch(speed) {
|
||||
case XFER_UDMA_4:
|
||||
case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break;
|
||||
case XFER_UDMA_5:
|
||||
case XFER_UDMA_3:
|
||||
case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break;
|
||||
case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break;
|
||||
case XFER_MW_DMA_2:
|
||||
case XFER_MW_DMA_1:
|
||||
case XFER_SW_DMA_2: break;
|
||||
case XFER_PIO_4:
|
||||
case XFER_PIO_3:
|
||||
case XFER_PIO_2:
|
||||
case XFER_PIO_0: break;
|
||||
default: return -1;
|
||||
}
|
||||
|
||||
if (speed >= XFER_UDMA_0) {
|
||||
if (!(reg48 & u_flag))
|
||||
pci_write_config_byte(dev, 0x48, reg48 | u_flag);
|
||||
if (speed == XFER_UDMA_5) {
|
||||
pci_write_config_byte(dev, 0x55, (u8) reg55|w_flag);
|
||||
} else {
|
||||
pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
|
||||
}
|
||||
if ((reg4a & a_speed) != u_speed)
|
||||
pci_write_config_word(dev, 0x4a, (reg4a & ~a_speed) | u_speed);
|
||||
if (speed > XFER_UDMA_2) {
|
||||
if (!(reg54 & v_flag))
|
||||
pci_write_config_byte(dev, 0x54, reg54 | v_flag);
|
||||
} else
|
||||
pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
|
||||
} else {
|
||||
if (reg48 & u_flag)
|
||||
pci_write_config_byte(dev, 0x48, reg48 & ~u_flag);
|
||||
if (reg4a & a_speed)
|
||||
pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
|
||||
if (reg54 & v_flag)
|
||||
pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
|
||||
if (reg55 & w_flag)
|
||||
pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
|
||||
}
|
||||
|
||||
piix_tune_pio(drive, piix_dma_2_pio(speed));
|
||||
return ide_config_drive_speed(drive, speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* piix_config_drive_for_dma - configure drive for DMA
|
||||
* @drive: IDE drive to configure
|
||||
*
|
||||
* Set up a PIIX interface channel for the best available speed.
|
||||
* We prefer UDMA if it is available and then MWDMA. If DMA is
|
||||
* not available we switch to PIO and return 0.
|
||||
*/
|
||||
|
||||
static int piix_config_drive_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, piix_ratemask(drive));
|
||||
|
||||
/*
|
||||
* If no DMA speed was available or the chipset has DMA bugs
|
||||
* then disable DMA and use PIO
|
||||
*/
|
||||
if (!speed)
|
||||
return 0;
|
||||
|
||||
(void) piix_tune_chipset(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
/**
|
||||
* piix_config_drive_xfer_rate - set up an IDE device
|
||||
* @drive: IDE drive to configure
|
||||
*
|
||||
* Set up the PIIX interface for the best available speed on this
|
||||
* interface, preferring DMA to PIO.
|
||||
*/
|
||||
|
||||
static int piix_config_drive_xfer_rate (ide_drive_t *drive)
|
||||
{
|
||||
drive->init_speed = 0;
|
||||
|
||||
if (ide_use_dma(drive) && piix_config_drive_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
if (ide_use_fast_pio(drive))
|
||||
piix_tune_drive(drive, 255);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* piix_is_ichx - check if ICHx
|
||||
* @dev: PCI device to check
|
||||
*
|
||||
* returns 1 if ICHx, 0 otherwise.
|
||||
*/
|
||||
static int piix_is_ichx(struct pci_dev *dev)
|
||||
{
|
||||
switch (dev->device) {
|
||||
case PCI_DEVICE_ID_INTEL_82801EB_1:
|
||||
case PCI_DEVICE_ID_INTEL_82801AA_1:
|
||||
case PCI_DEVICE_ID_INTEL_82801AB_1:
|
||||
case PCI_DEVICE_ID_INTEL_82801BA_8:
|
||||
case PCI_DEVICE_ID_INTEL_82801BA_9:
|
||||
case PCI_DEVICE_ID_INTEL_82801CA_10:
|
||||
case PCI_DEVICE_ID_INTEL_82801CA_11:
|
||||
case PCI_DEVICE_ID_INTEL_82801DB_1:
|
||||
case PCI_DEVICE_ID_INTEL_82801DB_10:
|
||||
case PCI_DEVICE_ID_INTEL_82801DB_11:
|
||||
case PCI_DEVICE_ID_INTEL_82801EB_11:
|
||||
case PCI_DEVICE_ID_INTEL_82801E_11:
|
||||
case PCI_DEVICE_ID_INTEL_ESB_2:
|
||||
case PCI_DEVICE_ID_INTEL_ICH6_19:
|
||||
case PCI_DEVICE_ID_INTEL_ICH7_21:
|
||||
case PCI_DEVICE_ID_INTEL_ESB2_18:
|
||||
case PCI_DEVICE_ID_INTEL_ICH8_6:
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_chipset_piix - set up the PIIX chipset
|
||||
* @dev: PCI device to set up
|
||||
* @name: Name of the device
|
||||
*
|
||||
* Initialize the PCI device as required. For the PIIX this turns
|
||||
* out to be nice and simple
|
||||
*/
|
||||
|
||||
static unsigned int __devinit init_chipset_piix (struct pci_dev *dev, const char *name)
|
||||
{
|
||||
if (piix_is_ichx(dev)) {
|
||||
unsigned int extra = 0;
|
||||
pci_read_config_dword(dev, 0x54, &extra);
|
||||
pci_write_config_dword(dev, 0x54, extra|0x400);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* piix_dma_clear_irq - clear BMDMA status
|
||||
* @drive: IDE drive to clear
|
||||
*
|
||||
* Called from ide_intr() for PIO interrupts
|
||||
* to clear BMDMA status as needed by ICHx
|
||||
*/
|
||||
static void piix_dma_clear_irq(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 dma_stat;
|
||||
|
||||
/* clear the INTR & ERROR bits */
|
||||
dma_stat = hwif->INB(hwif->dma_status);
|
||||
/* Should we force the bit as well ? */
|
||||
hwif->OUTB(dma_stat, hwif->dma_status);
|
||||
}
|
||||
|
||||
static int __devinit piix_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u8 reg54h = 0, mask = hwif->channel ? 0xc0 : 0x30;
|
||||
|
||||
pci_read_config_byte(dev, 0x54, ®54h);
|
||||
|
||||
return (reg54h & mask) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_hwif_piix - fill in the hwif for the PIIX
|
||||
* @hwif: IDE interface
|
||||
*
|
||||
* Set up the ide_hwif_t for the PIIX interface according to the
|
||||
* capabilities of the hardware.
|
||||
*/
|
||||
|
||||
static void __devinit init_hwif_piix(ide_hwif_t *hwif)
|
||||
{
|
||||
#ifndef CONFIG_IA64
|
||||
if (!hwif->irq)
|
||||
hwif->irq = hwif->channel ? 15 : 14;
|
||||
#endif /* CONFIG_IA64 */
|
||||
|
||||
if (hwif->pci_dev->device == PCI_DEVICE_ID_INTEL_82371MX) {
|
||||
/* This is a painful system best to let it self tune for now */
|
||||
return;
|
||||
}
|
||||
|
||||
hwif->autodma = 0;
|
||||
hwif->tuneproc = &piix_tune_drive;
|
||||
hwif->speedproc = &piix_tune_chipset;
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].autotune = 1;
|
||||
|
||||
if (!hwif->dma_base)
|
||||
return;
|
||||
|
||||
/* ICHx need to clear the bmdma status for all interrupts */
|
||||
if (piix_is_ichx(hwif->pci_dev))
|
||||
hwif->ide_dma_clear_irq = &piix_dma_clear_irq;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x3f;
|
||||
hwif->mwdma_mask = 0x06;
|
||||
hwif->swdma_mask = 0x04;
|
||||
|
||||
switch(hwif->pci_dev->device) {
|
||||
case PCI_DEVICE_ID_INTEL_82371FB_0:
|
||||
case PCI_DEVICE_ID_INTEL_82371FB_1:
|
||||
case PCI_DEVICE_ID_INTEL_82371SB_1:
|
||||
hwif->ultra_mask = 0x80;
|
||||
break;
|
||||
case PCI_DEVICE_ID_INTEL_82371AB:
|
||||
case PCI_DEVICE_ID_INTEL_82443MX_1:
|
||||
case PCI_DEVICE_ID_INTEL_82451NX:
|
||||
case PCI_DEVICE_ID_INTEL_82801AB_1:
|
||||
hwif->ultra_mask = 0x07;
|
||||
break;
|
||||
default:
|
||||
if (!hwif->udma_four)
|
||||
hwif->udma_four = piix_cable_detect(hwif);
|
||||
break;
|
||||
}
|
||||
|
||||
if (no_piix_dma)
|
||||
hwif->ultra_mask = hwif->mwdma_mask = hwif->swdma_mask = 0;
|
||||
|
||||
hwif->ide_dma_check = &piix_config_drive_xfer_rate;
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
#define DECLARE_PIIX_DEV(name_str) \
|
||||
{ \
|
||||
.name = name_str, \
|
||||
.init_chipset = init_chipset_piix, \
|
||||
.init_hwif = init_hwif_piix, \
|
||||
.channels = 2, \
|
||||
.autodma = AUTODMA, \
|
||||
.enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \
|
||||
.bootable = ON_BOARD, \
|
||||
}
|
||||
|
||||
static ide_pci_device_t piix_pci_info[] __devinitdata = {
|
||||
/* 0 */ DECLARE_PIIX_DEV("PIIXa"),
|
||||
/* 1 */ DECLARE_PIIX_DEV("PIIXb"),
|
||||
|
||||
/* 2 */
|
||||
{ /*
|
||||
* MPIIX actually has only a single IDE channel mapped to
|
||||
* the primary or secondary ports depending on the value
|
||||
* of the bit 14 of the IDETIM register at offset 0x6c
|
||||
*/
|
||||
.name = "MPIIX",
|
||||
.init_hwif = init_hwif_piix,
|
||||
.channels = 2,
|
||||
.autodma = NODMA,
|
||||
.enablebits = {{0x6d,0xc0,0x80}, {0x6d,0xc0,0xc0}},
|
||||
.bootable = ON_BOARD,
|
||||
.flags = IDEPCI_FLAG_ISA_PORTS
|
||||
},
|
||||
|
||||
/* 3 */ DECLARE_PIIX_DEV("PIIX3"),
|
||||
/* 4 */ DECLARE_PIIX_DEV("PIIX4"),
|
||||
/* 5 */ DECLARE_PIIX_DEV("ICH0"),
|
||||
/* 6 */ DECLARE_PIIX_DEV("PIIX4"),
|
||||
/* 7 */ DECLARE_PIIX_DEV("ICH"),
|
||||
/* 8 */ DECLARE_PIIX_DEV("PIIX4"),
|
||||
/* 9 */ DECLARE_PIIX_DEV("PIIX4"),
|
||||
/* 10 */ DECLARE_PIIX_DEV("ICH2"),
|
||||
/* 11 */ DECLARE_PIIX_DEV("ICH2M"),
|
||||
/* 12 */ DECLARE_PIIX_DEV("ICH3M"),
|
||||
/* 13 */ DECLARE_PIIX_DEV("ICH3"),
|
||||
/* 14 */ DECLARE_PIIX_DEV("ICH4"),
|
||||
/* 15 */ DECLARE_PIIX_DEV("ICH5"),
|
||||
/* 16 */ DECLARE_PIIX_DEV("C-ICH"),
|
||||
/* 17 */ DECLARE_PIIX_DEV("ICH4"),
|
||||
/* 18 */ DECLARE_PIIX_DEV("ICH5-SATA"),
|
||||
/* 19 */ DECLARE_PIIX_DEV("ICH5"),
|
||||
/* 20 */ DECLARE_PIIX_DEV("ICH6"),
|
||||
/* 21 */ DECLARE_PIIX_DEV("ICH7"),
|
||||
/* 22 */ DECLARE_PIIX_DEV("ICH4"),
|
||||
/* 23 */ DECLARE_PIIX_DEV("ESB2"),
|
||||
/* 24 */ DECLARE_PIIX_DEV("ICH8M"),
|
||||
};
|
||||
|
||||
/**
|
||||
* piix_init_one - called when a PIIX is found
|
||||
* @dev: the piix device
|
||||
* @id: the matching pci id
|
||||
*
|
||||
* Called when the PCI registration layer (or the IDE initialization)
|
||||
* finds a device matching our IDE device tables.
|
||||
*/
|
||||
|
||||
static int __devinit piix_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
ide_pci_device_t *d = &piix_pci_info[id->driver_data];
|
||||
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
/**
|
||||
* piix_check_450nx - Check for problem 450NX setup
|
||||
*
|
||||
* Check for the present of 450NX errata #19 and errata #25. If
|
||||
* they are found, disable use of DMA IDE
|
||||
*/
|
||||
|
||||
static void __devinit piix_check_450nx(void)
|
||||
{
|
||||
struct pci_dev *pdev = NULL;
|
||||
u16 cfg;
|
||||
u8 rev;
|
||||
while((pdev=pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, pdev))!=NULL)
|
||||
{
|
||||
/* Look for 450NX PXB. Check for problem configurations
|
||||
A PCI quirk checks bit 6 already */
|
||||
pci_read_config_byte(pdev, PCI_REVISION_ID, &rev);
|
||||
pci_read_config_word(pdev, 0x41, &cfg);
|
||||
/* Only on the original revision: IDE DMA can hang */
|
||||
if(rev == 0x00)
|
||||
no_piix_dma = 1;
|
||||
/* On all revisions below 5 PXB bus lock must be disabled for IDE */
|
||||
else if(cfg & (1<<14) && rev < 5)
|
||||
no_piix_dma = 2;
|
||||
}
|
||||
if(no_piix_dma)
|
||||
printk(KERN_WARNING "piix: 450NX errata present, disabling IDE DMA.\n");
|
||||
if(no_piix_dma == 2)
|
||||
printk(KERN_WARNING "piix: A BIOS update may resolve this.\n");
|
||||
}
|
||||
|
||||
static struct pci_device_id piix_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82372FB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_11,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_11,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 14},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_11,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 15},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_11, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 16},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_10,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 17},
|
||||
#ifdef CONFIG_BLK_DEV_IDE_SATA
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 18},
|
||||
#endif
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 19},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_19, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 20},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_21, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 21},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 22},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 23},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 24},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, piix_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "PIIX_IDE",
|
||||
.id_table = piix_pci_tbl,
|
||||
.probe = piix_init_one,
|
||||
};
|
||||
|
||||
static int __init piix_ide_init(void)
|
||||
{
|
||||
piix_check_450nx();
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(piix_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick, Andrzej Krzysztofowicz");
|
||||
MODULE_DESCRIPTION("PCI driver module for Intel PIIX IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
88
drivers/ide/pci/rz1000.c
Normal file
88
drivers/ide/pci/rz1000.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/rz1000.c Version 0.06 January 12, 2003
|
||||
*
|
||||
* Copyright (C) 1995-1998 Linus Torvalds & author (see below)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Principal Author: mlord@pobox.com (Mark Lord)
|
||||
*
|
||||
* See linux/MAINTAINERS for address of current maintainer.
|
||||
*
|
||||
* This file provides support for disabling the buggy read-ahead
|
||||
* mode of the RZ1000 IDE chipset, commonly used on Intel motherboards.
|
||||
*
|
||||
* Dunno if this fixes both ports, or only the primary port (?).
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.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/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
static void __devinit init_hwif_rz1000 (ide_hwif_t *hwif)
|
||||
{
|
||||
u16 reg;
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
hwif->chipset = ide_rz1000;
|
||||
if (!pci_read_config_word (dev, 0x40, ®) &&
|
||||
!pci_write_config_word(dev, 0x40, reg & 0xdfff)) {
|
||||
printk(KERN_INFO "%s: disabled chipset read-ahead "
|
||||
"(buggy RZ1000/RZ1001)\n", hwif->name);
|
||||
} else {
|
||||
hwif->serialized = 1;
|
||||
hwif->drives[0].no_unmask = 1;
|
||||
hwif->drives[1].no_unmask = 1;
|
||||
printk(KERN_INFO "%s: serialized, disabled unmasking "
|
||||
"(buggy RZ1000/RZ1001)\n", hwif->name);
|
||||
}
|
||||
}
|
||||
|
||||
static ide_pci_device_t rz1000_chipset __devinitdata = {
|
||||
.name = "RZ100x",
|
||||
.init_hwif = init_hwif_rz1000,
|
||||
.channels = 2,
|
||||
.autodma = NODMA,
|
||||
.bootable = ON_BOARD,
|
||||
};
|
||||
|
||||
static int __devinit rz1000_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &rz1000_chipset);
|
||||
}
|
||||
|
||||
static struct pci_device_id rz1000_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, rz1000_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "RZ1000_IDE",
|
||||
.id_table = rz1000_pci_tbl,
|
||||
.probe = rz1000_init_one,
|
||||
};
|
||||
|
||||
static int __init rz1000_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(rz1000_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for RZ1000 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
516
drivers/ide/pci/sc1200.c
Normal file
516
drivers/ide/pci/sc1200.c
Normal file
@@ -0,0 +1,516 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/sc1200.c Version 0.91 28-Jan-2003
|
||||
*
|
||||
* Copyright (C) 2000-2002 Mark Lord <mlord@pobox.com>
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*
|
||||
* Development of this chipset driver was funded
|
||||
* by the nice folks at National Semiconductor.
|
||||
*
|
||||
* Documentation:
|
||||
* Available from National Semiconductor
|
||||
*/
|
||||
|
||||
#include <linux/module.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/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/pm.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#define SC1200_REV_A 0x00
|
||||
#define SC1200_REV_B1 0x01
|
||||
#define SC1200_REV_B3 0x02
|
||||
#define SC1200_REV_C1 0x03
|
||||
#define SC1200_REV_D1 0x04
|
||||
|
||||
#define PCI_CLK_33 0x00
|
||||
#define PCI_CLK_48 0x01
|
||||
#define PCI_CLK_66 0x02
|
||||
#define PCI_CLK_33A 0x03
|
||||
|
||||
static unsigned short sc1200_get_pci_clock (void)
|
||||
{
|
||||
unsigned char chip_id, silicon_revision;
|
||||
unsigned int pci_clock;
|
||||
/*
|
||||
* Check the silicon revision, as not all versions of the chip
|
||||
* have the register with the fast PCI bus timings.
|
||||
*/
|
||||
chip_id = inb (0x903c);
|
||||
silicon_revision = inb (0x903d);
|
||||
|
||||
// Read the fast pci clock frequency
|
||||
if (chip_id == 0x04 && silicon_revision < SC1200_REV_B1) {
|
||||
pci_clock = PCI_CLK_33;
|
||||
} else {
|
||||
// check clock generator configuration (cfcc)
|
||||
// the clock is in bits 8 and 9 of this word
|
||||
|
||||
pci_clock = inw (0x901e);
|
||||
pci_clock >>= 8;
|
||||
pci_clock &= 0x03;
|
||||
if (pci_clock == PCI_CLK_33A)
|
||||
pci_clock = PCI_CLK_33;
|
||||
}
|
||||
return pci_clock;
|
||||
}
|
||||
|
||||
extern char *ide_xfer_verbose (byte xfer_rate);
|
||||
|
||||
/*
|
||||
* Set a new transfer mode at the drive
|
||||
*/
|
||||
static int sc1200_set_xfer_mode (ide_drive_t *drive, byte mode)
|
||||
{
|
||||
printk("%s: sc1200_set_xfer_mode(%s)\n", drive->name, ide_xfer_verbose(mode));
|
||||
return ide_config_drive_speed(drive, mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Here are the standard PIO mode 0-4 timings for each "format".
|
||||
* Format-0 uses fast data reg timings, with slower command reg timings.
|
||||
* Format-1 uses fast timings for all registers, but won't work with all drives.
|
||||
*/
|
||||
static const unsigned int sc1200_pio_timings[4][5] =
|
||||
{{0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010}, // format0 33Mhz
|
||||
{0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010}, // format1, 33Mhz
|
||||
{0xfaa3f4f3, 0xc23232b2, 0x513101c1, 0x31213121, 0x10211021}, // format1, 48Mhz
|
||||
{0xfff4fff4, 0xf35353d3, 0x814102f1, 0x42314231, 0x11311131}}; // format1, 66Mhz
|
||||
|
||||
/*
|
||||
* After chip reset, the PIO timings are set to 0x00009172, which is not valid.
|
||||
*/
|
||||
//#define SC1200_BAD_PIO(timings) (((timings)&~0x80000000)==0x00009172)
|
||||
|
||||
static int sc1200_autoselect_dma_mode (ide_drive_t *drive)
|
||||
{
|
||||
int udma_ok = 1, mode = 0;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
int unit = drive->select.b.unit;
|
||||
ide_drive_t *mate = &hwif->drives[unit^1];
|
||||
struct hd_driveid *id = drive->id;
|
||||
|
||||
/*
|
||||
* The SC1200 specifies that two drives sharing a cable cannot
|
||||
* mix UDMA/MDMA. It has to be one or the other, for the pair,
|
||||
* though different timings can still be chosen for each drive.
|
||||
* We could set the appropriate timing bits on the fly,
|
||||
* but that might be a bit confusing. So, for now we statically
|
||||
* handle this requirement by looking at our mate drive to see
|
||||
* what it is capable of, before choosing a mode for our own drive.
|
||||
*/
|
||||
if (mate->present) {
|
||||
struct hd_driveid *mateid = mate->id;
|
||||
if (mateid && (mateid->capability & 1) && !__ide_dma_bad_drive(mate)) {
|
||||
if ((mateid->field_valid & 4) && (mateid->dma_ultra & 7))
|
||||
udma_ok = 1;
|
||||
else if ((mateid->field_valid & 2) && (mateid->dma_mword & 7))
|
||||
udma_ok = 0;
|
||||
else
|
||||
udma_ok = 1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Now see what the current drive is capable of,
|
||||
* selecting UDMA only if the mate said it was ok.
|
||||
*/
|
||||
if (id && (id->capability & 1) && hwif->autodma && !__ide_dma_bad_drive(drive)) {
|
||||
if (udma_ok && (id->field_valid & 4) && (id->dma_ultra & 7)) {
|
||||
if (id->dma_ultra & 4)
|
||||
mode = XFER_UDMA_2;
|
||||
else if (id->dma_ultra & 2)
|
||||
mode = XFER_UDMA_1;
|
||||
else if (id->dma_ultra & 1)
|
||||
mode = XFER_UDMA_0;
|
||||
}
|
||||
if (!mode && (id->field_valid & 2) && (id->dma_mword & 7)) {
|
||||
if (id->dma_mword & 4)
|
||||
mode = XFER_MW_DMA_2;
|
||||
else if (id->dma_mword & 2)
|
||||
mode = XFER_MW_DMA_1;
|
||||
else if (id->dma_mword & 1)
|
||||
mode = XFER_MW_DMA_0;
|
||||
}
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
/*
|
||||
* sc1200_config_dma2() handles selection/setting of DMA/UDMA modes
|
||||
* for both the chipset and drive.
|
||||
*/
|
||||
static int sc1200_config_dma2 (ide_drive_t *drive, int mode)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
int unit = drive->select.b.unit;
|
||||
unsigned int reg, timings;
|
||||
unsigned short pci_clock;
|
||||
unsigned int basereg = hwif->channel ? 0x50 : 0x40;
|
||||
|
||||
/*
|
||||
* Default to DMA-off in case we run into trouble here.
|
||||
*/
|
||||
hwif->dma_off_quietly(drive); /* turn off DMA while we fiddle */
|
||||
outb(inb(hwif->dma_base+2)&~(unit?0x40:0x20), hwif->dma_base+2); /* clear DMA_capable bit */
|
||||
|
||||
/*
|
||||
* Tell the drive to switch to the new mode; abort on failure.
|
||||
*/
|
||||
if (!mode || sc1200_set_xfer_mode(drive, mode)) {
|
||||
printk("SC1200: set xfer mode failure\n");
|
||||
return 1; /* failure */
|
||||
}
|
||||
|
||||
pci_clock = sc1200_get_pci_clock();
|
||||
|
||||
/*
|
||||
* Now tune the chipset to match the drive:
|
||||
*
|
||||
* Note that each DMA mode has several timings associated with it.
|
||||
* The correct timing depends on the fast PCI clock freq.
|
||||
*/
|
||||
timings = 0;
|
||||
switch (mode) {
|
||||
case XFER_UDMA_0:
|
||||
switch (pci_clock) {
|
||||
case PCI_CLK_33: timings = 0x00921250; break;
|
||||
case PCI_CLK_48: timings = 0x00932470; break;
|
||||
case PCI_CLK_66: timings = 0x009436a1; break;
|
||||
}
|
||||
break;
|
||||
case XFER_UDMA_1:
|
||||
switch (pci_clock) {
|
||||
case PCI_CLK_33: timings = 0x00911140; break;
|
||||
case PCI_CLK_48: timings = 0x00922260; break;
|
||||
case PCI_CLK_66: timings = 0x00933481; break;
|
||||
}
|
||||
break;
|
||||
case XFER_UDMA_2:
|
||||
switch (pci_clock) {
|
||||
case PCI_CLK_33: timings = 0x00911030; break;
|
||||
case PCI_CLK_48: timings = 0x00922140; break;
|
||||
case PCI_CLK_66: timings = 0x00923261; break;
|
||||
}
|
||||
break;
|
||||
case XFER_MW_DMA_0:
|
||||
switch (pci_clock) {
|
||||
case PCI_CLK_33: timings = 0x00077771; break;
|
||||
case PCI_CLK_48: timings = 0x000bbbb2; break;
|
||||
case PCI_CLK_66: timings = 0x000ffff3; break;
|
||||
}
|
||||
break;
|
||||
case XFER_MW_DMA_1:
|
||||
switch (pci_clock) {
|
||||
case PCI_CLK_33: timings = 0x00012121; break;
|
||||
case PCI_CLK_48: timings = 0x00024241; break;
|
||||
case PCI_CLK_66: timings = 0x00035352; break;
|
||||
}
|
||||
break;
|
||||
case XFER_MW_DMA_2:
|
||||
switch (pci_clock) {
|
||||
case PCI_CLK_33: timings = 0x00002020; break;
|
||||
case PCI_CLK_48: timings = 0x00013131; break;
|
||||
case PCI_CLK_66: timings = 0x00015151; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (timings == 0) {
|
||||
printk("%s: sc1200_config_dma: huh? mode=%02x clk=%x \n", drive->name, mode, pci_clock);
|
||||
return 1; /* failure */
|
||||
}
|
||||
|
||||
if (unit == 0) { /* are we configuring drive0? */
|
||||
pci_read_config_dword(hwif->pci_dev, basereg+4, ®);
|
||||
timings |= reg & 0x80000000; /* preserve PIO format bit */
|
||||
pci_write_config_dword(hwif->pci_dev, basereg+4, timings);
|
||||
} else {
|
||||
pci_write_config_dword(hwif->pci_dev, basereg+12, timings);
|
||||
}
|
||||
|
||||
outb(inb(hwif->dma_base+2)|(unit?0x40:0x20), hwif->dma_base+2); /* set DMA_capable bit */
|
||||
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
/*
|
||||
* sc1200_config_dma() handles selection/setting of DMA/UDMA modes
|
||||
* for both the chipset and drive.
|
||||
*/
|
||||
static int sc1200_config_dma (ide_drive_t *drive)
|
||||
{
|
||||
return sc1200_config_dma2(drive, sc1200_autoselect_dma_mode(drive));
|
||||
}
|
||||
|
||||
|
||||
/* Replacement for the standard ide_dma_end action in
|
||||
* dma_proc.
|
||||
*
|
||||
* returns 1 on error, 0 otherwise
|
||||
*/
|
||||
static int sc1200_ide_dma_end (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned long dma_base = hwif->dma_base;
|
||||
byte dma_stat;
|
||||
|
||||
dma_stat = inb(dma_base+2); /* get DMA status */
|
||||
|
||||
if (!(dma_stat & 4))
|
||||
printk(" ide_dma_end dma_stat=%0x err=%x newerr=%x\n",
|
||||
dma_stat, ((dma_stat&7)!=4), ((dma_stat&2)==2));
|
||||
|
||||
outb(dma_stat|0x1b, dma_base+2); /* clear the INTR & ERROR bits */
|
||||
outb(inb(dma_base)&~1, dma_base); /* !! DO THIS HERE !! stop DMA */
|
||||
|
||||
drive->waiting_for_dma = 0;
|
||||
ide_destroy_dmatable(drive); /* purge DMA mappings */
|
||||
|
||||
return (dma_stat & 7) != 4; /* verify good DMA status */
|
||||
}
|
||||
|
||||
/*
|
||||
* sc1200_tuneproc() handles selection/setting of PIO modes
|
||||
* for both the chipset and drive.
|
||||
*
|
||||
* All existing BIOSs for this chipset guarantee that all drives
|
||||
* will have valid default PIO timings set up before we get here.
|
||||
*/
|
||||
static void sc1200_tuneproc (ide_drive_t *drive, byte pio) /* mode=255 means "autotune" */
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned int format;
|
||||
static byte modes[5] = {XFER_PIO_0, XFER_PIO_1, XFER_PIO_2, XFER_PIO_3, XFER_PIO_4};
|
||||
int mode = -1;
|
||||
|
||||
switch (pio) {
|
||||
case 200: mode = XFER_UDMA_0; break;
|
||||
case 201: mode = XFER_UDMA_1; break;
|
||||
case 202: mode = XFER_UDMA_2; break;
|
||||
case 100: mode = XFER_MW_DMA_0; break;
|
||||
case 101: mode = XFER_MW_DMA_1; break;
|
||||
case 102: mode = XFER_MW_DMA_2; break;
|
||||
}
|
||||
if (mode != -1) {
|
||||
printk("SC1200: %s: changing (U)DMA mode\n", drive->name);
|
||||
(void)sc1200_config_dma2(drive, mode);
|
||||
return;
|
||||
}
|
||||
|
||||
pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
|
||||
printk("SC1200: %s: setting PIO mode%d\n", drive->name, pio);
|
||||
if (!sc1200_set_xfer_mode(drive, modes[pio])) {
|
||||
unsigned int basereg = hwif->channel ? 0x50 : 0x40;
|
||||
pci_read_config_dword (hwif->pci_dev, basereg+4, &format);
|
||||
format = (format >> 31) & 1;
|
||||
if (format)
|
||||
format += sc1200_get_pci_clock();
|
||||
pci_write_config_dword(hwif->pci_dev, basereg + (drive->select.b.unit << 3), sc1200_pio_timings[format][pio]);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static ide_hwif_t *lookup_pci_dev (ide_hwif_t *prev, struct pci_dev *dev)
|
||||
{
|
||||
int h;
|
||||
|
||||
for (h = 0; h < MAX_HWIFS; h++) {
|
||||
ide_hwif_t *hwif = &ide_hwifs[h];
|
||||
if (prev) {
|
||||
if (hwif == prev)
|
||||
prev = NULL; // found previous, now look for next match
|
||||
} else {
|
||||
if (hwif && hwif->pci_dev == dev)
|
||||
return hwif; // found next match
|
||||
}
|
||||
}
|
||||
return NULL; // not found
|
||||
}
|
||||
|
||||
typedef struct sc1200_saved_state_s {
|
||||
__u32 regs[4];
|
||||
} sc1200_saved_state_t;
|
||||
|
||||
|
||||
static int sc1200_suspend (struct pci_dev *dev, pm_message_t state)
|
||||
{
|
||||
ide_hwif_t *hwif = NULL;
|
||||
|
||||
printk("SC1200: suspend(%u)\n", state.event);
|
||||
|
||||
if (state.event == PM_EVENT_ON) {
|
||||
// we only save state when going from full power to less
|
||||
|
||||
//
|
||||
// Loop over all interfaces that are part of this PCI device:
|
||||
//
|
||||
while ((hwif = lookup_pci_dev(hwif, dev)) != NULL) {
|
||||
sc1200_saved_state_t *ss;
|
||||
unsigned int basereg, r;
|
||||
//
|
||||
// allocate a permanent save area, if not already allocated
|
||||
//
|
||||
ss = (sc1200_saved_state_t *)hwif->config_data;
|
||||
if (ss == NULL) {
|
||||
ss = kmalloc(sizeof(sc1200_saved_state_t), GFP_KERNEL);
|
||||
if (ss == NULL)
|
||||
return -ENOMEM;
|
||||
hwif->config_data = (unsigned long)ss;
|
||||
}
|
||||
ss = (sc1200_saved_state_t *)hwif->config_data;
|
||||
//
|
||||
// Save timing registers: this may be unnecessary if
|
||||
// BIOS also does it
|
||||
//
|
||||
basereg = hwif->channel ? 0x50 : 0x40;
|
||||
for (r = 0; r < 4; ++r) {
|
||||
pci_read_config_dword (hwif->pci_dev, basereg + (r<<2), &ss->regs[r]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* You don't need to iterate over disks -- sysfs should have done that for you already */
|
||||
|
||||
pci_disable_device(dev);
|
||||
pci_set_power_state(dev, pci_choose_state(dev, state));
|
||||
dev->current_state = state.event;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc1200_resume (struct pci_dev *dev)
|
||||
{
|
||||
ide_hwif_t *hwif = NULL;
|
||||
|
||||
pci_set_power_state(dev, PCI_D0); // bring chip back from sleep state
|
||||
dev->current_state = PM_EVENT_ON;
|
||||
pci_enable_device(dev);
|
||||
//
|
||||
// loop over all interfaces that are part of this pci device:
|
||||
//
|
||||
while ((hwif = lookup_pci_dev(hwif, dev)) != NULL) {
|
||||
unsigned int basereg, r, d, format;
|
||||
sc1200_saved_state_t *ss = (sc1200_saved_state_t *)hwif->config_data;
|
||||
|
||||
//
|
||||
// Restore timing registers: this may be unnecessary if BIOS also does it
|
||||
//
|
||||
basereg = hwif->channel ? 0x50 : 0x40;
|
||||
if (ss != NULL) {
|
||||
for (r = 0; r < 4; ++r) {
|
||||
pci_write_config_dword(hwif->pci_dev, basereg + (r<<2), ss->regs[r]);
|
||||
}
|
||||
}
|
||||
//
|
||||
// Re-program drive PIO modes
|
||||
//
|
||||
pci_read_config_dword(hwif->pci_dev, basereg+4, &format);
|
||||
format = (format >> 31) & 1;
|
||||
if (format)
|
||||
format += sc1200_get_pci_clock();
|
||||
for (d = 0; d < 2; ++d) {
|
||||
ide_drive_t *drive = &(hwif->drives[d]);
|
||||
if (drive->present) {
|
||||
unsigned int pio, timings;
|
||||
pci_read_config_dword(hwif->pci_dev, basereg+(drive->select.b.unit << 3), &timings);
|
||||
for (pio = 0; pio <= 4; ++pio) {
|
||||
if (sc1200_pio_timings[format][pio] == timings)
|
||||
break;
|
||||
}
|
||||
if (pio > 4)
|
||||
pio = 255; /* autotune */
|
||||
(void)sc1200_tuneproc(drive, pio);
|
||||
}
|
||||
}
|
||||
//
|
||||
// Re-program drive DMA modes
|
||||
//
|
||||
for (d = 0; d < MAX_DRIVES; ++d) {
|
||||
ide_drive_t *drive = &(hwif->drives[d]);
|
||||
if (drive->present && !__ide_dma_bad_drive(drive)) {
|
||||
int was_using_dma = drive->using_dma;
|
||||
hwif->dma_off_quietly(drive);
|
||||
sc1200_config_dma(drive);
|
||||
if (!was_using_dma && drive->using_dma) {
|
||||
hwif->dma_off_quietly(drive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This gets invoked by the IDE driver once for each channel,
|
||||
* and performs channel-specific pre-initialization before drive probing.
|
||||
*/
|
||||
static void __devinit init_hwif_sc1200 (ide_hwif_t *hwif)
|
||||
{
|
||||
if (hwif->mate)
|
||||
hwif->serialized = hwif->mate->serialized = 1;
|
||||
hwif->autodma = 0;
|
||||
if (hwif->dma_base) {
|
||||
hwif->ide_dma_check = &sc1200_config_dma;
|
||||
hwif->ide_dma_end = &sc1200_ide_dma_end;
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->tuneproc = &sc1200_tuneproc;
|
||||
}
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x07;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
static ide_pci_device_t sc1200_chipset __devinitdata = {
|
||||
.name = "SC1200",
|
||||
.init_hwif = init_hwif_sc1200,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
};
|
||||
|
||||
static int __devinit sc1200_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &sc1200_chipset);
|
||||
}
|
||||
|
||||
static struct pci_device_id sc1200_pci_tbl[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_IDE), 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, sc1200_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "SC1200_IDE",
|
||||
.id_table = sc1200_pci_tbl,
|
||||
.probe = sc1200_init_one,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = sc1200_suspend,
|
||||
.resume = sc1200_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init sc1200_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(sc1200_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Mark Lord");
|
||||
MODULE_DESCRIPTION("PCI driver module for NS SC1200 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
858
drivers/ide/pci/scc_pata.c
Normal file
858
drivers/ide/pci/scc_pata.c
Normal file
@@ -0,0 +1,858 @@
|
||||
/*
|
||||
* Support for IDE interfaces on Celleb platform
|
||||
*
|
||||
* (C) Copyright 2006 TOSHIBA CORPORATION
|
||||
*
|
||||
* This code is based on drivers/ide/pci/siimage.c:
|
||||
* Copyright (C) 2001-2002 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2003 Red Hat <alan@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define PCI_DEVICE_ID_TOSHIBA_SCC_ATA 0x01b4
|
||||
|
||||
#define SCC_PATA_NAME "scc IDE"
|
||||
|
||||
#define TDVHSEL_MASTER 0x00000001
|
||||
#define TDVHSEL_SLAVE 0x00000004
|
||||
|
||||
#define MODE_JCUSFEN 0x00000080
|
||||
|
||||
#define CCKCTRL_ATARESET 0x00040000
|
||||
#define CCKCTRL_BUFCNT 0x00020000
|
||||
#define CCKCTRL_CRST 0x00010000
|
||||
#define CCKCTRL_OCLKEN 0x00000100
|
||||
#define CCKCTRL_ATACLKOEN 0x00000002
|
||||
#define CCKCTRL_LCLKEN 0x00000001
|
||||
|
||||
#define QCHCD_IOS_SS 0x00000001
|
||||
|
||||
#define QCHSD_STPDIAG 0x00020000
|
||||
|
||||
#define INTMASK_MSK 0xD1000012
|
||||
#define INTSTS_SERROR 0x80000000
|
||||
#define INTSTS_PRERR 0x40000000
|
||||
#define INTSTS_RERR 0x10000000
|
||||
#define INTSTS_ICERR 0x01000000
|
||||
#define INTSTS_BMSINT 0x00000010
|
||||
#define INTSTS_BMHE 0x00000008
|
||||
#define INTSTS_IOIRQS 0x00000004
|
||||
#define INTSTS_INTRQ 0x00000002
|
||||
#define INTSTS_ACTEINT 0x00000001
|
||||
|
||||
#define ECMODE_VALUE 0x01
|
||||
|
||||
static struct scc_ports {
|
||||
unsigned long ctl, dma;
|
||||
unsigned char hwif_id; /* for removing hwif from system */
|
||||
} scc_ports[MAX_HWIFS];
|
||||
|
||||
/* PIO transfer mode table */
|
||||
/* JCHST */
|
||||
static unsigned long JCHSTtbl[2][7] = {
|
||||
{0x0E, 0x05, 0x02, 0x03, 0x02, 0x00, 0x00}, /* 100MHz */
|
||||
{0x13, 0x07, 0x04, 0x04, 0x03, 0x00, 0x00} /* 133MHz */
|
||||
};
|
||||
|
||||
/* JCHHT */
|
||||
static unsigned long JCHHTtbl[2][7] = {
|
||||
{0x0E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00}, /* 100MHz */
|
||||
{0x13, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00} /* 133MHz */
|
||||
};
|
||||
|
||||
/* JCHCT */
|
||||
static unsigned long JCHCTtbl[2][7] = {
|
||||
{0x1D, 0x1D, 0x1C, 0x0B, 0x06, 0x00, 0x00}, /* 100MHz */
|
||||
{0x27, 0x26, 0x26, 0x0E, 0x09, 0x00, 0x00} /* 133MHz */
|
||||
};
|
||||
|
||||
|
||||
/* DMA transfer mode table */
|
||||
/* JCHDCTM/JCHDCTS */
|
||||
static unsigned long JCHDCTxtbl[2][7] = {
|
||||
{0x0A, 0x06, 0x04, 0x03, 0x01, 0x00, 0x00}, /* 100MHz */
|
||||
{0x0E, 0x09, 0x06, 0x04, 0x02, 0x01, 0x00} /* 133MHz */
|
||||
};
|
||||
|
||||
/* JCSTWTM/JCSTWTS */
|
||||
static unsigned long JCSTWTxtbl[2][7] = {
|
||||
{0x06, 0x04, 0x03, 0x02, 0x02, 0x02, 0x00}, /* 100MHz */
|
||||
{0x09, 0x06, 0x04, 0x02, 0x02, 0x02, 0x02} /* 133MHz */
|
||||
};
|
||||
|
||||
/* JCTSS */
|
||||
static unsigned long JCTSStbl[2][7] = {
|
||||
{0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00}, /* 100MHz */
|
||||
{0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05} /* 133MHz */
|
||||
};
|
||||
|
||||
/* JCENVT */
|
||||
static unsigned long JCENVTtbl[2][7] = {
|
||||
{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00}, /* 100MHz */
|
||||
{0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02} /* 133MHz */
|
||||
};
|
||||
|
||||
/* JCACTSELS/JCACTSELM */
|
||||
static unsigned long JCACTSELtbl[2][7] = {
|
||||
{0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00}, /* 100MHz */
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} /* 133MHz */
|
||||
};
|
||||
|
||||
|
||||
static u8 scc_ide_inb(unsigned long port)
|
||||
{
|
||||
u32 data = in_be32((void*)port);
|
||||
return (u8)data;
|
||||
}
|
||||
|
||||
static u16 scc_ide_inw(unsigned long port)
|
||||
{
|
||||
u32 data = in_be32((void*)port);
|
||||
return (u16)data;
|
||||
}
|
||||
|
||||
static void scc_ide_insw(unsigned long port, void *addr, u32 count)
|
||||
{
|
||||
u16 *ptr = (u16 *)addr;
|
||||
while (count--) {
|
||||
*ptr++ = le16_to_cpu(in_be32((void*)port));
|
||||
}
|
||||
}
|
||||
|
||||
static void scc_ide_insl(unsigned long port, void *addr, u32 count)
|
||||
{
|
||||
u16 *ptr = (u16 *)addr;
|
||||
while (count--) {
|
||||
*ptr++ = le16_to_cpu(in_be32((void*)port));
|
||||
*ptr++ = le16_to_cpu(in_be32((void*)port));
|
||||
}
|
||||
}
|
||||
|
||||
static void scc_ide_outb(u8 addr, unsigned long port)
|
||||
{
|
||||
out_be32((void*)port, addr);
|
||||
}
|
||||
|
||||
static void scc_ide_outw(u16 addr, unsigned long port)
|
||||
{
|
||||
out_be32((void*)port, addr);
|
||||
}
|
||||
|
||||
static void
|
||||
scc_ide_outbsync(ide_drive_t * drive, u8 addr, unsigned long port)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
|
||||
out_be32((void*)port, addr);
|
||||
__asm__ __volatile__("eieio":::"memory");
|
||||
in_be32((void*)(hwif->dma_base + 0x01c));
|
||||
__asm__ __volatile__("eieio":::"memory");
|
||||
}
|
||||
|
||||
static void
|
||||
scc_ide_outsw(unsigned long port, void *addr, u32 count)
|
||||
{
|
||||
u16 *ptr = (u16 *)addr;
|
||||
while (count--) {
|
||||
out_be32((void*)port, cpu_to_le16(*ptr++));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
scc_ide_outsl(unsigned long port, void *addr, u32 count)
|
||||
{
|
||||
u16 *ptr = (u16 *)addr;
|
||||
while (count--) {
|
||||
out_be32((void*)port, cpu_to_le16(*ptr++));
|
||||
out_be32((void*)port, cpu_to_le16(*ptr++));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* scc_ratemask - Compute available modes
|
||||
* @drive: IDE drive
|
||||
*
|
||||
* Compute the available speeds for the devices on the interface.
|
||||
* Enforce UDMA33 as a limit if there is no 80pin cable present.
|
||||
*/
|
||||
|
||||
static u8 scc_ratemask(ide_drive_t *drive)
|
||||
{
|
||||
u8 mode = 4;
|
||||
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min(mode, (u8)1);
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* scc_tuneproc - tune a drive PIO mode
|
||||
* @drive: drive to tune
|
||||
* @mode_wanted: the target operating mode
|
||||
*
|
||||
* Load the timing settings for this device mode into the
|
||||
* controller.
|
||||
*/
|
||||
|
||||
static void scc_tuneproc(ide_drive_t *drive, byte mode_wanted)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct scc_ports *ports = ide_get_hwifdata(hwif);
|
||||
unsigned long ctl_base = ports->ctl;
|
||||
unsigned long cckctrl_port = ctl_base + 0xff0;
|
||||
unsigned long piosht_port = ctl_base + 0x000;
|
||||
unsigned long pioct_port = ctl_base + 0x004;
|
||||
unsigned long reg;
|
||||
unsigned char speed = XFER_PIO_0;
|
||||
int offset;
|
||||
|
||||
mode_wanted = ide_get_best_pio_mode(drive, mode_wanted, 4, NULL);
|
||||
switch (mode_wanted) {
|
||||
case 4:
|
||||
speed = XFER_PIO_4;
|
||||
break;
|
||||
case 3:
|
||||
speed = XFER_PIO_3;
|
||||
break;
|
||||
case 2:
|
||||
speed = XFER_PIO_2;
|
||||
break;
|
||||
case 1:
|
||||
speed = XFER_PIO_1;
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
speed = XFER_PIO_0;
|
||||
break;
|
||||
}
|
||||
|
||||
reg = in_be32((void __iomem *)cckctrl_port);
|
||||
if (reg & CCKCTRL_ATACLKOEN) {
|
||||
offset = 1; /* 133MHz */
|
||||
} else {
|
||||
offset = 0; /* 100MHz */
|
||||
}
|
||||
reg = JCHSTtbl[offset][mode_wanted] << 16 | JCHHTtbl[offset][mode_wanted];
|
||||
out_be32((void __iomem *)piosht_port, reg);
|
||||
reg = JCHCTtbl[offset][mode_wanted];
|
||||
out_be32((void __iomem *)pioct_port, reg);
|
||||
|
||||
ide_config_drive_speed(drive, speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* scc_tune_chipset - tune a drive DMA mode
|
||||
* @drive: Drive to set up
|
||||
* @xferspeed: speed we want to achieve
|
||||
*
|
||||
* Load the timing settings for this device mode into the
|
||||
* controller.
|
||||
*/
|
||||
|
||||
static int scc_tune_chipset(ide_drive_t *drive, byte xferspeed)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 speed = ide_rate_filter(scc_ratemask(drive), xferspeed);
|
||||
struct scc_ports *ports = ide_get_hwifdata(hwif);
|
||||
unsigned long ctl_base = ports->ctl;
|
||||
unsigned long cckctrl_port = ctl_base + 0xff0;
|
||||
unsigned long mdmact_port = ctl_base + 0x008;
|
||||
unsigned long mcrcst_port = ctl_base + 0x00c;
|
||||
unsigned long sdmact_port = ctl_base + 0x010;
|
||||
unsigned long scrcst_port = ctl_base + 0x014;
|
||||
unsigned long udenvt_port = ctl_base + 0x018;
|
||||
unsigned long tdvhsel_port = ctl_base + 0x020;
|
||||
int is_slave = (&hwif->drives[1] == drive);
|
||||
int offset, idx;
|
||||
unsigned long reg;
|
||||
unsigned long jcactsel;
|
||||
|
||||
reg = in_be32((void __iomem *)cckctrl_port);
|
||||
if (reg & CCKCTRL_ATACLKOEN) {
|
||||
offset = 1; /* 133MHz */
|
||||
} else {
|
||||
offset = 0; /* 100MHz */
|
||||
}
|
||||
|
||||
switch (speed) {
|
||||
case XFER_UDMA_6:
|
||||
idx = 6;
|
||||
break;
|
||||
case XFER_UDMA_5:
|
||||
idx = 5;
|
||||
break;
|
||||
case XFER_UDMA_4:
|
||||
idx = 4;
|
||||
break;
|
||||
case XFER_UDMA_3:
|
||||
idx = 3;
|
||||
break;
|
||||
case XFER_UDMA_2:
|
||||
idx = 2;
|
||||
break;
|
||||
case XFER_UDMA_1:
|
||||
idx = 1;
|
||||
break;
|
||||
case XFER_UDMA_0:
|
||||
idx = 0;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
jcactsel = JCACTSELtbl[offset][idx];
|
||||
if (is_slave) {
|
||||
out_be32((void __iomem *)sdmact_port, JCHDCTxtbl[offset][idx]);
|
||||
out_be32((void __iomem *)scrcst_port, JCSTWTxtbl[offset][idx]);
|
||||
jcactsel = jcactsel << 2;
|
||||
out_be32((void __iomem *)tdvhsel_port, (in_be32((void __iomem *)tdvhsel_port) & ~TDVHSEL_SLAVE) | jcactsel);
|
||||
} else {
|
||||
out_be32((void __iomem *)mdmact_port, JCHDCTxtbl[offset][idx]);
|
||||
out_be32((void __iomem *)mcrcst_port, JCSTWTxtbl[offset][idx]);
|
||||
out_be32((void __iomem *)tdvhsel_port, (in_be32((void __iomem *)tdvhsel_port) & ~TDVHSEL_MASTER) | jcactsel);
|
||||
}
|
||||
reg = JCTSStbl[offset][idx] << 16 | JCENVTtbl[offset][idx];
|
||||
out_be32((void __iomem *)udenvt_port, reg);
|
||||
|
||||
return ide_config_drive_speed(drive, speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* scc_config_chipset_for_dma - configure for DMA
|
||||
* @drive: drive to configure
|
||||
*
|
||||
* Called by scc_config_drive_for_dma().
|
||||
*/
|
||||
|
||||
static int scc_config_chipset_for_dma(ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, scc_ratemask(drive));
|
||||
|
||||
if (!speed)
|
||||
return 0;
|
||||
|
||||
if (scc_tune_chipset(drive, speed))
|
||||
return 0;
|
||||
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
/**
|
||||
* scc_configure_drive_for_dma - set up for DMA transfers
|
||||
* @drive: drive we are going to set up
|
||||
*
|
||||
* Set up the drive for DMA, tune the controller and drive as
|
||||
* required.
|
||||
* If the drive isn't suitable for DMA or we hit other problems
|
||||
* then we will drop down to PIO and set up PIO appropriately.
|
||||
* (return 1)
|
||||
*/
|
||||
|
||||
static int scc_config_drive_for_dma(ide_drive_t *drive)
|
||||
{
|
||||
if (ide_use_dma(drive) && scc_config_chipset_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
if (ide_use_fast_pio(drive))
|
||||
scc_tuneproc(drive, 4);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* scc_ide_dma_setup - begin a DMA phase
|
||||
* @drive: target device
|
||||
*
|
||||
* Build an IDE DMA PRD (IDE speak for scatter gather table)
|
||||
* and then set up the DMA transfer registers.
|
||||
*
|
||||
* Returns 0 on success. If a PIO fallback is required then 1
|
||||
* is returned.
|
||||
*/
|
||||
|
||||
static int scc_dma_setup(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct request *rq = HWGROUP(drive)->rq;
|
||||
unsigned int reading;
|
||||
u8 dma_stat;
|
||||
|
||||
if (rq_data_dir(rq))
|
||||
reading = 0;
|
||||
else
|
||||
reading = 1 << 3;
|
||||
|
||||
/* fall back to pio! */
|
||||
if (!ide_build_dmatable(drive, rq)) {
|
||||
ide_map_sg(drive, rq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* PRD table */
|
||||
out_be32((void __iomem *)hwif->dma_prdtable, hwif->dmatable_dma);
|
||||
|
||||
/* specify r/w */
|
||||
out_be32((void __iomem *)hwif->dma_command, reading);
|
||||
|
||||
/* read dma_status for INTR & ERROR flags */
|
||||
dma_stat = in_be32((void __iomem *)hwif->dma_status);
|
||||
|
||||
/* clear INTR & ERROR flags */
|
||||
out_be32((void __iomem *)hwif->dma_status, dma_stat|6);
|
||||
drive->waiting_for_dma = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* scc_ide_dma_end - Stop DMA
|
||||
* @drive: IDE drive
|
||||
*
|
||||
* Check and clear INT Status register.
|
||||
* Then call __ide_dma_end().
|
||||
*/
|
||||
|
||||
static int scc_ide_dma_end(ide_drive_t * drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned long intsts_port = hwif->dma_base + 0x014;
|
||||
u32 reg;
|
||||
|
||||
while (1) {
|
||||
reg = in_be32((void __iomem *)intsts_port);
|
||||
|
||||
if (reg & INTSTS_SERROR) {
|
||||
printk(KERN_WARNING "%s: SERROR\n", SCC_PATA_NAME);
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_SERROR|INTSTS_BMSINT);
|
||||
|
||||
out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_PRERR) {
|
||||
u32 maea0, maec0;
|
||||
unsigned long ctl_base = hwif->config_data;
|
||||
|
||||
maea0 = in_be32((void __iomem *)(ctl_base + 0xF50));
|
||||
maec0 = in_be32((void __iomem *)(ctl_base + 0xF54));
|
||||
|
||||
printk(KERN_WARNING "%s: PRERR [addr:%x cmd:%x]\n", SCC_PATA_NAME, maea0, maec0);
|
||||
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_PRERR|INTSTS_BMSINT);
|
||||
|
||||
out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_RERR) {
|
||||
printk(KERN_WARNING "%s: Response Error\n", SCC_PATA_NAME);
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_RERR|INTSTS_BMSINT);
|
||||
|
||||
out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_ICERR) {
|
||||
out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS);
|
||||
|
||||
printk(KERN_WARNING "%s: Illegal Configuration\n", SCC_PATA_NAME);
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_ICERR|INTSTS_BMSINT);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_BMSINT) {
|
||||
printk(KERN_WARNING "%s: Internal Bus Error\n", SCC_PATA_NAME);
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_BMSINT);
|
||||
|
||||
ide_do_reset(drive);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_BMHE) {
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_BMHE);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_ACTEINT) {
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_ACTEINT);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_IOIRQS) {
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_IOIRQS);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return __ide_dma_end(drive);
|
||||
}
|
||||
|
||||
/* returns 1 if dma irq issued, 0 otherwise */
|
||||
static int scc_dma_test_irq(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u8 dma_stat = hwif->INB(hwif->dma_status);
|
||||
|
||||
/* return 1 if INTR asserted */
|
||||
if ((dma_stat & 4) == 4)
|
||||
return 1;
|
||||
|
||||
/* Workaround for PTERADD: emulate DMA_INTR when
|
||||
* - IDE_STATUS[ERR] = 1
|
||||
* - INT_STATUS[INTRQ] = 1
|
||||
* - DMA_STATUS[IORACTA] = 1
|
||||
*/
|
||||
if (in_be32((void __iomem *)IDE_ALTSTATUS_REG) & ERR_STAT &&
|
||||
in_be32((void __iomem *)(hwif->dma_base + 0x014)) & INTSTS_INTRQ &&
|
||||
dma_stat & 1)
|
||||
return 1;
|
||||
|
||||
if (!drive->waiting_for_dma)
|
||||
printk(KERN_WARNING "%s: (%s) called while not waiting\n",
|
||||
drive->name, __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* setup_mmio_scc - map CTRL/BMID region
|
||||
* @dev: PCI device we are configuring
|
||||
* @name: device name
|
||||
*
|
||||
*/
|
||||
|
||||
static int setup_mmio_scc (struct pci_dev *dev, const char *name)
|
||||
{
|
||||
unsigned long ctl_base = pci_resource_start(dev, 0);
|
||||
unsigned long dma_base = pci_resource_start(dev, 1);
|
||||
unsigned long ctl_size = pci_resource_len(dev, 0);
|
||||
unsigned long dma_size = pci_resource_len(dev, 1);
|
||||
void *ctl_addr;
|
||||
void *dma_addr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_HWIFS; i++) {
|
||||
if (scc_ports[i].ctl == 0)
|
||||
break;
|
||||
}
|
||||
if (i >= MAX_HWIFS)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!request_mem_region(ctl_base, ctl_size, name)) {
|
||||
printk(KERN_WARNING "%s: IDE controller MMIO ports not available.\n", SCC_PATA_NAME);
|
||||
goto fail_0;
|
||||
}
|
||||
|
||||
if (!request_mem_region(dma_base, dma_size, name)) {
|
||||
printk(KERN_WARNING "%s: IDE controller MMIO ports not available.\n", SCC_PATA_NAME);
|
||||
goto fail_1;
|
||||
}
|
||||
|
||||
if ((ctl_addr = ioremap(ctl_base, ctl_size)) == NULL)
|
||||
goto fail_2;
|
||||
|
||||
if ((dma_addr = ioremap(dma_base, dma_size)) == NULL)
|
||||
goto fail_3;
|
||||
|
||||
pci_set_master(dev);
|
||||
scc_ports[i].ctl = (unsigned long)ctl_addr;
|
||||
scc_ports[i].dma = (unsigned long)dma_addr;
|
||||
pci_set_drvdata(dev, (void *) &scc_ports[i]);
|
||||
|
||||
return 1;
|
||||
|
||||
fail_3:
|
||||
iounmap(ctl_addr);
|
||||
fail_2:
|
||||
release_mem_region(dma_base, dma_size);
|
||||
fail_1:
|
||||
release_mem_region(ctl_base, ctl_size);
|
||||
fail_0:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_setup_scc - set up an SCC PATA Controller
|
||||
* @dev: PCI device
|
||||
* @d: IDE PCI device
|
||||
*
|
||||
* Perform the initial set up for this device.
|
||||
*/
|
||||
|
||||
static int __devinit init_setup_scc(struct pci_dev *dev, ide_pci_device_t *d)
|
||||
{
|
||||
unsigned long ctl_base;
|
||||
unsigned long dma_base;
|
||||
unsigned long cckctrl_port;
|
||||
unsigned long intmask_port;
|
||||
unsigned long mode_port;
|
||||
unsigned long ecmode_port;
|
||||
unsigned long dma_status_port;
|
||||
u32 reg = 0;
|
||||
struct scc_ports *ports;
|
||||
int rc;
|
||||
|
||||
rc = setup_mmio_scc(dev, d->name);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
ports = pci_get_drvdata(dev);
|
||||
ctl_base = ports->ctl;
|
||||
dma_base = ports->dma;
|
||||
cckctrl_port = ctl_base + 0xff0;
|
||||
intmask_port = dma_base + 0x010;
|
||||
mode_port = ctl_base + 0x024;
|
||||
ecmode_port = ctl_base + 0xf00;
|
||||
dma_status_port = dma_base + 0x004;
|
||||
|
||||
/* controller initialization */
|
||||
reg = 0;
|
||||
out_be32((void*)cckctrl_port, reg);
|
||||
reg |= CCKCTRL_ATACLKOEN;
|
||||
out_be32((void*)cckctrl_port, reg);
|
||||
reg |= CCKCTRL_LCLKEN | CCKCTRL_OCLKEN;
|
||||
out_be32((void*)cckctrl_port, reg);
|
||||
reg |= CCKCTRL_CRST;
|
||||
out_be32((void*)cckctrl_port, reg);
|
||||
|
||||
for (;;) {
|
||||
reg = in_be32((void*)cckctrl_port);
|
||||
if (reg & CCKCTRL_CRST)
|
||||
break;
|
||||
udelay(5000);
|
||||
}
|
||||
|
||||
reg |= CCKCTRL_ATARESET;
|
||||
out_be32((void*)cckctrl_port, reg);
|
||||
|
||||
out_be32((void*)ecmode_port, ECMODE_VALUE);
|
||||
out_be32((void*)mode_port, MODE_JCUSFEN);
|
||||
out_be32((void*)intmask_port, INTMASK_MSK);
|
||||
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
/**
|
||||
* init_mmio_iops_scc - set up the iops for MMIO
|
||||
* @hwif: interface to set up
|
||||
*
|
||||
*/
|
||||
|
||||
static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
struct scc_ports *ports = pci_get_drvdata(dev);
|
||||
unsigned long dma_base = ports->dma;
|
||||
|
||||
ide_set_hwifdata(hwif, ports);
|
||||
|
||||
hwif->INB = scc_ide_inb;
|
||||
hwif->INW = scc_ide_inw;
|
||||
hwif->INSW = scc_ide_insw;
|
||||
hwif->INSL = scc_ide_insl;
|
||||
hwif->OUTB = scc_ide_outb;
|
||||
hwif->OUTBSYNC = scc_ide_outbsync;
|
||||
hwif->OUTW = scc_ide_outw;
|
||||
hwif->OUTSW = scc_ide_outsw;
|
||||
hwif->OUTSL = scc_ide_outsl;
|
||||
|
||||
hwif->io_ports[IDE_DATA_OFFSET] = dma_base + 0x20;
|
||||
hwif->io_ports[IDE_ERROR_OFFSET] = dma_base + 0x24;
|
||||
hwif->io_ports[IDE_NSECTOR_OFFSET] = dma_base + 0x28;
|
||||
hwif->io_ports[IDE_SECTOR_OFFSET] = dma_base + 0x2c;
|
||||
hwif->io_ports[IDE_LCYL_OFFSET] = dma_base + 0x30;
|
||||
hwif->io_ports[IDE_HCYL_OFFSET] = dma_base + 0x34;
|
||||
hwif->io_ports[IDE_SELECT_OFFSET] = dma_base + 0x38;
|
||||
hwif->io_ports[IDE_STATUS_OFFSET] = dma_base + 0x3c;
|
||||
hwif->io_ports[IDE_CONTROL_OFFSET] = dma_base + 0x40;
|
||||
|
||||
hwif->irq = hwif->pci_dev->irq;
|
||||
hwif->dma_base = dma_base;
|
||||
hwif->config_data = ports->ctl;
|
||||
hwif->mmio = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_iops_scc - set up iops
|
||||
* @hwif: interface to set up
|
||||
*
|
||||
* Do the basic setup for the SCC hardware interface
|
||||
* and then do the MMIO setup.
|
||||
*/
|
||||
|
||||
static void __devinit init_iops_scc(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
hwif->hwif_data = NULL;
|
||||
if (pci_get_drvdata(dev) == NULL)
|
||||
return;
|
||||
init_mmio_iops_scc(hwif);
|
||||
}
|
||||
|
||||
/**
|
||||
* init_hwif_scc - set up hwif
|
||||
* @hwif: interface to set up
|
||||
*
|
||||
* We do the basic set up of the interface structure. The SCC
|
||||
* requires several custom handlers so we override the default
|
||||
* ide DMA handlers appropriately.
|
||||
*/
|
||||
|
||||
static void __devinit init_hwif_scc(ide_hwif_t *hwif)
|
||||
{
|
||||
struct scc_ports *ports = ide_get_hwifdata(hwif);
|
||||
|
||||
ports->hwif_id = hwif->index;
|
||||
|
||||
hwif->dma_command = hwif->dma_base;
|
||||
hwif->dma_status = hwif->dma_base + 0x04;
|
||||
hwif->dma_prdtable = hwif->dma_base + 0x08;
|
||||
|
||||
/* PTERADD */
|
||||
out_be32((void __iomem *)(hwif->dma_base + 0x018), hwif->dmatable_dma);
|
||||
|
||||
hwif->dma_setup = scc_dma_setup;
|
||||
hwif->ide_dma_end = scc_ide_dma_end;
|
||||
hwif->speedproc = scc_tune_chipset;
|
||||
hwif->tuneproc = scc_tuneproc;
|
||||
hwif->ide_dma_check = scc_config_drive_for_dma;
|
||||
hwif->ide_dma_test_irq = scc_dma_test_irq;
|
||||
|
||||
hwif->drives[0].autotune = IDE_TUNE_AUTO;
|
||||
hwif->drives[1].autotune = IDE_TUNE_AUTO;
|
||||
|
||||
if (in_be32((void __iomem *)(hwif->config_data + 0xff0)) & CCKCTRL_ATACLKOEN) {
|
||||
hwif->ultra_mask = 0x7f; /* 133MHz */
|
||||
} else {
|
||||
hwif->ultra_mask = 0x3f; /* 100MHz */
|
||||
}
|
||||
hwif->mwdma_mask = 0x00;
|
||||
hwif->swdma_mask = 0x00;
|
||||
hwif->atapi_dma = 1;
|
||||
|
||||
/* we support 80c cable only. */
|
||||
hwif->udma_four = 1;
|
||||
|
||||
hwif->autodma = 0;
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
#define DECLARE_SCC_DEV(name_str) \
|
||||
{ \
|
||||
.name = name_str, \
|
||||
.init_setup = init_setup_scc, \
|
||||
.init_iops = init_iops_scc, \
|
||||
.init_hwif = init_hwif_scc, \
|
||||
.channels = 1, \
|
||||
.autodma = AUTODMA, \
|
||||
.bootable = ON_BOARD, \
|
||||
}
|
||||
|
||||
static ide_pci_device_t scc_chipsets[] __devinitdata = {
|
||||
/* 0 */ DECLARE_SCC_DEV("sccIDE"),
|
||||
};
|
||||
|
||||
/**
|
||||
* scc_init_one - pci layer discovery entry
|
||||
* @dev: PCI device
|
||||
* @id: ident table entry
|
||||
*
|
||||
* Called by the PCI code when it finds an SCC PATA controller.
|
||||
* We then use the IDE PCI generic helper to do most of the work.
|
||||
*/
|
||||
|
||||
static int __devinit scc_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
ide_pci_device_t *d = &scc_chipsets[id->driver_data];
|
||||
return d->init_setup(dev, d);
|
||||
}
|
||||
|
||||
/**
|
||||
* scc_remove - pci layer remove entry
|
||||
* @dev: PCI device
|
||||
*
|
||||
* Called by the PCI code when it removes an SCC PATA controller.
|
||||
*/
|
||||
|
||||
static void __devexit scc_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct scc_ports *ports = pci_get_drvdata(dev);
|
||||
ide_hwif_t *hwif = &ide_hwifs[ports->hwif_id];
|
||||
unsigned long ctl_base = pci_resource_start(dev, 0);
|
||||
unsigned long dma_base = pci_resource_start(dev, 1);
|
||||
unsigned long ctl_size = pci_resource_len(dev, 0);
|
||||
unsigned long dma_size = pci_resource_len(dev, 1);
|
||||
|
||||
if (hwif->dmatable_cpu) {
|
||||
pci_free_consistent(hwif->pci_dev,
|
||||
PRD_ENTRIES * PRD_BYTES,
|
||||
hwif->dmatable_cpu,
|
||||
hwif->dmatable_dma);
|
||||
hwif->dmatable_cpu = NULL;
|
||||
}
|
||||
|
||||
ide_unregister(hwif->index);
|
||||
|
||||
hwif->chipset = ide_unknown;
|
||||
iounmap((void*)ports->dma);
|
||||
iounmap((void*)ports->ctl);
|
||||
release_mem_region(dma_base, dma_size);
|
||||
release_mem_region(ctl_base, ctl_size);
|
||||
memset(ports, 0, sizeof(*ports));
|
||||
}
|
||||
|
||||
static struct pci_device_id scc_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_SCC_ATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, scc_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "SCC IDE",
|
||||
.id_table = scc_pci_tbl,
|
||||
.probe = scc_init_one,
|
||||
.remove = scc_remove,
|
||||
};
|
||||
|
||||
static int scc_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(scc_ide_init);
|
||||
/* -- No exit code?
|
||||
static void scc_ide_exit(void)
|
||||
{
|
||||
ide_pci_unregister_driver(&driver);
|
||||
}
|
||||
module_exit(scc_ide_exit);
|
||||
*/
|
||||
|
||||
|
||||
MODULE_DESCRIPTION("PCI driver module for Toshiba SCC IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
639
drivers/ide/pci/serverworks.c
Normal file
639
drivers/ide/pci/serverworks.c
Normal file
@@ -0,0 +1,639 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/serverworks.c Version 0.8 25 Ebr 2003
|
||||
*
|
||||
* Copyright (C) 1998-2000 Michel Aubry
|
||||
* Copyright (C) 1998-2000 Andrzej Krzysztofowicz
|
||||
* Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
|
||||
* Portions copyright (c) 2001 Sun Microsystems
|
||||
*
|
||||
*
|
||||
* RCC/ServerWorks IDE driver for Linux
|
||||
*
|
||||
* OSB4: `Open South Bridge' IDE Interface (fn 1)
|
||||
* supports UDMA mode 2 (33 MB/s)
|
||||
*
|
||||
* CSB5: `Champion South Bridge' IDE Interface (fn 1)
|
||||
* all revisions support UDMA mode 4 (66 MB/s)
|
||||
* revision A2.0 and up support UDMA mode 5 (100 MB/s)
|
||||
*
|
||||
* *** The CSB5 does not provide ANY register ***
|
||||
* *** to detect 80-conductor cable presence. ***
|
||||
*
|
||||
* CSB6: `Champion South Bridge' IDE Interface (optional: third channel)
|
||||
*
|
||||
* HT1000: AKA BCM5785 - Hypertransport Southbridge for Opteron systems. IDE
|
||||
* controller same as the CSB6. Single channel ATA100 only.
|
||||
*
|
||||
* Documentation:
|
||||
* Available under NDA only. Errata info very hard to get.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */
|
||||
#define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */
|
||||
|
||||
/* Seagate Barracuda ATA IV Family drives in UDMA mode 5
|
||||
* can overrun their FIFOs when used with the CSB5 */
|
||||
static const char *svwks_bad_ata100[] = {
|
||||
"ST320011A",
|
||||
"ST340016A",
|
||||
"ST360021A",
|
||||
"ST380021A",
|
||||
NULL
|
||||
};
|
||||
|
||||
static u8 svwks_revision = 0;
|
||||
static struct pci_dev *isa_dev;
|
||||
|
||||
static int check_in_drive_lists (ide_drive_t *drive, const char **list)
|
||||
{
|
||||
while (*list)
|
||||
if (!strcmp(*list++, drive->id->model))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 svwks_ratemask (ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = HWIF(drive)->pci_dev;
|
||||
u8 mode = 0;
|
||||
|
||||
if (!svwks_revision)
|
||||
pci_read_config_byte(dev, PCI_REVISION_ID, &svwks_revision);
|
||||
|
||||
if (dev->device == PCI_DEVICE_ID_SERVERWORKS_HT1000IDE)
|
||||
return 2;
|
||||
if (dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) {
|
||||
u32 reg = 0;
|
||||
if (isa_dev)
|
||||
pci_read_config_dword(isa_dev, 0x64, ®);
|
||||
|
||||
/*
|
||||
* Don't enable UDMA on disk devices for the moment
|
||||
*/
|
||||
if(drive->media == ide_disk)
|
||||
return 0;
|
||||
/* Check the OSB4 DMA33 enable bit */
|
||||
return ((reg & 0x00004000) == 0x00004000) ? 1 : 0;
|
||||
} else if (svwks_revision < SVWKS_CSB5_REVISION_NEW) {
|
||||
return 1;
|
||||
} else if (svwks_revision >= SVWKS_CSB5_REVISION_NEW) {
|
||||
u8 btr = 0;
|
||||
pci_read_config_byte(dev, 0x5A, &btr);
|
||||
mode = btr & 0x3;
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min(mode, (u8)1);
|
||||
/* If someone decides to do UDMA133 on CSB5 the same
|
||||
issue will bite so be inclusive */
|
||||
if (mode > 2 && check_in_drive_lists(drive, svwks_bad_ata100))
|
||||
mode = 2;
|
||||
}
|
||||
if (((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) ||
|
||||
(dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) &&
|
||||
(!(PCI_FUNC(dev->devfn) & 1)))
|
||||
mode = 2;
|
||||
return mode;
|
||||
}
|
||||
|
||||
static u8 svwks_csb_check (struct pci_dev *dev)
|
||||
{
|
||||
switch (dev->device) {
|
||||
case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE:
|
||||
case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE:
|
||||
case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2:
|
||||
case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int svwks_tune_chipset (ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
static const u8 udma_modes[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
|
||||
static const u8 dma_modes[] = { 0x77, 0x21, 0x20 };
|
||||
static const u8 pio_modes[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 };
|
||||
static const u8 drive_pci[] = { 0x41, 0x40, 0x43, 0x42 };
|
||||
static const u8 drive_pci2[] = { 0x45, 0x44, 0x47, 0x46 };
|
||||
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u8 speed;
|
||||
u8 pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
|
||||
u8 unit = (drive->select.b.unit & 0x01);
|
||||
u8 csb5 = svwks_csb_check(dev);
|
||||
u8 ultra_enable = 0, ultra_timing = 0;
|
||||
u8 dma_timing = 0, pio_timing = 0;
|
||||
u16 csb5_pio = 0;
|
||||
|
||||
if (xferspeed == 255) /* PIO auto-tuning */
|
||||
speed = XFER_PIO_0 + pio;
|
||||
else
|
||||
speed = ide_rate_filter(svwks_ratemask(drive), xferspeed);
|
||||
|
||||
/* If we are about to put a disk into UDMA mode we screwed up.
|
||||
Our code assumes we never _ever_ do this on an OSB4 */
|
||||
|
||||
if(dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4 &&
|
||||
drive->media == ide_disk && speed >= XFER_UDMA_0)
|
||||
BUG();
|
||||
|
||||
pci_read_config_byte(dev, drive_pci[drive->dn], &pio_timing);
|
||||
pci_read_config_byte(dev, drive_pci2[drive->dn], &dma_timing);
|
||||
pci_read_config_byte(dev, (0x56|hwif->channel), &ultra_timing);
|
||||
pci_read_config_word(dev, 0x4A, &csb5_pio);
|
||||
pci_read_config_byte(dev, 0x54, &ultra_enable);
|
||||
|
||||
/* Per Specified Design by OEM, and ASIC Architect */
|
||||
if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) ||
|
||||
(dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) {
|
||||
if (!drive->init_speed) {
|
||||
u8 dma_stat = inb(hwif->dma_status);
|
||||
|
||||
dma_pio:
|
||||
if (((ultra_enable << (7-drive->dn) & 0x80) == 0x80) &&
|
||||
((dma_stat & (1<<(5+unit))) == (1<<(5+unit)))) {
|
||||
drive->current_speed = drive->init_speed = XFER_UDMA_0 + udma_modes[(ultra_timing >> (4*unit)) & ~(0xF0)];
|
||||
return 0;
|
||||
} else if ((dma_timing) &&
|
||||
((dma_stat&(1<<(5+unit)))==(1<<(5+unit)))) {
|
||||
u8 dmaspeed = dma_timing;
|
||||
|
||||
dma_timing &= ~0xFF;
|
||||
if ((dmaspeed & 0x20) == 0x20)
|
||||
dmaspeed = XFER_MW_DMA_2;
|
||||
else if ((dmaspeed & 0x21) == 0x21)
|
||||
dmaspeed = XFER_MW_DMA_1;
|
||||
else if ((dmaspeed & 0x77) == 0x77)
|
||||
dmaspeed = XFER_MW_DMA_0;
|
||||
else
|
||||
goto dma_pio;
|
||||
drive->current_speed = drive->init_speed = dmaspeed;
|
||||
return 0;
|
||||
} else if (pio_timing) {
|
||||
u8 piospeed = pio_timing;
|
||||
|
||||
pio_timing &= ~0xFF;
|
||||
if ((piospeed & 0x20) == 0x20)
|
||||
piospeed = XFER_PIO_4;
|
||||
else if ((piospeed & 0x22) == 0x22)
|
||||
piospeed = XFER_PIO_3;
|
||||
else if ((piospeed & 0x34) == 0x34)
|
||||
piospeed = XFER_PIO_2;
|
||||
else if ((piospeed & 0x47) == 0x47)
|
||||
piospeed = XFER_PIO_1;
|
||||
else if ((piospeed & 0x5d) == 0x5d)
|
||||
piospeed = XFER_PIO_0;
|
||||
else
|
||||
goto oem_setup_failed;
|
||||
drive->current_speed = drive->init_speed = piospeed;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
oem_setup_failed:
|
||||
|
||||
pio_timing &= ~0xFF;
|
||||
dma_timing &= ~0xFF;
|
||||
ultra_timing &= ~(0x0F << (4*unit));
|
||||
ultra_enable &= ~(0x01 << drive->dn);
|
||||
csb5_pio &= ~(0x0F << (4*drive->dn));
|
||||
|
||||
switch(speed) {
|
||||
case XFER_PIO_4:
|
||||
case XFER_PIO_3:
|
||||
case XFER_PIO_2:
|
||||
case XFER_PIO_1:
|
||||
case XFER_PIO_0:
|
||||
pio_timing |= pio_modes[speed - XFER_PIO_0];
|
||||
csb5_pio |= ((speed - XFER_PIO_0) << (4*drive->dn));
|
||||
break;
|
||||
|
||||
case XFER_MW_DMA_2:
|
||||
case XFER_MW_DMA_1:
|
||||
case XFER_MW_DMA_0:
|
||||
pio_timing |= pio_modes[pio];
|
||||
csb5_pio |= (pio << (4*drive->dn));
|
||||
dma_timing |= dma_modes[speed - XFER_MW_DMA_0];
|
||||
break;
|
||||
|
||||
case XFER_UDMA_5:
|
||||
case XFER_UDMA_4:
|
||||
case XFER_UDMA_3:
|
||||
case XFER_UDMA_2:
|
||||
case XFER_UDMA_1:
|
||||
case XFER_UDMA_0:
|
||||
pio_timing |= pio_modes[pio];
|
||||
csb5_pio |= (pio << (4*drive->dn));
|
||||
dma_timing |= dma_modes[2];
|
||||
ultra_timing |= ((udma_modes[speed - XFER_UDMA_0]) << (4*unit));
|
||||
ultra_enable |= (0x01 << drive->dn);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pci_write_config_byte(dev, drive_pci[drive->dn], pio_timing);
|
||||
if (csb5)
|
||||
pci_write_config_word(dev, 0x4A, csb5_pio);
|
||||
|
||||
pci_write_config_byte(dev, drive_pci2[drive->dn], dma_timing);
|
||||
pci_write_config_byte(dev, (0x56|hwif->channel), ultra_timing);
|
||||
pci_write_config_byte(dev, 0x54, ultra_enable);
|
||||
|
||||
return (ide_config_drive_speed(drive, speed));
|
||||
}
|
||||
|
||||
static void config_chipset_for_pio (ide_drive_t *drive)
|
||||
{
|
||||
u16 eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
|
||||
u16 xfer_pio = drive->id->eide_pio_modes;
|
||||
u8 timing, speed, pio;
|
||||
|
||||
pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
|
||||
|
||||
if (xfer_pio > 4)
|
||||
xfer_pio = 0;
|
||||
|
||||
if (drive->id->eide_pio_iordy > 0)
|
||||
for (xfer_pio = 5;
|
||||
xfer_pio>0 &&
|
||||
drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
|
||||
xfer_pio--);
|
||||
else
|
||||
xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
|
||||
(drive->id->eide_pio_modes & 2) ? 0x04 :
|
||||
(drive->id->eide_pio_modes & 1) ? 0x03 :
|
||||
(drive->id->tPIO & 2) ? 0x02 :
|
||||
(drive->id->tPIO & 1) ? 0x01 : xfer_pio;
|
||||
|
||||
timing = (xfer_pio >= pio) ? xfer_pio : pio;
|
||||
|
||||
switch(timing) {
|
||||
case 4: speed = XFER_PIO_4;break;
|
||||
case 3: speed = XFER_PIO_3;break;
|
||||
case 2: speed = XFER_PIO_2;break;
|
||||
case 1: speed = XFER_PIO_1;break;
|
||||
default:
|
||||
speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
|
||||
break;
|
||||
}
|
||||
(void) svwks_tune_chipset(drive, speed);
|
||||
drive->current_speed = speed;
|
||||
}
|
||||
|
||||
static void svwks_tune_drive (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
if(pio == 255)
|
||||
(void) svwks_tune_chipset(drive, 255);
|
||||
else
|
||||
(void) svwks_tune_chipset(drive, (XFER_PIO_0 + pio));
|
||||
}
|
||||
|
||||
static int config_chipset_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, svwks_ratemask(drive));
|
||||
|
||||
if (!(speed))
|
||||
speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
|
||||
|
||||
(void) svwks_tune_chipset(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
static int svwks_config_drive_xfer_rate (ide_drive_t *drive)
|
||||
{
|
||||
drive->init_speed = 0;
|
||||
|
||||
if (ide_use_dma(drive) && config_chipset_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
if (ide_use_fast_pio(drive))
|
||||
config_chipset_for_pio(drive);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const char *name)
|
||||
{
|
||||
unsigned int reg;
|
||||
u8 btr;
|
||||
|
||||
/* save revision id to determine DMA capability */
|
||||
pci_read_config_byte(dev, PCI_REVISION_ID, &svwks_revision);
|
||||
|
||||
/* force Master Latency Timer value to 64 PCICLKs */
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x40);
|
||||
|
||||
/* OSB4 : South Bridge and IDE */
|
||||
if (dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) {
|
||||
isa_dev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
|
||||
PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL);
|
||||
if (isa_dev) {
|
||||
pci_read_config_dword(isa_dev, 0x64, ®);
|
||||
reg &= ~0x00002000; /* disable 600ns interrupt mask */
|
||||
if(!(reg & 0x00004000))
|
||||
printk(KERN_DEBUG "%s: UDMA not BIOS enabled.\n", name);
|
||||
reg |= 0x00004000; /* enable UDMA/33 support */
|
||||
pci_write_config_dword(isa_dev, 0x64, reg);
|
||||
}
|
||||
}
|
||||
|
||||
/* setup CSB5/CSB6 : South Bridge and IDE option RAID */
|
||||
else if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) ||
|
||||
(dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) ||
|
||||
(dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) {
|
||||
|
||||
/* Third Channel Test */
|
||||
if (!(PCI_FUNC(dev->devfn) & 1)) {
|
||||
struct pci_dev * findev = NULL;
|
||||
u32 reg4c = 0;
|
||||
findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
|
||||
PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL);
|
||||
if (findev) {
|
||||
pci_read_config_dword(findev, 0x4C, ®4c);
|
||||
reg4c &= ~0x000007FF;
|
||||
reg4c |= 0x00000040;
|
||||
reg4c |= 0x00000020;
|
||||
pci_write_config_dword(findev, 0x4C, reg4c);
|
||||
pci_dev_put(findev);
|
||||
}
|
||||
outb_p(0x06, 0x0c00);
|
||||
dev->irq = inb_p(0x0c01);
|
||||
} else {
|
||||
struct pci_dev * findev = NULL;
|
||||
u8 reg41 = 0;
|
||||
|
||||
findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
|
||||
PCI_DEVICE_ID_SERVERWORKS_CSB6, NULL);
|
||||
if (findev) {
|
||||
pci_read_config_byte(findev, 0x41, ®41);
|
||||
reg41 &= ~0x40;
|
||||
pci_write_config_byte(findev, 0x41, reg41);
|
||||
pci_dev_put(findev);
|
||||
}
|
||||
/*
|
||||
* This is a device pin issue on CSB6.
|
||||
* Since there will be a future raid mode,
|
||||
* early versions of the chipset require the
|
||||
* interrupt pin to be set, and it is a compatibility
|
||||
* mode issue.
|
||||
*/
|
||||
if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE)
|
||||
dev->irq = 0;
|
||||
}
|
||||
// pci_read_config_dword(dev, 0x40, &pioreg)
|
||||
// pci_write_config_dword(dev, 0x40, 0x99999999);
|
||||
// pci_read_config_dword(dev, 0x44, &dmareg);
|
||||
// pci_write_config_dword(dev, 0x44, 0xFFFFFFFF);
|
||||
/* setup the UDMA Control register
|
||||
*
|
||||
* 1. clear bit 6 to enable DMA
|
||||
* 2. enable DMA modes with bits 0-1
|
||||
* 00 : legacy
|
||||
* 01 : udma2
|
||||
* 10 : udma2/udma4
|
||||
* 11 : udma2/udma4/udma5
|
||||
*/
|
||||
pci_read_config_byte(dev, 0x5A, &btr);
|
||||
btr &= ~0x40;
|
||||
if (!(PCI_FUNC(dev->devfn) & 1))
|
||||
btr |= 0x2;
|
||||
else
|
||||
btr |= (svwks_revision >= SVWKS_CSB5_REVISION_NEW) ? 0x3 : 0x2;
|
||||
pci_write_config_byte(dev, 0x5A, btr);
|
||||
}
|
||||
/* Setup HT1000 SouthBridge Controller - Single Channel Only */
|
||||
else if (dev->device == PCI_DEVICE_ID_SERVERWORKS_HT1000IDE) {
|
||||
pci_read_config_byte(dev, 0x5A, &btr);
|
||||
btr &= ~0x40;
|
||||
btr |= 0x3;
|
||||
pci_write_config_byte(dev, 0x5A, btr);
|
||||
}
|
||||
|
||||
return dev->irq;
|
||||
}
|
||||
|
||||
static unsigned int __devinit ata66_svwks_svwks (ide_hwif_t *hwif)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* On Dell PowerEdge servers with a CSB5/CSB6, the top two bits
|
||||
* of the subsystem device ID indicate presence of an 80-pin cable.
|
||||
* Bit 15 clear = secondary IDE channel does not have 80-pin cable.
|
||||
* Bit 15 set = secondary IDE channel has 80-pin cable.
|
||||
* Bit 14 clear = primary IDE channel does not have 80-pin cable.
|
||||
* Bit 14 set = primary IDE channel has 80-pin cable.
|
||||
*/
|
||||
static unsigned int __devinit ata66_svwks_dell (ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL &&
|
||||
dev->vendor == PCI_VENDOR_ID_SERVERWORKS &&
|
||||
(dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE ||
|
||||
dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE))
|
||||
return ((1 << (hwif->channel + 14)) &
|
||||
dev->subsystem_device) ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sun Cobalt Alpine hardware avoids the 80-pin cable
|
||||
* detect issue by attaching the drives directly to the board.
|
||||
* This check follows the Dell precedent (how scary is that?!)
|
||||
*
|
||||
* WARNING: this only works on Alpine hardware!
|
||||
*/
|
||||
static unsigned int __devinit ata66_svwks_cobalt (ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
if (dev->subsystem_vendor == PCI_VENDOR_ID_SUN &&
|
||||
dev->vendor == PCI_VENDOR_ID_SERVERWORKS &&
|
||||
dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE)
|
||||
return ((1 << (hwif->channel + 14)) &
|
||||
dev->subsystem_device) ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int __devinit ata66_svwks (ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
/* Server Works */
|
||||
if (dev->subsystem_vendor == PCI_VENDOR_ID_SERVERWORKS)
|
||||
return ata66_svwks_svwks (hwif);
|
||||
|
||||
/* Dell PowerEdge */
|
||||
if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL)
|
||||
return ata66_svwks_dell (hwif);
|
||||
|
||||
/* Cobalt Alpine */
|
||||
if (dev->subsystem_vendor == PCI_VENDOR_ID_SUN)
|
||||
return ata66_svwks_cobalt (hwif);
|
||||
|
||||
/* Per Specified Design by OEM, and ASIC Architect */
|
||||
if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) ||
|
||||
(dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_svwks (ide_hwif_t *hwif)
|
||||
{
|
||||
u8 dma_stat = 0;
|
||||
|
||||
if (!hwif->irq)
|
||||
hwif->irq = hwif->channel ? 15 : 14;
|
||||
|
||||
hwif->tuneproc = &svwks_tune_drive;
|
||||
hwif->speedproc = &svwks_tune_chipset;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
|
||||
if (hwif->pci_dev->device != PCI_DEVICE_ID_SERVERWORKS_OSB4IDE)
|
||||
hwif->ultra_mask = 0x3f;
|
||||
|
||||
hwif->mwdma_mask = 0x07;
|
||||
|
||||
hwif->autodma = 0;
|
||||
|
||||
if (!hwif->dma_base) {
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].autotune = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
hwif->ide_dma_check = &svwks_config_drive_xfer_rate;
|
||||
if (hwif->pci_dev->device != PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) {
|
||||
if (!hwif->udma_four)
|
||||
hwif->udma_four = ata66_svwks(hwif);
|
||||
}
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
|
||||
dma_stat = inb(hwif->dma_status);
|
||||
hwif->drives[0].autodma = (dma_stat & 0x20);
|
||||
hwif->drives[1].autodma = (dma_stat & 0x40);
|
||||
hwif->drives[0].autotune = (!(dma_stat & 0x20));
|
||||
hwif->drives[1].autotune = (!(dma_stat & 0x40));
|
||||
}
|
||||
|
||||
static int __devinit init_setup_svwks (struct pci_dev *dev, ide_pci_device_t *d)
|
||||
{
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
static int __devinit init_setup_csb6 (struct pci_dev *dev, ide_pci_device_t *d)
|
||||
{
|
||||
if (!(PCI_FUNC(dev->devfn) & 1)) {
|
||||
d->bootable = NEVER_BOARD;
|
||||
if (dev->resource[0].start == 0x01f1)
|
||||
d->bootable = ON_BOARD;
|
||||
}
|
||||
|
||||
d->channels = ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE ||
|
||||
dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2) &&
|
||||
(!(PCI_FUNC(dev->devfn) & 1))) ? 1 : 2;
|
||||
|
||||
return ide_setup_pci_device(dev, d);
|
||||
}
|
||||
|
||||
static ide_pci_device_t serverworks_chipsets[] __devinitdata = {
|
||||
{ /* 0 */
|
||||
.name = "SvrWks OSB4",
|
||||
.init_setup = init_setup_svwks,
|
||||
.init_chipset = init_chipset_svwks,
|
||||
.init_hwif = init_hwif_svwks,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 1 */
|
||||
.name = "SvrWks CSB5",
|
||||
.init_setup = init_setup_svwks,
|
||||
.init_chipset = init_chipset_svwks,
|
||||
.init_hwif = init_hwif_svwks,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 2 */
|
||||
.name = "SvrWks CSB6",
|
||||
.init_setup = init_setup_csb6,
|
||||
.init_chipset = init_chipset_svwks,
|
||||
.init_hwif = init_hwif_svwks,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 3 */
|
||||
.name = "SvrWks CSB6",
|
||||
.init_setup = init_setup_csb6,
|
||||
.init_chipset = init_chipset_svwks,
|
||||
.init_hwif = init_hwif_svwks,
|
||||
.channels = 1, /* 2 */
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
},{ /* 4 */
|
||||
.name = "SvrWks HT1000",
|
||||
.init_setup = init_setup_svwks,
|
||||
.init_chipset = init_chipset_svwks,
|
||||
.init_hwif = init_hwif_svwks,
|
||||
.channels = 1, /* 2 */
|
||||
.autodma = AUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* svwks_init_one - called when a OSB/CSB is found
|
||||
* @dev: the svwks device
|
||||
* @id: the matching pci id
|
||||
*
|
||||
* Called when the PCI registration layer (or the IDE initialization)
|
||||
* finds a device matching our IDE device tables.
|
||||
*/
|
||||
|
||||
static int __devinit svwks_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
ide_pci_device_t *d = &serverworks_chipsets[id->driver_data];
|
||||
|
||||
return d->init_setup(dev, d);
|
||||
}
|
||||
|
||||
static struct pci_device_id svwks_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
|
||||
{ PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2},
|
||||
{ PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3},
|
||||
{ PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, svwks_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "Serverworks_IDE",
|
||||
.id_table = svwks_pci_tbl,
|
||||
.probe = svwks_init_one,
|
||||
};
|
||||
|
||||
static int __init svwks_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(svwks_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Michael Aubry. Andrzej Krzysztofowicz, Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for Serverworks OSB4/CSB5/CSB6 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
761
drivers/ide/pci/sgiioc4.c
Normal file
761
drivers/ide/pci/sgiioc4.c
Normal file
@@ -0,0 +1,761 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2006 Silicon Graphics, Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program; if not, write the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
|
||||
*
|
||||
* For further information regarding this notice, see:
|
||||
*
|
||||
* http://oss.sgi.com/projects/GenInfo/NoticeExplan
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/ioc4.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <linux/ide.h>
|
||||
|
||||
/* IOC4 Specific Definitions */
|
||||
#define IOC4_CMD_OFFSET 0x100
|
||||
#define IOC4_CTRL_OFFSET 0x120
|
||||
#define IOC4_DMA_OFFSET 0x140
|
||||
#define IOC4_INTR_OFFSET 0x0
|
||||
|
||||
#define IOC4_TIMING 0x00
|
||||
#define IOC4_DMA_PTR_L 0x01
|
||||
#define IOC4_DMA_PTR_H 0x02
|
||||
#define IOC4_DMA_ADDR_L 0x03
|
||||
#define IOC4_DMA_ADDR_H 0x04
|
||||
#define IOC4_BC_DEV 0x05
|
||||
#define IOC4_BC_MEM 0x06
|
||||
#define IOC4_DMA_CTRL 0x07
|
||||
#define IOC4_DMA_END_ADDR 0x08
|
||||
|
||||
/* Bits in the IOC4 Control/Status Register */
|
||||
#define IOC4_S_DMA_START 0x01
|
||||
#define IOC4_S_DMA_STOP 0x02
|
||||
#define IOC4_S_DMA_DIR 0x04
|
||||
#define IOC4_S_DMA_ACTIVE 0x08
|
||||
#define IOC4_S_DMA_ERROR 0x10
|
||||
#define IOC4_ATA_MEMERR 0x02
|
||||
|
||||
/* Read/Write Directions */
|
||||
#define IOC4_DMA_WRITE 0x04
|
||||
#define IOC4_DMA_READ 0x00
|
||||
|
||||
/* Interrupt Register Offsets */
|
||||
#define IOC4_INTR_REG 0x03
|
||||
#define IOC4_INTR_SET 0x05
|
||||
#define IOC4_INTR_CLEAR 0x07
|
||||
|
||||
#define IOC4_IDE_CACHELINE_SIZE 128
|
||||
#define IOC4_CMD_CTL_BLK_SIZE 0x20
|
||||
#define IOC4_SUPPORTED_FIRMWARE_REV 46
|
||||
|
||||
typedef struct {
|
||||
u32 timing_reg0;
|
||||
u32 timing_reg1;
|
||||
u32 low_mem_ptr;
|
||||
u32 high_mem_ptr;
|
||||
u32 low_mem_addr;
|
||||
u32 high_mem_addr;
|
||||
u32 dev_byte_count;
|
||||
u32 mem_byte_count;
|
||||
u32 status;
|
||||
} ioc4_dma_regs_t;
|
||||
|
||||
/* Each Physical Region Descriptor Entry size is 16 bytes (2 * 64 bits) */
|
||||
/* IOC4 has only 1 IDE channel */
|
||||
#define IOC4_PRD_BYTES 16
|
||||
#define IOC4_PRD_ENTRIES (PAGE_SIZE /(4*IOC4_PRD_BYTES))
|
||||
|
||||
|
||||
static void
|
||||
sgiioc4_init_hwif_ports(hw_regs_t * hw, unsigned long data_port,
|
||||
unsigned long ctrl_port, unsigned long irq_port)
|
||||
{
|
||||
unsigned long reg = data_port;
|
||||
int i;
|
||||
|
||||
/* Registers are word (32 bit) aligned */
|
||||
for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++)
|
||||
hw->io_ports[i] = reg + i * 4;
|
||||
|
||||
if (ctrl_port)
|
||||
hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
|
||||
|
||||
if (irq_port)
|
||||
hw->io_ports[IDE_IRQ_OFFSET] = irq_port;
|
||||
}
|
||||
|
||||
static void
|
||||
sgiioc4_maskproc(ide_drive_t * drive, int mask)
|
||||
{
|
||||
writeb(mask ? (drive->ctl | 2) : (drive->ctl & ~2),
|
||||
(void __iomem *)IDE_CONTROL_REG);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
sgiioc4_checkirq(ide_hwif_t * hwif)
|
||||
{
|
||||
unsigned long intr_addr =
|
||||
hwif->io_ports[IDE_IRQ_OFFSET] + IOC4_INTR_REG * 4;
|
||||
|
||||
if ((u8)readl((void __iomem *)intr_addr) & 0x03)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 sgiioc4_INB(unsigned long);
|
||||
|
||||
static int
|
||||
sgiioc4_clearirq(ide_drive_t * drive)
|
||||
{
|
||||
u32 intr_reg;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned long other_ir =
|
||||
hwif->io_ports[IDE_IRQ_OFFSET] + (IOC4_INTR_REG << 2);
|
||||
|
||||
/* Code to check for PCI error conditions */
|
||||
intr_reg = readl((void __iomem *)other_ir);
|
||||
if (intr_reg & 0x03) { /* Valid IOC4-IDE interrupt */
|
||||
/*
|
||||
* Using sgiioc4_INB to read the IDE_STATUS_REG has a side effect
|
||||
* of clearing the interrupt. The first read should clear it
|
||||
* if it is set. The second read should return a "clear" status
|
||||
* if it got cleared. If not, then spin for a bit trying to
|
||||
* clear it.
|
||||
*/
|
||||
u8 stat = sgiioc4_INB(IDE_STATUS_REG);
|
||||
int count = 0;
|
||||
stat = sgiioc4_INB(IDE_STATUS_REG);
|
||||
while ((stat & 0x80) && (count++ < 100)) {
|
||||
udelay(1);
|
||||
stat = sgiioc4_INB(IDE_STATUS_REG);
|
||||
}
|
||||
|
||||
if (intr_reg & 0x02) {
|
||||
/* Error when transferring DMA data on PCI bus */
|
||||
u32 pci_err_addr_low, pci_err_addr_high,
|
||||
pci_stat_cmd_reg;
|
||||
|
||||
pci_err_addr_low =
|
||||
readl((void __iomem *)hwif->io_ports[IDE_IRQ_OFFSET]);
|
||||
pci_err_addr_high =
|
||||
readl((void __iomem *)(hwif->io_ports[IDE_IRQ_OFFSET] + 4));
|
||||
pci_read_config_dword(hwif->pci_dev, PCI_COMMAND,
|
||||
&pci_stat_cmd_reg);
|
||||
printk(KERN_ERR
|
||||
"%s(%s) : PCI Bus Error when doing DMA:"
|
||||
" status-cmd reg is 0x%x\n",
|
||||
__FUNCTION__, drive->name, pci_stat_cmd_reg);
|
||||
printk(KERN_ERR
|
||||
"%s(%s) : PCI Error Address is 0x%x%x\n",
|
||||
__FUNCTION__, drive->name,
|
||||
pci_err_addr_high, pci_err_addr_low);
|
||||
/* Clear the PCI Error indicator */
|
||||
pci_write_config_dword(hwif->pci_dev, PCI_COMMAND,
|
||||
0x00000146);
|
||||
}
|
||||
|
||||
/* Clear the Interrupt, Error bits on the IOC4 */
|
||||
writel(0x03, (void __iomem *)other_ir);
|
||||
|
||||
intr_reg = readl((void __iomem *)other_ir);
|
||||
}
|
||||
|
||||
return intr_reg & 3;
|
||||
}
|
||||
|
||||
static void sgiioc4_ide_dma_start(ide_drive_t * drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned long ioc4_dma_addr = hwif->dma_base + IOC4_DMA_CTRL * 4;
|
||||
unsigned int reg = readl((void __iomem *)ioc4_dma_addr);
|
||||
unsigned int temp_reg = reg | IOC4_S_DMA_START;
|
||||
|
||||
writel(temp_reg, (void __iomem *)ioc4_dma_addr);
|
||||
}
|
||||
|
||||
static u32
|
||||
sgiioc4_ide_dma_stop(ide_hwif_t *hwif, u64 dma_base)
|
||||
{
|
||||
unsigned long ioc4_dma_addr = dma_base + IOC4_DMA_CTRL * 4;
|
||||
u32 ioc4_dma;
|
||||
int count;
|
||||
|
||||
count = 0;
|
||||
ioc4_dma = readl((void __iomem *)ioc4_dma_addr);
|
||||
while ((ioc4_dma & IOC4_S_DMA_STOP) && (count++ < 200)) {
|
||||
udelay(1);
|
||||
ioc4_dma = readl((void __iomem *)ioc4_dma_addr);
|
||||
}
|
||||
return ioc4_dma;
|
||||
}
|
||||
|
||||
/* Stops the IOC4 DMA Engine */
|
||||
static int
|
||||
sgiioc4_ide_dma_end(ide_drive_t * drive)
|
||||
{
|
||||
u32 ioc4_dma, bc_dev, bc_mem, num, valid = 0, cnt = 0;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned long dma_base = hwif->dma_base;
|
||||
int dma_stat = 0;
|
||||
unsigned long *ending_dma = ide_get_hwifdata(hwif);
|
||||
|
||||
writel(IOC4_S_DMA_STOP, (void __iomem *)(dma_base + IOC4_DMA_CTRL * 4));
|
||||
|
||||
ioc4_dma = sgiioc4_ide_dma_stop(hwif, dma_base);
|
||||
|
||||
if (ioc4_dma & IOC4_S_DMA_STOP) {
|
||||
printk(KERN_ERR
|
||||
"%s(%s): IOC4 DMA STOP bit is still 1 :"
|
||||
"ioc4_dma_reg 0x%x\n",
|
||||
__FUNCTION__, drive->name, ioc4_dma);
|
||||
dma_stat = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The IOC4 will DMA 1's to the ending dma area to indicate that
|
||||
* previous data DMA is complete. This is necessary because of relaxed
|
||||
* ordering between register reads and DMA writes on the Altix.
|
||||
*/
|
||||
while ((cnt++ < 200) && (!valid)) {
|
||||
for (num = 0; num < 16; num++) {
|
||||
if (ending_dma[num]) {
|
||||
valid = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
if (!valid) {
|
||||
printk(KERN_ERR "%s(%s) : DMA incomplete\n", __FUNCTION__,
|
||||
drive->name);
|
||||
dma_stat = 1;
|
||||
}
|
||||
|
||||
bc_dev = readl((void __iomem *)(dma_base + IOC4_BC_DEV * 4));
|
||||
bc_mem = readl((void __iomem *)(dma_base + IOC4_BC_MEM * 4));
|
||||
|
||||
if ((bc_dev & 0x01FF) || (bc_mem & 0x1FF)) {
|
||||
if (bc_dev > bc_mem + 8) {
|
||||
printk(KERN_ERR
|
||||
"%s(%s): WARNING!! byte_count_dev %d "
|
||||
"!= byte_count_mem %d\n",
|
||||
__FUNCTION__, drive->name, bc_dev, bc_mem);
|
||||
}
|
||||
}
|
||||
|
||||
drive->waiting_for_dma = 0;
|
||||
ide_destroy_dmatable(drive);
|
||||
|
||||
return dma_stat;
|
||||
}
|
||||
|
||||
static int
|
||||
sgiioc4_ide_dma_on(ide_drive_t * drive)
|
||||
{
|
||||
drive->using_dma = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sgiioc4_dma_off_quietly(ide_drive_t *drive)
|
||||
{
|
||||
drive->using_dma = 0;
|
||||
|
||||
drive->hwif->dma_host_off(drive);
|
||||
}
|
||||
|
||||
static int sgiioc4_ide_dma_check(ide_drive_t *drive)
|
||||
{
|
||||
/* FIXME: check for available DMA modes */
|
||||
if (ide_config_drive_speed(drive, XFER_MW_DMA_2) != 0) {
|
||||
printk(KERN_WARNING "%s: couldn't set MWDMA2 mode, "
|
||||
"using PIO instead\n", drive->name);
|
||||
return -1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns 1 if dma irq issued, 0 otherwise */
|
||||
static int
|
||||
sgiioc4_ide_dma_test_irq(ide_drive_t * drive)
|
||||
{
|
||||
return sgiioc4_checkirq(HWIF(drive));
|
||||
}
|
||||
|
||||
static void sgiioc4_dma_host_on(ide_drive_t * drive)
|
||||
{
|
||||
}
|
||||
|
||||
static void sgiioc4_dma_host_off(ide_drive_t * drive)
|
||||
{
|
||||
sgiioc4_clearirq(drive);
|
||||
}
|
||||
|
||||
static int
|
||||
sgiioc4_ide_dma_lostirq(ide_drive_t * drive)
|
||||
{
|
||||
HWIF(drive)->resetproc(drive);
|
||||
|
||||
return __ide_dma_lostirq(drive);
|
||||
}
|
||||
|
||||
static void
|
||||
sgiioc4_resetproc(ide_drive_t * drive)
|
||||
{
|
||||
sgiioc4_ide_dma_end(drive);
|
||||
sgiioc4_clearirq(drive);
|
||||
}
|
||||
|
||||
static u8
|
||||
sgiioc4_INB(unsigned long port)
|
||||
{
|
||||
u8 reg = (u8) readb((void __iomem *) port);
|
||||
|
||||
if ((port & 0xFFF) == 0x11C) { /* Status register of IOC4 */
|
||||
if (reg & 0x51) { /* Not busy...check for interrupt */
|
||||
unsigned long other_ir = port - 0x110;
|
||||
unsigned int intr_reg = (u32) readl((void __iomem *) other_ir);
|
||||
|
||||
/* Clear the Interrupt, Error bits on the IOC4 */
|
||||
if (intr_reg & 0x03) {
|
||||
writel(0x03, (void __iomem *) other_ir);
|
||||
intr_reg = (u32) readl((void __iomem *) other_ir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
/* Creates a dma map for the scatter-gather list entries */
|
||||
static void __devinit
|
||||
ide_dma_sgiioc4(ide_hwif_t * hwif, unsigned long dma_base)
|
||||
{
|
||||
void __iomem *virt_dma_base;
|
||||
int num_ports = sizeof (ioc4_dma_regs_t);
|
||||
void *pad;
|
||||
|
||||
printk(KERN_INFO "%s: BM-DMA at 0x%04lx-0x%04lx\n", hwif->name,
|
||||
dma_base, dma_base + num_ports - 1);
|
||||
|
||||
if (!request_mem_region(dma_base, num_ports, hwif->name)) {
|
||||
printk(KERN_ERR
|
||||
"%s(%s) -- ERROR, Addresses 0x%p to 0x%p "
|
||||
"ALREADY in use\n",
|
||||
__FUNCTION__, hwif->name, (void *) dma_base,
|
||||
(void *) dma_base + num_ports - 1);
|
||||
goto dma_alloc_failure;
|
||||
}
|
||||
|
||||
virt_dma_base = ioremap(dma_base, num_ports);
|
||||
if (virt_dma_base == NULL) {
|
||||
printk(KERN_ERR
|
||||
"%s(%s) -- ERROR, Unable to map addresses 0x%lx to 0x%lx\n",
|
||||
__FUNCTION__, hwif->name, dma_base, dma_base + num_ports - 1);
|
||||
goto dma_remap_failure;
|
||||
}
|
||||
hwif->dma_base = (unsigned long) virt_dma_base;
|
||||
|
||||
hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev,
|
||||
IOC4_PRD_ENTRIES * IOC4_PRD_BYTES,
|
||||
&hwif->dmatable_dma);
|
||||
|
||||
if (!hwif->dmatable_cpu)
|
||||
goto dma_pci_alloc_failure;
|
||||
|
||||
hwif->sg_max_nents = IOC4_PRD_ENTRIES;
|
||||
|
||||
pad = pci_alloc_consistent(hwif->pci_dev, IOC4_IDE_CACHELINE_SIZE,
|
||||
(dma_addr_t *) &(hwif->dma_status));
|
||||
|
||||
if (pad) {
|
||||
ide_set_hwifdata(hwif, pad);
|
||||
return;
|
||||
}
|
||||
|
||||
pci_free_consistent(hwif->pci_dev,
|
||||
IOC4_PRD_ENTRIES * IOC4_PRD_BYTES,
|
||||
hwif->dmatable_cpu, hwif->dmatable_dma);
|
||||
printk(KERN_INFO
|
||||
"%s() -- Error! Unable to allocate DMA Maps for drive %s\n",
|
||||
__FUNCTION__, hwif->name);
|
||||
printk(KERN_INFO
|
||||
"Changing from DMA to PIO mode for Drive %s\n", hwif->name);
|
||||
|
||||
dma_pci_alloc_failure:
|
||||
iounmap(virt_dma_base);
|
||||
|
||||
dma_remap_failure:
|
||||
release_mem_region(dma_base, num_ports);
|
||||
|
||||
dma_alloc_failure:
|
||||
/* Disable DMA because we couldnot allocate any DMA maps */
|
||||
hwif->autodma = 0;
|
||||
hwif->atapi_dma = 0;
|
||||
}
|
||||
|
||||
/* Initializes the IOC4 DMA Engine */
|
||||
static void
|
||||
sgiioc4_configure_for_dma(int dma_direction, ide_drive_t * drive)
|
||||
{
|
||||
u32 ioc4_dma;
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned long dma_base = hwif->dma_base;
|
||||
unsigned long ioc4_dma_addr = dma_base + IOC4_DMA_CTRL * 4;
|
||||
u32 dma_addr, ending_dma_addr;
|
||||
|
||||
ioc4_dma = readl((void __iomem *)ioc4_dma_addr);
|
||||
|
||||
if (ioc4_dma & IOC4_S_DMA_ACTIVE) {
|
||||
printk(KERN_WARNING
|
||||
"%s(%s):Warning!! DMA from previous transfer was still active\n",
|
||||
__FUNCTION__, drive->name);
|
||||
writel(IOC4_S_DMA_STOP, (void __iomem *)ioc4_dma_addr);
|
||||
ioc4_dma = sgiioc4_ide_dma_stop(hwif, dma_base);
|
||||
|
||||
if (ioc4_dma & IOC4_S_DMA_STOP)
|
||||
printk(KERN_ERR
|
||||
"%s(%s) : IOC4 Dma STOP bit is still 1\n",
|
||||
__FUNCTION__, drive->name);
|
||||
}
|
||||
|
||||
ioc4_dma = readl((void __iomem *)ioc4_dma_addr);
|
||||
if (ioc4_dma & IOC4_S_DMA_ERROR) {
|
||||
printk(KERN_WARNING
|
||||
"%s(%s) : Warning!! - DMA Error during Previous"
|
||||
" transfer | status 0x%x\n",
|
||||
__FUNCTION__, drive->name, ioc4_dma);
|
||||
writel(IOC4_S_DMA_STOP, (void __iomem *)ioc4_dma_addr);
|
||||
ioc4_dma = sgiioc4_ide_dma_stop(hwif, dma_base);
|
||||
|
||||
if (ioc4_dma & IOC4_S_DMA_STOP)
|
||||
printk(KERN_ERR
|
||||
"%s(%s) : IOC4 DMA STOP bit is still 1\n",
|
||||
__FUNCTION__, drive->name);
|
||||
}
|
||||
|
||||
/* Address of the Scatter Gather List */
|
||||
dma_addr = cpu_to_le32(hwif->dmatable_dma);
|
||||
writel(dma_addr, (void __iomem *)(dma_base + IOC4_DMA_PTR_L * 4));
|
||||
|
||||
/* Address of the Ending DMA */
|
||||
memset(ide_get_hwifdata(hwif), 0, IOC4_IDE_CACHELINE_SIZE);
|
||||
ending_dma_addr = cpu_to_le32(hwif->dma_status);
|
||||
writel(ending_dma_addr, (void __iomem *)(dma_base + IOC4_DMA_END_ADDR * 4));
|
||||
|
||||
writel(dma_direction, (void __iomem *)ioc4_dma_addr);
|
||||
drive->waiting_for_dma = 1;
|
||||
}
|
||||
|
||||
/* IOC4 Scatter Gather list Format */
|
||||
/* 128 Bit entries to support 64 bit addresses in the future */
|
||||
/* The Scatter Gather list Entry should be in the BIG-ENDIAN Format */
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* | Upper 32 bits - Zero | Lower 32 bits- address | */
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* | Upper 32 bits - Zero |EOL| 15 unused | 16 Bit Length| */
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* Creates the scatter gather list, DMA Table */
|
||||
static unsigned int
|
||||
sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned int *table = hwif->dmatable_cpu;
|
||||
unsigned int count = 0, i = 1;
|
||||
struct scatterlist *sg;
|
||||
|
||||
hwif->sg_nents = i = ide_build_sglist(drive, rq);
|
||||
|
||||
if (!i)
|
||||
return 0; /* sglist of length Zero */
|
||||
|
||||
sg = hwif->sg_table;
|
||||
while (i && sg_dma_len(sg)) {
|
||||
dma_addr_t cur_addr;
|
||||
int cur_len;
|
||||
cur_addr = sg_dma_address(sg);
|
||||
cur_len = sg_dma_len(sg);
|
||||
|
||||
while (cur_len) {
|
||||
if (count++ >= IOC4_PRD_ENTRIES) {
|
||||
printk(KERN_WARNING
|
||||
"%s: DMA table too small\n",
|
||||
drive->name);
|
||||
goto use_pio_instead;
|
||||
} else {
|
||||
u32 bcount =
|
||||
0x10000 - (cur_addr & 0xffff);
|
||||
|
||||
if (bcount > cur_len)
|
||||
bcount = cur_len;
|
||||
|
||||
/* put the addr, length in
|
||||
* the IOC4 dma-table format */
|
||||
*table = 0x0;
|
||||
table++;
|
||||
*table = cpu_to_be32(cur_addr);
|
||||
table++;
|
||||
*table = 0x0;
|
||||
table++;
|
||||
|
||||
*table = cpu_to_be32(bcount);
|
||||
table++;
|
||||
|
||||
cur_addr += bcount;
|
||||
cur_len -= bcount;
|
||||
}
|
||||
}
|
||||
|
||||
sg++;
|
||||
i--;
|
||||
}
|
||||
|
||||
if (count) {
|
||||
table--;
|
||||
*table |= cpu_to_be32(0x80000000);
|
||||
return count;
|
||||
}
|
||||
|
||||
use_pio_instead:
|
||||
pci_unmap_sg(hwif->pci_dev, hwif->sg_table, hwif->sg_nents,
|
||||
hwif->sg_dma_direction);
|
||||
|
||||
return 0; /* revert to PIO for this request */
|
||||
}
|
||||
|
||||
static int sgiioc4_ide_dma_setup(ide_drive_t *drive)
|
||||
{
|
||||
struct request *rq = HWGROUP(drive)->rq;
|
||||
unsigned int count = 0;
|
||||
int ddir;
|
||||
|
||||
if (rq_data_dir(rq))
|
||||
ddir = PCI_DMA_TODEVICE;
|
||||
else
|
||||
ddir = PCI_DMA_FROMDEVICE;
|
||||
|
||||
if (!(count = sgiioc4_build_dma_table(drive, rq, ddir))) {
|
||||
/* try PIO instead of DMA */
|
||||
ide_map_sg(drive, rq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rq_data_dir(rq))
|
||||
/* Writes TO the IOC4 FROM Main Memory */
|
||||
ddir = IOC4_DMA_READ;
|
||||
else
|
||||
/* Writes FROM the IOC4 TO Main Memory */
|
||||
ddir = IOC4_DMA_WRITE;
|
||||
|
||||
sgiioc4_configure_for_dma(ddir, drive);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devinit
|
||||
ide_init_sgiioc4(ide_hwif_t * hwif)
|
||||
{
|
||||
hwif->mmio = 1;
|
||||
hwif->autodma = 1;
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x0; /* Disable Ultra DMA */
|
||||
hwif->mwdma_mask = 0x2; /* Multimode-2 DMA */
|
||||
hwif->swdma_mask = 0x2;
|
||||
hwif->tuneproc = NULL; /* Sets timing for PIO mode */
|
||||
hwif->speedproc = NULL; /* Sets timing for DMA &/or PIO modes */
|
||||
hwif->selectproc = NULL;/* Use the default routine to select drive */
|
||||
hwif->reset_poll = NULL;/* No HBA specific reset_poll needed */
|
||||
hwif->pre_reset = NULL; /* No HBA specific pre_set needed */
|
||||
hwif->resetproc = &sgiioc4_resetproc;/* Reset DMA engine,
|
||||
clear interrupts */
|
||||
hwif->intrproc = NULL; /* Enable or Disable interrupt from drive */
|
||||
hwif->maskproc = &sgiioc4_maskproc; /* Mask on/off NIEN register */
|
||||
hwif->quirkproc = NULL;
|
||||
hwif->busproc = NULL;
|
||||
|
||||
hwif->dma_setup = &sgiioc4_ide_dma_setup;
|
||||
hwif->dma_start = &sgiioc4_ide_dma_start;
|
||||
hwif->ide_dma_end = &sgiioc4_ide_dma_end;
|
||||
hwif->ide_dma_check = &sgiioc4_ide_dma_check;
|
||||
hwif->ide_dma_on = &sgiioc4_ide_dma_on;
|
||||
hwif->dma_off_quietly = &sgiioc4_dma_off_quietly;
|
||||
hwif->ide_dma_test_irq = &sgiioc4_ide_dma_test_irq;
|
||||
hwif->dma_host_on = &sgiioc4_dma_host_on;
|
||||
hwif->dma_host_off = &sgiioc4_dma_host_off;
|
||||
hwif->ide_dma_lostirq = &sgiioc4_ide_dma_lostirq;
|
||||
hwif->ide_dma_timeout = &__ide_dma_timeout;
|
||||
|
||||
hwif->INB = &sgiioc4_INB;
|
||||
}
|
||||
|
||||
static int __devinit
|
||||
sgiioc4_ide_setup_pci_device(struct pci_dev *dev, ide_pci_device_t * d)
|
||||
{
|
||||
unsigned long cmd_base, dma_base, irqport;
|
||||
unsigned long bar0, cmd_phys_base, ctl;
|
||||
void __iomem *virt_base;
|
||||
ide_hwif_t *hwif;
|
||||
int h;
|
||||
|
||||
/*
|
||||
* Find an empty HWIF; if none available, return -ENOMEM.
|
||||
*/
|
||||
for (h = 0; h < MAX_HWIFS; ++h) {
|
||||
hwif = &ide_hwifs[h];
|
||||
if (hwif->chipset == ide_unknown)
|
||||
break;
|
||||
}
|
||||
if (h == MAX_HWIFS) {
|
||||
printk(KERN_ERR "%s: too many IDE interfaces, no room in table\n", d->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Get the CmdBlk and CtrlBlk Base Registers */
|
||||
bar0 = pci_resource_start(dev, 0);
|
||||
virt_base = ioremap(bar0, pci_resource_len(dev, 0));
|
||||
if (virt_base == NULL) {
|
||||
printk(KERN_ERR "%s: Unable to remap BAR 0 address: 0x%lx\n",
|
||||
d->name, bar0);
|
||||
return -ENOMEM;
|
||||
}
|
||||
cmd_base = (unsigned long) virt_base + IOC4_CMD_OFFSET;
|
||||
ctl = (unsigned long) virt_base + IOC4_CTRL_OFFSET;
|
||||
irqport = (unsigned long) virt_base + IOC4_INTR_OFFSET;
|
||||
dma_base = pci_resource_start(dev, 0) + IOC4_DMA_OFFSET;
|
||||
|
||||
cmd_phys_base = bar0 + IOC4_CMD_OFFSET;
|
||||
if (!request_mem_region(cmd_phys_base, IOC4_CMD_CTL_BLK_SIZE,
|
||||
hwif->name)) {
|
||||
printk(KERN_ERR
|
||||
"%s : %s -- ERROR, Addresses "
|
||||
"0x%p to 0x%p ALREADY in use\n",
|
||||
__FUNCTION__, hwif->name, (void *) cmd_phys_base,
|
||||
(void *) cmd_phys_base + IOC4_CMD_CTL_BLK_SIZE);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (hwif->io_ports[IDE_DATA_OFFSET] != cmd_base) {
|
||||
/* Initialize the IO registers */
|
||||
sgiioc4_init_hwif_ports(&hwif->hw, cmd_base, ctl, irqport);
|
||||
memcpy(hwif->io_ports, hwif->hw.io_ports,
|
||||
sizeof (hwif->io_ports));
|
||||
hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
|
||||
}
|
||||
|
||||
hwif->irq = dev->irq;
|
||||
hwif->chipset = ide_pci;
|
||||
hwif->pci_dev = dev;
|
||||
hwif->channel = 0; /* Single Channel chip */
|
||||
hwif->cds = (struct ide_pci_device_s *) d;
|
||||
hwif->gendev.parent = &dev->dev;/* setup proper ancestral information */
|
||||
|
||||
/* The IOC4 uses MMIO rather than Port IO. */
|
||||
default_hwif_mmiops(hwif);
|
||||
|
||||
/* Initializing chipset IRQ Registers */
|
||||
writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4));
|
||||
|
||||
ide_init_sgiioc4(hwif);
|
||||
|
||||
if (dma_base)
|
||||
ide_dma_sgiioc4(hwif, dma_base);
|
||||
else
|
||||
printk(KERN_INFO "%s: %s Bus-Master DMA disabled\n",
|
||||
hwif->name, d->name);
|
||||
|
||||
if (probe_hwif_init(hwif))
|
||||
return -EIO;
|
||||
|
||||
/* Create /proc/ide entries */
|
||||
create_proc_ide_interfaces();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int __devinit
|
||||
pci_init_sgiioc4(struct pci_dev *dev, ide_pci_device_t * d)
|
||||
{
|
||||
unsigned int class_rev;
|
||||
int ret;
|
||||
|
||||
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
|
||||
class_rev &= 0xff;
|
||||
printk(KERN_INFO "%s: IDE controller at PCI slot %s, revision %d\n",
|
||||
d->name, pci_name(dev), class_rev);
|
||||
if (class_rev < IOC4_SUPPORTED_FIRMWARE_REV) {
|
||||
printk(KERN_ERR "Skipping %s IDE controller in slot %s: "
|
||||
"firmware is obsolete - please upgrade to revision"
|
||||
"46 or higher\n", d->name, pci_name(dev));
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
ret = sgiioc4_ide_setup_pci_device(dev, d);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ide_pci_device_t sgiioc4_chipset __devinitdata = {
|
||||
/* Channel 0 */
|
||||
.name = "SGIIOC4",
|
||||
.init_hwif = ide_init_sgiioc4,
|
||||
.init_dma = ide_dma_sgiioc4,
|
||||
.channels = 1,
|
||||
.autodma = AUTODMA,
|
||||
/* SGI IOC4 doesn't have enablebits. */
|
||||
.bootable = ON_BOARD,
|
||||
};
|
||||
|
||||
int
|
||||
ioc4_ide_attach_one(struct ioc4_driver_data *idd)
|
||||
{
|
||||
/* PCI-RT does not bring out IDE connection.
|
||||
* Do not attach to this particular IOC4.
|
||||
*/
|
||||
if (idd->idd_variant == IOC4_VARIANT_PCI_RT)
|
||||
return 0;
|
||||
|
||||
return pci_init_sgiioc4(idd->idd_pdev, &sgiioc4_chipset);
|
||||
}
|
||||
|
||||
static struct ioc4_submodule ioc4_ide_submodule = {
|
||||
.is_name = "IOC4_ide",
|
||||
.is_owner = THIS_MODULE,
|
||||
.is_probe = ioc4_ide_attach_one,
|
||||
/* .is_remove = ioc4_ide_remove_one, */
|
||||
};
|
||||
|
||||
static int __init ioc4_ide_init(void)
|
||||
{
|
||||
return ioc4_register_submodule(&ioc4_ide_submodule);
|
||||
}
|
||||
|
||||
late_initcall(ioc4_ide_init); /* Call only after IDE init is done */
|
||||
|
||||
MODULE_AUTHOR("Aniket Malatpure/Jeremy Higdon");
|
||||
MODULE_DESCRIPTION("IDE PCI driver module for SGI IOC4 Base-IO Card");
|
||||
MODULE_LICENSE("GPL");
|
||||
1100
drivers/ide/pci/siimage.c
Normal file
1100
drivers/ide/pci/siimage.c
Normal file
File diff suppressed because it is too large
Load Diff
943
drivers/ide/pci/sis5513.c
Normal file
943
drivers/ide/pci/sis5513.c
Normal file
@@ -0,0 +1,943 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/sis5513.c Version 0.16ac+vp Jun 18, 2003
|
||||
*
|
||||
* Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2002 Lionel Bouton <Lionel.Bouton@inet6.fr>, Maintainer
|
||||
* Copyright (C) 2003 Vojtech Pavlik <vojtech@suse.cz>
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*
|
||||
*
|
||||
* Thanks :
|
||||
*
|
||||
* SiS Taiwan : for direct support and hardware.
|
||||
* Daniela Engert : for initial ATA100 advices and numerous others.
|
||||
* John Fremlin, Manfred Spraul, Dave Morgan, Peter Kjellerstedt :
|
||||
* for checking code correctness, providing patches.
|
||||
*
|
||||
*
|
||||
* Original tests and design on the SiS620 chipset.
|
||||
* ATA100 tests and design on the SiS735 chipset.
|
||||
* ATA16/33 support from specs
|
||||
* ATA133 support for SiS961/962 by L.C. Chang <lcchang@sis.com.tw>
|
||||
* ATA133 961/962/963 fixes by Vojtech Pavlik <vojtech@suse.cz>
|
||||
*
|
||||
* Documentation:
|
||||
* SiS chipset documentation available under NDA to companies only
|
||||
* (not to individuals).
|
||||
*/
|
||||
|
||||
/*
|
||||
* The original SiS5513 comes from a SiS5511/55112/5513 chipset. The original
|
||||
* SiS5513 was also used in the SiS5596/5513 chipset. Thus if we see a SiS5511
|
||||
* or SiS5596, we can assume we see the first MWDMA-16 capable SiS5513 chip.
|
||||
*
|
||||
* Later SiS chipsets integrated the 5513 functionality into the NorthBridge,
|
||||
* starting with SiS5571 and up to SiS745. The PCI ID didn't change, though. We
|
||||
* can figure out that we have a more modern and more capable 5513 by looking
|
||||
* for the respective NorthBridge IDs.
|
||||
*
|
||||
* Even later (96x family) SiS chipsets use the MuTIOL link and place the 5513
|
||||
* into the SouthBrige. Here we cannot rely on looking up the NorthBridge PCI
|
||||
* ID, while the now ATA-133 capable 5513 still has the same PCI ID.
|
||||
* Fortunately the 5513 can be 'unmasked' by fiddling with some config space
|
||||
* bits, changing its device id to the true one - 5517 for 961 and 5518 for
|
||||
* 962/963.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.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/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
|
||||
#include "ide-timing.h"
|
||||
|
||||
#define DISPLAY_SIS_TIMINGS
|
||||
|
||||
/* registers layout and init values are chipset family dependant */
|
||||
|
||||
#define ATA_16 0x01
|
||||
#define ATA_33 0x02
|
||||
#define ATA_66 0x03
|
||||
#define ATA_100a 0x04 // SiS730/SiS550 is ATA100 with ATA66 layout
|
||||
#define ATA_100 0x05
|
||||
#define ATA_133a 0x06 // SiS961b with 133 support
|
||||
#define ATA_133 0x07 // SiS962/963
|
||||
|
||||
static u8 chipset_family;
|
||||
|
||||
/*
|
||||
* Devices supported
|
||||
*/
|
||||
static const struct {
|
||||
const char *name;
|
||||
u16 host_id;
|
||||
u8 chipset_family;
|
||||
u8 flags;
|
||||
} SiSHostChipInfo[] = {
|
||||
{ "SiS968", PCI_DEVICE_ID_SI_968, ATA_133 },
|
||||
{ "SiS966", PCI_DEVICE_ID_SI_966, ATA_133 },
|
||||
{ "SiS965", PCI_DEVICE_ID_SI_965, ATA_133 },
|
||||
{ "SiS745", PCI_DEVICE_ID_SI_745, ATA_100 },
|
||||
{ "SiS735", PCI_DEVICE_ID_SI_735, ATA_100 },
|
||||
{ "SiS733", PCI_DEVICE_ID_SI_733, ATA_100 },
|
||||
{ "SiS635", PCI_DEVICE_ID_SI_635, ATA_100 },
|
||||
{ "SiS633", PCI_DEVICE_ID_SI_633, ATA_100 },
|
||||
|
||||
{ "SiS730", PCI_DEVICE_ID_SI_730, ATA_100a },
|
||||
{ "SiS550", PCI_DEVICE_ID_SI_550, ATA_100a },
|
||||
|
||||
{ "SiS640", PCI_DEVICE_ID_SI_640, ATA_66 },
|
||||
{ "SiS630", PCI_DEVICE_ID_SI_630, ATA_66 },
|
||||
{ "SiS620", PCI_DEVICE_ID_SI_620, ATA_66 },
|
||||
{ "SiS540", PCI_DEVICE_ID_SI_540, ATA_66 },
|
||||
{ "SiS530", PCI_DEVICE_ID_SI_530, ATA_66 },
|
||||
|
||||
{ "SiS5600", PCI_DEVICE_ID_SI_5600, ATA_33 },
|
||||
{ "SiS5598", PCI_DEVICE_ID_SI_5598, ATA_33 },
|
||||
{ "SiS5597", PCI_DEVICE_ID_SI_5597, ATA_33 },
|
||||
{ "SiS5591/2", PCI_DEVICE_ID_SI_5591, ATA_33 },
|
||||
{ "SiS5582", PCI_DEVICE_ID_SI_5582, ATA_33 },
|
||||
{ "SiS5581", PCI_DEVICE_ID_SI_5581, ATA_33 },
|
||||
|
||||
{ "SiS5596", PCI_DEVICE_ID_SI_5596, ATA_16 },
|
||||
{ "SiS5571", PCI_DEVICE_ID_SI_5571, ATA_16 },
|
||||
{ "SiS5517", PCI_DEVICE_ID_SI_5517, ATA_16 },
|
||||
{ "SiS551x", PCI_DEVICE_ID_SI_5511, ATA_16 },
|
||||
};
|
||||
|
||||
/* Cycle time bits and values vary across chip dma capabilities
|
||||
These three arrays hold the register layout and the values to set.
|
||||
Indexed by chipset_family and (dma_mode - XFER_UDMA_0) */
|
||||
|
||||
/* {0, ATA_16, ATA_33, ATA_66, ATA_100a, ATA_100, ATA_133} */
|
||||
static u8 cycle_time_offset[] = {0,0,5,4,4,0,0};
|
||||
static u8 cycle_time_range[] = {0,0,2,3,3,4,4};
|
||||
static u8 cycle_time_value[][XFER_UDMA_6 - XFER_UDMA_0 + 1] = {
|
||||
{0,0,0,0,0,0,0}, /* no udma */
|
||||
{0,0,0,0,0,0,0}, /* no udma */
|
||||
{3,2,1,0,0,0,0}, /* ATA_33 */
|
||||
{7,5,3,2,1,0,0}, /* ATA_66 */
|
||||
{7,5,3,2,1,0,0}, /* ATA_100a (730 specific), differences are on cycle_time range and offset */
|
||||
{11,7,5,4,2,1,0}, /* ATA_100 */
|
||||
{15,10,7,5,3,2,1}, /* ATA_133a (earliest 691 southbridges) */
|
||||
{15,10,7,5,3,2,1}, /* ATA_133 */
|
||||
};
|
||||
/* CRC Valid Setup Time vary across IDE clock setting 33/66/100/133
|
||||
See SiS962 data sheet for more detail */
|
||||
static u8 cvs_time_value[][XFER_UDMA_6 - XFER_UDMA_0 + 1] = {
|
||||
{0,0,0,0,0,0,0}, /* no udma */
|
||||
{0,0,0,0,0,0,0}, /* no udma */
|
||||
{2,1,1,0,0,0,0},
|
||||
{4,3,2,1,0,0,0},
|
||||
{4,3,2,1,0,0,0},
|
||||
{6,4,3,1,1,1,0},
|
||||
{9,6,4,2,2,2,2},
|
||||
{9,6,4,2,2,2,2},
|
||||
};
|
||||
/* Initialize time, Active time, Recovery time vary across
|
||||
IDE clock settings. These 3 arrays hold the register value
|
||||
for PIO0/1/2/3/4 and DMA0/1/2 mode in order */
|
||||
static u8 ini_time_value[][8] = {
|
||||
{0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0},
|
||||
{2,1,0,0,0,1,0,0},
|
||||
{4,3,1,1,1,3,1,1},
|
||||
{4,3,1,1,1,3,1,1},
|
||||
{6,4,2,2,2,4,2,2},
|
||||
{9,6,3,3,3,6,3,3},
|
||||
{9,6,3,3,3,6,3,3},
|
||||
};
|
||||
static u8 act_time_value[][8] = {
|
||||
{0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0},
|
||||
{9,9,9,2,2,7,2,2},
|
||||
{19,19,19,5,4,14,5,4},
|
||||
{19,19,19,5,4,14,5,4},
|
||||
{28,28,28,7,6,21,7,6},
|
||||
{38,38,38,10,9,28,10,9},
|
||||
{38,38,38,10,9,28,10,9},
|
||||
};
|
||||
static u8 rco_time_value[][8] = {
|
||||
{0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0},
|
||||
{9,2,0,2,0,7,1,1},
|
||||
{19,5,1,5,2,16,3,2},
|
||||
{19,5,1,5,2,16,3,2},
|
||||
{30,9,3,9,4,25,6,4},
|
||||
{40,12,4,12,5,34,12,5},
|
||||
{40,12,4,12,5,34,12,5},
|
||||
};
|
||||
|
||||
/*
|
||||
* Printing configuration
|
||||
*/
|
||||
/* Used for chipset type printing at boot time */
|
||||
static char* chipset_capability[] = {
|
||||
"ATA", "ATA 16",
|
||||
"ATA 33", "ATA 66",
|
||||
"ATA 100 (1st gen)", "ATA 100 (2nd gen)",
|
||||
"ATA 133 (1st gen)", "ATA 133 (2nd gen)"
|
||||
};
|
||||
|
||||
#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS)
|
||||
#include <linux/stat.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
static u8 sis_proc = 0;
|
||||
|
||||
static struct pci_dev *bmide_dev;
|
||||
|
||||
static char* cable_type[] = {
|
||||
"80 pins",
|
||||
"40 pins"
|
||||
};
|
||||
|
||||
static char* recovery_time[] ={
|
||||
"12 PCICLK", "1 PCICLK",
|
||||
"2 PCICLK", "3 PCICLK",
|
||||
"4 PCICLK", "5 PCICLCK",
|
||||
"6 PCICLK", "7 PCICLCK",
|
||||
"8 PCICLK", "9 PCICLCK",
|
||||
"10 PCICLK", "11 PCICLK",
|
||||
"13 PCICLK", "14 PCICLK",
|
||||
"15 PCICLK", "15 PCICLK"
|
||||
};
|
||||
|
||||
static char* active_time[] = {
|
||||
"8 PCICLK", "1 PCICLCK",
|
||||
"2 PCICLK", "3 PCICLK",
|
||||
"4 PCICLK", "5 PCICLK",
|
||||
"6 PCICLK", "12 PCICLK"
|
||||
};
|
||||
|
||||
static char* cycle_time[] = {
|
||||
"Reserved", "2 CLK",
|
||||
"3 CLK", "4 CLK",
|
||||
"5 CLK", "6 CLK",
|
||||
"7 CLK", "8 CLK",
|
||||
"9 CLK", "10 CLK",
|
||||
"11 CLK", "12 CLK",
|
||||
"13 CLK", "14 CLK",
|
||||
"15 CLK", "16 CLK"
|
||||
};
|
||||
|
||||
/* Generic add master or slave info function */
|
||||
static char* get_drives_info (char *buffer, u8 pos)
|
||||
{
|
||||
u8 reg00, reg01, reg10, reg11; /* timing registers */
|
||||
u32 regdw0, regdw1;
|
||||
char* p = buffer;
|
||||
|
||||
/* Postwrite/Prefetch */
|
||||
if (chipset_family < ATA_133) {
|
||||
pci_read_config_byte(bmide_dev, 0x4b, ®00);
|
||||
p += sprintf(p, "Drive %d: Postwrite %s \t \t Postwrite %s\n",
|
||||
pos, (reg00 & (0x10 << pos)) ? "Enabled" : "Disabled",
|
||||
(reg00 & (0x40 << pos)) ? "Enabled" : "Disabled");
|
||||
p += sprintf(p, " Prefetch %s \t \t Prefetch %s\n",
|
||||
(reg00 & (0x01 << pos)) ? "Enabled" : "Disabled",
|
||||
(reg00 & (0x04 << pos)) ? "Enabled" : "Disabled");
|
||||
pci_read_config_byte(bmide_dev, 0x40+2*pos, ®00);
|
||||
pci_read_config_byte(bmide_dev, 0x41+2*pos, ®01);
|
||||
pci_read_config_byte(bmide_dev, 0x44+2*pos, ®10);
|
||||
pci_read_config_byte(bmide_dev, 0x45+2*pos, ®11);
|
||||
} else {
|
||||
u32 reg54h;
|
||||
u8 drive_pci = 0x40;
|
||||
pci_read_config_dword(bmide_dev, 0x54, ®54h);
|
||||
if (reg54h & 0x40000000) {
|
||||
// Configuration space remapped to 0x70
|
||||
drive_pci = 0x70;
|
||||
}
|
||||
pci_read_config_dword(bmide_dev, (unsigned long)drive_pci+4*pos, ®dw0);
|
||||
pci_read_config_dword(bmide_dev, (unsigned long)drive_pci+4*pos+8, ®dw1);
|
||||
|
||||
p += sprintf(p, "Drive %d:\n", pos);
|
||||
}
|
||||
|
||||
|
||||
/* UDMA */
|
||||
if (chipset_family >= ATA_133) {
|
||||
p += sprintf(p, " UDMA %s \t \t \t UDMA %s\n",
|
||||
(regdw0 & 0x04) ? "Enabled" : "Disabled",
|
||||
(regdw1 & 0x04) ? "Enabled" : "Disabled");
|
||||
p += sprintf(p, " UDMA Cycle Time %s \t UDMA Cycle Time %s\n",
|
||||
cycle_time[(regdw0 & 0xF0) >> 4],
|
||||
cycle_time[(regdw1 & 0xF0) >> 4]);
|
||||
} else if (chipset_family >= ATA_33) {
|
||||
p += sprintf(p, " UDMA %s \t \t \t UDMA %s\n",
|
||||
(reg01 & 0x80) ? "Enabled" : "Disabled",
|
||||
(reg11 & 0x80) ? "Enabled" : "Disabled");
|
||||
|
||||
p += sprintf(p, " UDMA Cycle Time ");
|
||||
switch(chipset_family) {
|
||||
case ATA_33: p += sprintf(p, cycle_time[(reg01 & 0x60) >> 5]); break;
|
||||
case ATA_66:
|
||||
case ATA_100a: p += sprintf(p, cycle_time[(reg01 & 0x70) >> 4]); break;
|
||||
case ATA_100:
|
||||
case ATA_133a: p += sprintf(p, cycle_time[reg01 & 0x0F]); break;
|
||||
default: p += sprintf(p, "?"); break;
|
||||
}
|
||||
p += sprintf(p, " \t UDMA Cycle Time ");
|
||||
switch(chipset_family) {
|
||||
case ATA_33: p += sprintf(p, cycle_time[(reg11 & 0x60) >> 5]); break;
|
||||
case ATA_66:
|
||||
case ATA_100a: p += sprintf(p, cycle_time[(reg11 & 0x70) >> 4]); break;
|
||||
case ATA_100:
|
||||
case ATA_133a: p += sprintf(p, cycle_time[reg11 & 0x0F]); break;
|
||||
default: p += sprintf(p, "?"); break;
|
||||
}
|
||||
p += sprintf(p, "\n");
|
||||
}
|
||||
|
||||
|
||||
if (chipset_family < ATA_133) { /* else case TODO */
|
||||
|
||||
/* Data Active */
|
||||
p += sprintf(p, " Data Active Time ");
|
||||
switch(chipset_family) {
|
||||
case ATA_16: /* confirmed */
|
||||
case ATA_33:
|
||||
case ATA_66:
|
||||
case ATA_100a: p += sprintf(p, active_time[reg01 & 0x07]); break;
|
||||
case ATA_100:
|
||||
case ATA_133a: p += sprintf(p, active_time[(reg00 & 0x70) >> 4]); break;
|
||||
default: p += sprintf(p, "?"); break;
|
||||
}
|
||||
p += sprintf(p, " \t Data Active Time ");
|
||||
switch(chipset_family) {
|
||||
case ATA_16:
|
||||
case ATA_33:
|
||||
case ATA_66:
|
||||
case ATA_100a: p += sprintf(p, active_time[reg11 & 0x07]); break;
|
||||
case ATA_100:
|
||||
case ATA_133a: p += sprintf(p, active_time[(reg10 & 0x70) >> 4]); break;
|
||||
default: p += sprintf(p, "?"); break;
|
||||
}
|
||||
p += sprintf(p, "\n");
|
||||
|
||||
/* Data Recovery */
|
||||
/* warning: may need (reg&0x07) for pre ATA66 chips */
|
||||
p += sprintf(p, " Data Recovery Time %s \t Data Recovery Time %s\n",
|
||||
recovery_time[reg00 & 0x0f], recovery_time[reg10 & 0x0f]);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static char* get_masters_info(char* buffer)
|
||||
{
|
||||
return get_drives_info(buffer, 0);
|
||||
}
|
||||
|
||||
static char* get_slaves_info(char* buffer)
|
||||
{
|
||||
return get_drives_info(buffer, 1);
|
||||
}
|
||||
|
||||
/* Main get_info, called on /proc/ide/sis reads */
|
||||
static int sis_get_info (char *buffer, char **addr, off_t offset, int count)
|
||||
{
|
||||
char *p = buffer;
|
||||
int len;
|
||||
u8 reg;
|
||||
u16 reg2, reg3;
|
||||
|
||||
p += sprintf(p, "\nSiS 5513 ");
|
||||
switch(chipset_family) {
|
||||
case ATA_16: p += sprintf(p, "DMA 16"); break;
|
||||
case ATA_33: p += sprintf(p, "Ultra 33"); break;
|
||||
case ATA_66: p += sprintf(p, "Ultra 66"); break;
|
||||
case ATA_100a:
|
||||
case ATA_100: p += sprintf(p, "Ultra 100"); break;
|
||||
case ATA_133a:
|
||||
case ATA_133: p += sprintf(p, "Ultra 133"); break;
|
||||
default: p+= sprintf(p, "Unknown???"); break;
|
||||
}
|
||||
p += sprintf(p, " chipset\n");
|
||||
p += sprintf(p, "--------------- Primary Channel "
|
||||
"---------------- Secondary Channel "
|
||||
"-------------\n");
|
||||
|
||||
/* Status */
|
||||
pci_read_config_byte(bmide_dev, 0x4a, ®);
|
||||
if (chipset_family == ATA_133) {
|
||||
pci_read_config_word(bmide_dev, 0x50, ®2);
|
||||
pci_read_config_word(bmide_dev, 0x52, ®3);
|
||||
}
|
||||
p += sprintf(p, "Channel Status: ");
|
||||
if (chipset_family < ATA_66) {
|
||||
p += sprintf(p, "%s \t \t \t \t %s\n",
|
||||
(reg & 0x04) ? "On" : "Off",
|
||||
(reg & 0x02) ? "On" : "Off");
|
||||
} else if (chipset_family < ATA_133) {
|
||||
p += sprintf(p, "%s \t \t \t \t %s \n",
|
||||
(reg & 0x02) ? "On" : "Off",
|
||||
(reg & 0x04) ? "On" : "Off");
|
||||
} else { /* ATA_133 */
|
||||
p += sprintf(p, "%s \t \t \t \t %s \n",
|
||||
(reg2 & 0x02) ? "On" : "Off",
|
||||
(reg3 & 0x02) ? "On" : "Off");
|
||||
}
|
||||
|
||||
/* Operation Mode */
|
||||
pci_read_config_byte(bmide_dev, 0x09, ®);
|
||||
p += sprintf(p, "Operation Mode: %s \t \t \t %s \n",
|
||||
(reg & 0x01) ? "Native" : "Compatible",
|
||||
(reg & 0x04) ? "Native" : "Compatible");
|
||||
|
||||
/* 80-pin cable ? */
|
||||
if (chipset_family >= ATA_133) {
|
||||
p += sprintf(p, "Cable Type: %s \t \t \t %s\n",
|
||||
(reg2 & 0x01) ? cable_type[1] : cable_type[0],
|
||||
(reg3 & 0x01) ? cable_type[1] : cable_type[0]);
|
||||
} else if (chipset_family > ATA_33) {
|
||||
pci_read_config_byte(bmide_dev, 0x48, ®);
|
||||
p += sprintf(p, "Cable Type: %s \t \t \t %s\n",
|
||||
(reg & 0x10) ? cable_type[1] : cable_type[0],
|
||||
(reg & 0x20) ? cable_type[1] : cable_type[0]);
|
||||
}
|
||||
|
||||
/* Prefetch Count */
|
||||
if (chipset_family < ATA_133) {
|
||||
pci_read_config_word(bmide_dev, 0x4c, ®2);
|
||||
pci_read_config_word(bmide_dev, 0x4e, ®3);
|
||||
p += sprintf(p, "Prefetch Count: %d \t \t \t \t %d\n",
|
||||
reg2, reg3);
|
||||
}
|
||||
|
||||
p = get_masters_info(p);
|
||||
p = get_slaves_info(p);
|
||||
|
||||
len = (p - buffer) - offset;
|
||||
*addr = buffer + offset;
|
||||
|
||||
return len > count ? count : len;
|
||||
}
|
||||
#endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */
|
||||
|
||||
static u8 sis5513_ratemask (ide_drive_t *drive)
|
||||
{
|
||||
u8 rates[] = { 0, 0, 1, 2, 3, 3, 4, 4 };
|
||||
u8 mode = rates[chipset_family];
|
||||
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min(mode, (u8)1);
|
||||
return mode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configuration functions
|
||||
*/
|
||||
/* Enables per-drive prefetch and postwrite */
|
||||
static void config_drive_art_rwp (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
u8 reg4bh = 0;
|
||||
u8 rw_prefetch = (0x11 << drive->dn);
|
||||
|
||||
if (drive->media != ide_disk)
|
||||
return;
|
||||
pci_read_config_byte(dev, 0x4b, ®4bh);
|
||||
|
||||
if ((reg4bh & rw_prefetch) != rw_prefetch)
|
||||
pci_write_config_byte(dev, 0x4b, reg4bh|rw_prefetch);
|
||||
}
|
||||
|
||||
|
||||
/* Set per-drive active and recovery time */
|
||||
static void config_art_rwp_pio (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
u8 timing, drive_pci, test1, test2;
|
||||
|
||||
u16 eide_pio_timing[6] = {600, 390, 240, 180, 120, 90};
|
||||
u16 xfer_pio = drive->id->eide_pio_modes;
|
||||
|
||||
config_drive_art_rwp(drive);
|
||||
pio = ide_get_best_pio_mode(drive, 255, pio, NULL);
|
||||
|
||||
if (xfer_pio> 4)
|
||||
xfer_pio = 0;
|
||||
|
||||
if (drive->id->eide_pio_iordy > 0) {
|
||||
for (xfer_pio = 5;
|
||||
(xfer_pio > 0) &&
|
||||
(drive->id->eide_pio_iordy > eide_pio_timing[xfer_pio]);
|
||||
xfer_pio--);
|
||||
} else {
|
||||
xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
|
||||
(drive->id->eide_pio_modes & 2) ? 0x04 :
|
||||
(drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
|
||||
}
|
||||
|
||||
timing = (xfer_pio >= pio) ? xfer_pio : pio;
|
||||
|
||||
/* In pre ATA_133 case, drives sit at 0x40 + 4*drive->dn */
|
||||
drive_pci = 0x40;
|
||||
/* In SiS962 case drives sit at (0x40 or 0x70) + 8*drive->dn) */
|
||||
if (chipset_family >= ATA_133) {
|
||||
u32 reg54h;
|
||||
pci_read_config_dword(dev, 0x54, ®54h);
|
||||
if (reg54h & 0x40000000) drive_pci = 0x70;
|
||||
drive_pci += ((drive->dn)*0x4);
|
||||
} else {
|
||||
drive_pci += ((drive->dn)*0x2);
|
||||
}
|
||||
|
||||
/* register layout changed with newer ATA100 chips */
|
||||
if (chipset_family < ATA_100) {
|
||||
pci_read_config_byte(dev, drive_pci, &test1);
|
||||
pci_read_config_byte(dev, drive_pci+1, &test2);
|
||||
|
||||
/* Clear active and recovery timings */
|
||||
test1 &= ~0x0F;
|
||||
test2 &= ~0x07;
|
||||
|
||||
switch(timing) {
|
||||
case 4: test1 |= 0x01; test2 |= 0x03; break;
|
||||
case 3: test1 |= 0x03; test2 |= 0x03; break;
|
||||
case 2: test1 |= 0x04; test2 |= 0x04; break;
|
||||
case 1: test1 |= 0x07; test2 |= 0x06; break;
|
||||
default: break;
|
||||
}
|
||||
pci_write_config_byte(dev, drive_pci, test1);
|
||||
pci_write_config_byte(dev, drive_pci+1, test2);
|
||||
} else if (chipset_family < ATA_133) {
|
||||
switch(timing) { /* active recovery
|
||||
v v */
|
||||
case 4: test1 = 0x30|0x01; break;
|
||||
case 3: test1 = 0x30|0x03; break;
|
||||
case 2: test1 = 0x40|0x04; break;
|
||||
case 1: test1 = 0x60|0x07; break;
|
||||
case 0: test1 = 0x00; break;
|
||||
default: break;
|
||||
}
|
||||
pci_write_config_byte(dev, drive_pci, test1);
|
||||
} else { /* ATA_133 */
|
||||
u32 test3;
|
||||
pci_read_config_dword(dev, drive_pci, &test3);
|
||||
test3 &= 0xc0c00fff;
|
||||
if (test3 & 0x08) {
|
||||
test3 |= (unsigned long)ini_time_value[ATA_133][timing] << 12;
|
||||
test3 |= (unsigned long)act_time_value[ATA_133][timing] << 16;
|
||||
test3 |= (unsigned long)rco_time_value[ATA_133][timing] << 24;
|
||||
} else {
|
||||
test3 |= (unsigned long)ini_time_value[ATA_100][timing] << 12;
|
||||
test3 |= (unsigned long)act_time_value[ATA_100][timing] << 16;
|
||||
test3 |= (unsigned long)rco_time_value[ATA_100][timing] << 24;
|
||||
}
|
||||
pci_write_config_dword(dev, drive_pci, test3);
|
||||
}
|
||||
}
|
||||
|
||||
static int config_chipset_for_pio (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
if (pio == 255)
|
||||
pio = ide_find_best_mode(drive, XFER_PIO | XFER_EPIO) - XFER_PIO_0;
|
||||
config_art_rwp_pio(drive, pio);
|
||||
return ide_config_drive_speed(drive, XFER_PIO_0 + min_t(u8, pio, 4));
|
||||
}
|
||||
|
||||
static int sis5513_tune_chipset (ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
u8 drive_pci, reg, speed;
|
||||
u32 regdw;
|
||||
|
||||
speed = ide_rate_filter(sis5513_ratemask(drive), xferspeed);
|
||||
|
||||
/* See config_art_rwp_pio for drive pci config registers */
|
||||
drive_pci = 0x40;
|
||||
if (chipset_family >= ATA_133) {
|
||||
u32 reg54h;
|
||||
pci_read_config_dword(dev, 0x54, ®54h);
|
||||
if (reg54h & 0x40000000) drive_pci = 0x70;
|
||||
drive_pci += ((drive->dn)*0x4);
|
||||
pci_read_config_dword(dev, (unsigned long)drive_pci, ®dw);
|
||||
/* Disable UDMA bit for non UDMA modes on UDMA chips */
|
||||
if (speed < XFER_UDMA_0) {
|
||||
regdw &= 0xfffffffb;
|
||||
pci_write_config_dword(dev, (unsigned long)drive_pci, regdw);
|
||||
}
|
||||
|
||||
} else {
|
||||
drive_pci += ((drive->dn)*0x2);
|
||||
pci_read_config_byte(dev, drive_pci+1, ®);
|
||||
/* Disable UDMA bit for non UDMA modes on UDMA chips */
|
||||
if ((speed < XFER_UDMA_0) && (chipset_family > ATA_16)) {
|
||||
reg &= 0x7F;
|
||||
pci_write_config_byte(dev, drive_pci+1, reg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Config chip for mode */
|
||||
switch(speed) {
|
||||
case XFER_UDMA_6:
|
||||
case XFER_UDMA_5:
|
||||
case XFER_UDMA_4:
|
||||
case XFER_UDMA_3:
|
||||
case XFER_UDMA_2:
|
||||
case XFER_UDMA_1:
|
||||
case XFER_UDMA_0:
|
||||
if (chipset_family >= ATA_133) {
|
||||
regdw |= 0x04;
|
||||
regdw &= 0xfffff00f;
|
||||
/* check if ATA133 enable */
|
||||
if (regdw & 0x08) {
|
||||
regdw |= (unsigned long)cycle_time_value[ATA_133][speed-XFER_UDMA_0] << 4;
|
||||
regdw |= (unsigned long)cvs_time_value[ATA_133][speed-XFER_UDMA_0] << 8;
|
||||
} else {
|
||||
/* if ATA133 disable, we should not set speed above UDMA5 */
|
||||
if (speed > XFER_UDMA_5)
|
||||
speed = XFER_UDMA_5;
|
||||
regdw |= (unsigned long)cycle_time_value[ATA_100][speed-XFER_UDMA_0] << 4;
|
||||
regdw |= (unsigned long)cvs_time_value[ATA_100][speed-XFER_UDMA_0] << 8;
|
||||
}
|
||||
pci_write_config_dword(dev, (unsigned long)drive_pci, regdw);
|
||||
} else {
|
||||
/* Force the UDMA bit on if we want to use UDMA */
|
||||
reg |= 0x80;
|
||||
/* clean reg cycle time bits */
|
||||
reg &= ~((0xFF >> (8 - cycle_time_range[chipset_family]))
|
||||
<< cycle_time_offset[chipset_family]);
|
||||
/* set reg cycle time bits */
|
||||
reg |= cycle_time_value[chipset_family][speed-XFER_UDMA_0]
|
||||
<< cycle_time_offset[chipset_family];
|
||||
pci_write_config_byte(dev, drive_pci+1, reg);
|
||||
}
|
||||
break;
|
||||
case XFER_MW_DMA_2:
|
||||
case XFER_MW_DMA_1:
|
||||
case XFER_MW_DMA_0:
|
||||
case XFER_SW_DMA_2:
|
||||
case XFER_SW_DMA_1:
|
||||
case XFER_SW_DMA_0:
|
||||
break;
|
||||
case XFER_PIO_4: return((int) config_chipset_for_pio(drive, 4));
|
||||
case XFER_PIO_3: return((int) config_chipset_for_pio(drive, 3));
|
||||
case XFER_PIO_2: return((int) config_chipset_for_pio(drive, 2));
|
||||
case XFER_PIO_1: return((int) config_chipset_for_pio(drive, 1));
|
||||
case XFER_PIO_0:
|
||||
default: return((int) config_chipset_for_pio(drive, 0));
|
||||
}
|
||||
|
||||
return ((int) ide_config_drive_speed(drive, speed));
|
||||
}
|
||||
|
||||
static void sis5513_tune_drive (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
(void) config_chipset_for_pio(drive, pio);
|
||||
}
|
||||
|
||||
/*
|
||||
* ((id->hw_config & 0x4000|0x2000) && (HWIF(drive)->udma_four))
|
||||
*/
|
||||
static int config_chipset_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, sis5513_ratemask(drive));
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("SIS5513: config_chipset_for_dma, drive %d, ultra %x\n",
|
||||
drive->dn, drive->id->dma_ultra);
|
||||
#endif
|
||||
|
||||
if (!(speed))
|
||||
return 0;
|
||||
|
||||
sis5513_tune_chipset(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
static int sis5513_config_xfer_rate(ide_drive_t *drive)
|
||||
{
|
||||
config_art_rwp_pio(drive, 5);
|
||||
|
||||
drive->init_speed = 0;
|
||||
|
||||
if (ide_use_dma(drive) && config_chipset_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
if (ide_use_fast_pio(drive))
|
||||
sis5513_tune_drive(drive, 5);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Chip detection and general config */
|
||||
static unsigned int __devinit init_chipset_sis5513 (struct pci_dev *dev, const char *name)
|
||||
{
|
||||
struct pci_dev *host;
|
||||
int i = 0;
|
||||
|
||||
chipset_family = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(SiSHostChipInfo) && !chipset_family; i++) {
|
||||
|
||||
host = pci_get_device(PCI_VENDOR_ID_SI, SiSHostChipInfo[i].host_id, NULL);
|
||||
|
||||
if (!host)
|
||||
continue;
|
||||
|
||||
chipset_family = SiSHostChipInfo[i].chipset_family;
|
||||
|
||||
/* Special case for SiS630 : 630S/ET is ATA_100a */
|
||||
if (SiSHostChipInfo[i].host_id == PCI_DEVICE_ID_SI_630) {
|
||||
u8 hostrev;
|
||||
pci_read_config_byte(host, PCI_REVISION_ID, &hostrev);
|
||||
if (hostrev >= 0x30)
|
||||
chipset_family = ATA_100a;
|
||||
}
|
||||
pci_dev_put(host);
|
||||
|
||||
printk(KERN_INFO "SIS5513: %s %s controller\n",
|
||||
SiSHostChipInfo[i].name, chipset_capability[chipset_family]);
|
||||
}
|
||||
|
||||
if (!chipset_family) { /* Belongs to pci-quirks */
|
||||
|
||||
u32 idemisc;
|
||||
u16 trueid;
|
||||
|
||||
/* Disable ID masking and register remapping */
|
||||
pci_read_config_dword(dev, 0x54, &idemisc);
|
||||
pci_write_config_dword(dev, 0x54, (idemisc & 0x7fffffff));
|
||||
pci_read_config_word(dev, PCI_DEVICE_ID, &trueid);
|
||||
pci_write_config_dword(dev, 0x54, idemisc);
|
||||
|
||||
if (trueid == 0x5518) {
|
||||
printk(KERN_INFO "SIS5513: SiS 962/963 MuTIOL IDE UDMA133 controller\n");
|
||||
chipset_family = ATA_133;
|
||||
|
||||
/* Check for 5513 compability mapping
|
||||
* We must use this, else the port enabled code will fail,
|
||||
* as it expects the enablebits at 0x4a.
|
||||
*/
|
||||
if ((idemisc & 0x40000000) == 0) {
|
||||
pci_write_config_dword(dev, 0x54, idemisc | 0x40000000);
|
||||
printk(KERN_INFO "SIS5513: Switching to 5513 register mapping\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!chipset_family) { /* Belongs to pci-quirks */
|
||||
|
||||
struct pci_dev *lpc_bridge;
|
||||
u16 trueid;
|
||||
u8 prefctl;
|
||||
u8 idecfg;
|
||||
u8 sbrev;
|
||||
|
||||
pci_read_config_byte(dev, 0x4a, &idecfg);
|
||||
pci_write_config_byte(dev, 0x4a, idecfg | 0x10);
|
||||
pci_read_config_word(dev, PCI_DEVICE_ID, &trueid);
|
||||
pci_write_config_byte(dev, 0x4a, idecfg);
|
||||
|
||||
if (trueid == 0x5517) { /* SiS 961/961B */
|
||||
|
||||
lpc_bridge = pci_get_slot(dev->bus, 0x10); /* Bus 0, Dev 2, Fn 0 */
|
||||
pci_read_config_byte(lpc_bridge, PCI_REVISION_ID, &sbrev);
|
||||
pci_read_config_byte(dev, 0x49, &prefctl);
|
||||
pci_dev_put(lpc_bridge);
|
||||
|
||||
if (sbrev == 0x10 && (prefctl & 0x80)) {
|
||||
printk(KERN_INFO "SIS5513: SiS 961B MuTIOL IDE UDMA133 controller\n");
|
||||
chipset_family = ATA_133a;
|
||||
} else {
|
||||
printk(KERN_INFO "SIS5513: SiS 961 MuTIOL IDE UDMA100 controller\n");
|
||||
chipset_family = ATA_100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!chipset_family)
|
||||
return -1;
|
||||
|
||||
/* Make general config ops here
|
||||
1/ tell IDE channels to operate in Compatibility mode only
|
||||
2/ tell old chips to allow per drive IDE timings */
|
||||
|
||||
{
|
||||
u8 reg;
|
||||
u16 regw;
|
||||
|
||||
switch(chipset_family) {
|
||||
case ATA_133:
|
||||
/* SiS962 operation mode */
|
||||
pci_read_config_word(dev, 0x50, ®w);
|
||||
if (regw & 0x08)
|
||||
pci_write_config_word(dev, 0x50, regw&0xfff7);
|
||||
pci_read_config_word(dev, 0x52, ®w);
|
||||
if (regw & 0x08)
|
||||
pci_write_config_word(dev, 0x52, regw&0xfff7);
|
||||
break;
|
||||
case ATA_133a:
|
||||
case ATA_100:
|
||||
/* Fixup latency */
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x80);
|
||||
/* Set compatibility bit */
|
||||
pci_read_config_byte(dev, 0x49, ®);
|
||||
if (!(reg & 0x01)) {
|
||||
pci_write_config_byte(dev, 0x49, reg|0x01);
|
||||
}
|
||||
break;
|
||||
case ATA_100a:
|
||||
case ATA_66:
|
||||
/* Fixup latency */
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x10);
|
||||
|
||||
/* On ATA_66 chips the bit was elsewhere */
|
||||
pci_read_config_byte(dev, 0x52, ®);
|
||||
if (!(reg & 0x04)) {
|
||||
pci_write_config_byte(dev, 0x52, reg|0x04);
|
||||
}
|
||||
break;
|
||||
case ATA_33:
|
||||
/* On ATA_33 we didn't have a single bit to set */
|
||||
pci_read_config_byte(dev, 0x09, ®);
|
||||
if ((reg & 0x0f) != 0x00) {
|
||||
pci_write_config_byte(dev, 0x09, reg&0xf0);
|
||||
}
|
||||
case ATA_16:
|
||||
/* force per drive recovery and active timings
|
||||
needed on ATA_33 and below chips */
|
||||
pci_read_config_byte(dev, 0x52, ®);
|
||||
if (!(reg & 0x08)) {
|
||||
pci_write_config_byte(dev, 0x52, reg|0x08);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS)
|
||||
if (!sis_proc) {
|
||||
sis_proc = 1;
|
||||
bmide_dev = dev;
|
||||
ide_pci_create_host_proc("sis", sis_get_info);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int __devinit ata66_sis5513 (ide_hwif_t *hwif)
|
||||
{
|
||||
u8 ata66 = 0;
|
||||
|
||||
if (chipset_family >= ATA_133) {
|
||||
u16 regw = 0;
|
||||
u16 reg_addr = hwif->channel ? 0x52: 0x50;
|
||||
pci_read_config_word(hwif->pci_dev, reg_addr, ®w);
|
||||
ata66 = (regw & 0x8000) ? 0 : 1;
|
||||
} else if (chipset_family >= ATA_66) {
|
||||
u8 reg48h = 0;
|
||||
u8 mask = hwif->channel ? 0x20 : 0x10;
|
||||
pci_read_config_byte(hwif->pci_dev, 0x48, ®48h);
|
||||
ata66 = (reg48h & mask) ? 0 : 1;
|
||||
}
|
||||
return ata66;
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_sis5513 (ide_hwif_t *hwif)
|
||||
{
|
||||
hwif->autodma = 0;
|
||||
|
||||
if (!hwif->irq)
|
||||
hwif->irq = hwif->channel ? 15 : 14;
|
||||
|
||||
hwif->tuneproc = &sis5513_tune_drive;
|
||||
hwif->speedproc = &sis5513_tune_chipset;
|
||||
|
||||
if (!(hwif->dma_base)) {
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].autotune = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x7f;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
hwif->swdma_mask = 0x07;
|
||||
|
||||
if (!chipset_family)
|
||||
return;
|
||||
|
||||
if (!(hwif->udma_four))
|
||||
hwif->udma_four = ata66_sis5513(hwif);
|
||||
|
||||
if (chipset_family > ATA_16) {
|
||||
hwif->ide_dma_check = &sis5513_config_xfer_rate;
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
}
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
return;
|
||||
}
|
||||
|
||||
static ide_pci_device_t sis5513_chipset __devinitdata = {
|
||||
.name = "SIS5513",
|
||||
.init_chipset = init_chipset_sis5513,
|
||||
.init_hwif = init_hwif_sis5513,
|
||||
.channels = 2,
|
||||
.autodma = NOAUTODMA,
|
||||
.enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}},
|
||||
.bootable = ON_BOARD,
|
||||
};
|
||||
|
||||
static int __devinit sis5513_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &sis5513_chipset);
|
||||
}
|
||||
|
||||
static struct pci_device_id sis5513_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5518, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, sis5513_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "SIS_IDE",
|
||||
.id_table = sis5513_pci_tbl,
|
||||
.probe = sis5513_init_one,
|
||||
};
|
||||
|
||||
static int __init sis5513_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(sis5513_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Lionel Bouton, L C Chang, Andre Hedrick, Vojtech Pavlik");
|
||||
MODULE_DESCRIPTION("PCI driver module for SIS IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* - CLEANUP
|
||||
* - Use drivers/ide/ide-timing.h !
|
||||
* - More checks in the config registers (force values instead of
|
||||
* relying on the BIOS setting them correctly).
|
||||
* - Further optimisations ?
|
||||
* . for example ATA66+ regs 0x48 & 0x4A
|
||||
*/
|
||||
490
drivers/ide/pci/sl82c105.c
Normal file
490
drivers/ide/pci/sl82c105.c
Normal file
@@ -0,0 +1,490 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/sl82c105.c
|
||||
*
|
||||
* SL82C105/Winbond 553 IDE driver
|
||||
*
|
||||
* Maintainer unknown.
|
||||
*
|
||||
* Drive tuning added from Rebel.com's kernel sources
|
||||
* -- Russell King (15/11/98) linux@arm.linux.org.uk
|
||||
*
|
||||
* Merge in Russell's HW workarounds, fix various problems
|
||||
* with the timing registers setup.
|
||||
* -- Benjamin Herrenschmidt (01/11/03) benh@kernel.crashing.org
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/dma.h>
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(arg) printk arg
|
||||
#else
|
||||
#define DBG(fmt,...)
|
||||
#endif
|
||||
/*
|
||||
* SL82C105 PCI config register 0x40 bits.
|
||||
*/
|
||||
#define CTRL_IDE_IRQB (1 << 30)
|
||||
#define CTRL_IDE_IRQA (1 << 28)
|
||||
#define CTRL_LEGIRQ (1 << 11)
|
||||
#define CTRL_P1F16 (1 << 5)
|
||||
#define CTRL_P1EN (1 << 4)
|
||||
#define CTRL_P0F16 (1 << 1)
|
||||
#define CTRL_P0EN (1 << 0)
|
||||
|
||||
/*
|
||||
* Convert a PIO mode and cycle time to the required on/off
|
||||
* times for the interface. This has protection against run-away
|
||||
* timings.
|
||||
*/
|
||||
static unsigned int get_timing_sl82c105(ide_pio_data_t *p)
|
||||
{
|
||||
unsigned int cmd_on;
|
||||
unsigned int cmd_off;
|
||||
|
||||
cmd_on = (ide_pio_timings[p->pio_mode].active_time + 29) / 30;
|
||||
cmd_off = (p->cycle_time - 30 * cmd_on + 29) / 30;
|
||||
|
||||
if (cmd_on > 32)
|
||||
cmd_on = 32;
|
||||
if (cmd_on == 0)
|
||||
cmd_on = 1;
|
||||
|
||||
if (cmd_off > 32)
|
||||
cmd_off = 32;
|
||||
if (cmd_off == 0)
|
||||
cmd_off = 1;
|
||||
|
||||
return (cmd_on - 1) << 8 | (cmd_off - 1) | (p->use_iordy ? 0x40 : 0x00);
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the drive and chipset for PIO
|
||||
*/
|
||||
static void config_for_pio(ide_drive_t *drive, int pio, int report, int chipset_only)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
ide_pio_data_t p;
|
||||
u16 drv_ctrl = 0x909;
|
||||
unsigned int xfer_mode, reg;
|
||||
|
||||
DBG(("config_for_pio(drive:%s, pio:%d, report:%d, chipset_only:%d)\n",
|
||||
drive->name, pio, report, chipset_only));
|
||||
|
||||
reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0);
|
||||
|
||||
pio = ide_get_best_pio_mode(drive, pio, 5, &p);
|
||||
|
||||
xfer_mode = XFER_PIO_0 + pio;
|
||||
|
||||
if (chipset_only || ide_config_drive_speed(drive, xfer_mode) == 0) {
|
||||
drv_ctrl = get_timing_sl82c105(&p);
|
||||
drive->pio_speed = xfer_mode;
|
||||
} else
|
||||
drive->pio_speed = XFER_PIO_0;
|
||||
|
||||
if (drive->using_dma == 0) {
|
||||
/*
|
||||
* If we are actually using MW DMA, then we can not
|
||||
* reprogram the interface drive control register.
|
||||
*/
|
||||
pci_write_config_word(dev, reg, drv_ctrl);
|
||||
pci_read_config_word(dev, reg, &drv_ctrl);
|
||||
|
||||
if (report) {
|
||||
printk("%s: selected %s (%dns) (%04X)\n", drive->name,
|
||||
ide_xfer_verbose(xfer_mode), p.cycle_time, drv_ctrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the drive and the chipset for DMA
|
||||
*/
|
||||
static int config_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
unsigned int reg;
|
||||
|
||||
DBG(("config_for_dma(drive:%s)\n", drive->name));
|
||||
|
||||
reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0);
|
||||
|
||||
if (ide_config_drive_speed(drive, XFER_MW_DMA_2) != 0)
|
||||
return 1;
|
||||
|
||||
pci_write_config_word(dev, reg, 0x0240);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if the drive and
|
||||
* chipset is capable of DMA mode
|
||||
*/
|
||||
|
||||
static int sl82c105_check_drive (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
|
||||
DBG(("sl82c105_check_drive(drive:%s)\n", drive->name));
|
||||
|
||||
do {
|
||||
struct hd_driveid *id = drive->id;
|
||||
|
||||
if (!drive->autodma)
|
||||
break;
|
||||
|
||||
if (!id || !(id->capability & 1))
|
||||
break;
|
||||
|
||||
/* Consult the list of known "bad" drives */
|
||||
if (__ide_dma_bad_drive(drive))
|
||||
break;
|
||||
|
||||
if (id->field_valid & 2) {
|
||||
if ((id->dma_mword & hwif->mwdma_mask) ||
|
||||
(id->dma_1word & hwif->swdma_mask))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (__ide_dma_good_drive(drive) && id->eide_dma_time < 150)
|
||||
return 0;
|
||||
} while (0);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The SL82C105 holds off all IDE interrupts while in DMA mode until
|
||||
* all DMA activity is completed. Sometimes this causes problems (eg,
|
||||
* when the drive wants to report an error condition).
|
||||
*
|
||||
* 0x7e is a "chip testing" register. Bit 2 resets the DMA controller
|
||||
* state machine. We need to kick this to work around various bugs.
|
||||
*/
|
||||
static inline void sl82c105_reset_host(struct pci_dev *dev)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
pci_read_config_word(dev, 0x7e, &val);
|
||||
pci_write_config_word(dev, 0x7e, val | (1 << 2));
|
||||
pci_write_config_word(dev, 0x7e, val & ~(1 << 2));
|
||||
}
|
||||
|
||||
/*
|
||||
* If we get an IRQ timeout, it might be that the DMA state machine
|
||||
* got confused. Fix from Todd Inglett. Details from Winbond.
|
||||
*
|
||||
* This function is called when the IDE timer expires, the drive
|
||||
* indicates that it is READY, and we were waiting for DMA to complete.
|
||||
*/
|
||||
static int sl82c105_ide_dma_lost_irq(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u32 val, mask = hwif->channel ? CTRL_IDE_IRQB : CTRL_IDE_IRQA;
|
||||
unsigned long dma_base = hwif->dma_base;
|
||||
|
||||
printk("sl82c105: lost IRQ: resetting host\n");
|
||||
|
||||
/*
|
||||
* Check the raw interrupt from the drive.
|
||||
*/
|
||||
pci_read_config_dword(dev, 0x40, &val);
|
||||
if (val & mask)
|
||||
printk("sl82c105: drive was requesting IRQ, but host lost it\n");
|
||||
|
||||
/*
|
||||
* Was DMA enabled? If so, disable it - we're resetting the
|
||||
* host. The IDE layer will be handling the drive for us.
|
||||
*/
|
||||
val = inb(dma_base);
|
||||
if (val & 1) {
|
||||
outb(val & ~1, dma_base);
|
||||
printk("sl82c105: DMA was enabled\n");
|
||||
}
|
||||
|
||||
sl82c105_reset_host(dev);
|
||||
|
||||
/* ide_dmaproc would return 1, so we do as well */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* ATAPI devices can cause the SL82C105 DMA state machine to go gaga.
|
||||
* Winbond recommend that the DMA state machine is reset prior to
|
||||
* setting the bus master DMA enable bit.
|
||||
*
|
||||
* The generic IDE core will have disabled the BMEN bit before this
|
||||
* function is called.
|
||||
*/
|
||||
static void sl82c105_ide_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
sl82c105_reset_host(dev);
|
||||
ide_dma_start(drive);
|
||||
}
|
||||
|
||||
static int sl82c105_ide_dma_timeout(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
DBG(("sl82c105_ide_dma_timeout(drive:%s)\n", drive->name));
|
||||
|
||||
sl82c105_reset_host(dev);
|
||||
return __ide_dma_timeout(drive);
|
||||
}
|
||||
|
||||
static int sl82c105_ide_dma_on (ide_drive_t *drive)
|
||||
{
|
||||
DBG(("sl82c105_ide_dma_on(drive:%s)\n", drive->name));
|
||||
|
||||
if (config_for_dma(drive))
|
||||
return 1;
|
||||
printk(KERN_INFO "%s: DMA enabled\n", drive->name);
|
||||
return __ide_dma_on(drive);
|
||||
}
|
||||
|
||||
static void sl82c105_dma_off_quietly(ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = XFER_PIO_0;
|
||||
|
||||
DBG(("sl82c105_dma_off_quietly(drive:%s)\n", drive->name));
|
||||
|
||||
ide_dma_off_quietly(drive);
|
||||
if (drive->pio_speed)
|
||||
speed = drive->pio_speed - XFER_PIO_0;
|
||||
config_for_pio(drive, speed, 0, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, that is nasty, but we must make sure the DMA timings
|
||||
* won't be used for a PIO access. The solution here is
|
||||
* to make sure the 16 bits mode is diabled on the channel
|
||||
* when DMA is enabled, thus causing the chip to use PIO0
|
||||
* timings for those operations.
|
||||
*/
|
||||
static void sl82c105_selectproc(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u32 val, old, mask;
|
||||
|
||||
//DBG(("sl82c105_selectproc(drive:%s)\n", drive->name));
|
||||
|
||||
mask = hwif->channel ? CTRL_P1F16 : CTRL_P0F16;
|
||||
old = val = (u32)pci_get_drvdata(dev);
|
||||
if (drive->using_dma)
|
||||
val &= ~mask;
|
||||
else
|
||||
val |= mask;
|
||||
if (old != val) {
|
||||
pci_write_config_dword(dev, 0x40, val);
|
||||
pci_set_drvdata(dev, (void *)val);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ATA reset will clear the 16 bits mode in the control
|
||||
* register, we need to update our cache
|
||||
*/
|
||||
static void sl82c105_resetproc(ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = HWIF(drive)->pci_dev;
|
||||
u32 val;
|
||||
|
||||
DBG(("sl82c105_resetproc(drive:%s)\n", drive->name));
|
||||
|
||||
pci_read_config_dword(dev, 0x40, &val);
|
||||
pci_set_drvdata(dev, (void *)val);
|
||||
}
|
||||
|
||||
/*
|
||||
* We only deal with PIO mode here - DMA mode 'using_dma' is not
|
||||
* initialised at the point that this function is called.
|
||||
*/
|
||||
static void tune_sl82c105(ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
DBG(("tune_sl82c105(drive:%s)\n", drive->name));
|
||||
|
||||
config_for_pio(drive, pio, 1, 0);
|
||||
|
||||
/*
|
||||
* We support 32-bit I/O on this interface, and it
|
||||
* doesn't have problems with interrupts.
|
||||
*/
|
||||
drive->io_32bit = 1;
|
||||
drive->unmask = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the revision of the Winbond bridge
|
||||
* which this function is part of.
|
||||
*/
|
||||
static unsigned int sl82c105_bridge_revision(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_dev *bridge;
|
||||
u8 rev;
|
||||
|
||||
/*
|
||||
* The bridge should be part of the same device, but function 0.
|
||||
*/
|
||||
bridge = pci_find_slot(dev->bus->number,
|
||||
PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
|
||||
if (!bridge)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Make sure it is a Winbond 553 and is an ISA bridge.
|
||||
*/
|
||||
if (bridge->vendor != PCI_VENDOR_ID_WINBOND ||
|
||||
bridge->device != PCI_DEVICE_ID_WINBOND_83C553 ||
|
||||
bridge->class >> 8 != PCI_CLASS_BRIDGE_ISA)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* We need to find function 0's revision, not function 1
|
||||
*/
|
||||
pci_read_config_byte(bridge, PCI_REVISION_ID, &rev);
|
||||
|
||||
return rev;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable the PCI device
|
||||
*
|
||||
* --BenH: It's arch fixup code that should enable channels that
|
||||
* have not been enabled by firmware. I decided we can still enable
|
||||
* channel 0 here at least, but channel 1 has to be enabled by
|
||||
* firmware or arch code. We still set both to 16 bits mode.
|
||||
*/
|
||||
static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev, const char *msg)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
DBG(("init_chipset_sl82c105()\n"));
|
||||
|
||||
pci_read_config_dword(dev, 0x40, &val);
|
||||
val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1F16;
|
||||
pci_write_config_dword(dev, 0x40, val);
|
||||
pci_set_drvdata(dev, (void *)val);
|
||||
|
||||
return dev->irq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise the chip
|
||||
*/
|
||||
static void __devinit init_hwif_sl82c105(ide_hwif_t *hwif)
|
||||
{
|
||||
unsigned int rev;
|
||||
|
||||
DBG(("init_hwif_sl82c105(hwif: ide%d)\n", hwif->index));
|
||||
|
||||
hwif->tuneproc = tune_sl82c105;
|
||||
hwif->selectproc = sl82c105_selectproc;
|
||||
hwif->resetproc = sl82c105_resetproc;
|
||||
|
||||
/*
|
||||
* Default to PIO 0 for fallback unless tuned otherwise.
|
||||
* We always autotune PIO, this is done before DMA is checked,
|
||||
* so there's no risk of accidentally disabling DMA
|
||||
*/
|
||||
hwif->drives[0].pio_speed = XFER_PIO_0;
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].pio_speed = XFER_PIO_0;
|
||||
hwif->drives[1].autotune = 1;
|
||||
|
||||
hwif->atapi_dma = 0;
|
||||
hwif->mwdma_mask = 0;
|
||||
hwif->swdma_mask = 0;
|
||||
hwif->autodma = 0;
|
||||
|
||||
if (!hwif->dma_base)
|
||||
return;
|
||||
|
||||
rev = sl82c105_bridge_revision(hwif->pci_dev);
|
||||
if (rev <= 5) {
|
||||
/*
|
||||
* Never ever EVER under any circumstances enable
|
||||
* DMA when the bridge is this old.
|
||||
*/
|
||||
printk(" %s: Winbond 553 bridge revision %d, BM-DMA disabled\n",
|
||||
hwif->name, rev);
|
||||
} else {
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->mwdma_mask = 0x04;
|
||||
|
||||
hwif->ide_dma_check = &sl82c105_check_drive;
|
||||
hwif->ide_dma_on = &sl82c105_ide_dma_on;
|
||||
hwif->dma_off_quietly = &sl82c105_dma_off_quietly;
|
||||
hwif->ide_dma_lostirq = &sl82c105_ide_dma_lost_irq;
|
||||
hwif->dma_start = &sl82c105_ide_dma_start;
|
||||
hwif->ide_dma_timeout = &sl82c105_ide_dma_timeout;
|
||||
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
|
||||
if (hwif->mate)
|
||||
hwif->serialized = hwif->mate->serialized = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static ide_pci_device_t sl82c105_chipset __devinitdata = {
|
||||
.name = "W82C105",
|
||||
.init_chipset = init_chipset_sl82c105,
|
||||
.init_hwif = init_hwif_sl82c105,
|
||||
.channels = 2,
|
||||
.autodma = NOAUTODMA,
|
||||
.enablebits = {{0x40,0x01,0x01}, {0x40,0x10,0x10}},
|
||||
.bootable = ON_BOARD,
|
||||
};
|
||||
|
||||
static int __devinit sl82c105_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &sl82c105_chipset);
|
||||
}
|
||||
|
||||
static struct pci_device_id sl82c105_pci_tbl[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105), 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, sl82c105_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "W82C105_IDE",
|
||||
.id_table = sl82c105_pci_tbl,
|
||||
.probe = sl82c105_init_one,
|
||||
};
|
||||
|
||||
static int __init sl82c105_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(sl82c105_ide_init);
|
||||
|
||||
MODULE_DESCRIPTION("PCI driver module for W82C105 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
270
drivers/ide/pci/slc90e66.c
Normal file
270
drivers/ide/pci/slc90e66.c
Normal file
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/slc90e66.c Version 0.14 February 8, 2007
|
||||
*
|
||||
* Copyright (C) 2000-2002 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2006-2007 MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* This is a look-alike variation of the ICH0 PIIX4 Ultra-66,
|
||||
* but this keeps the ISA-Bridge and slots alive.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
static u8 slc90e66_ratemask (ide_drive_t *drive)
|
||||
{
|
||||
u8 mode = 2;
|
||||
|
||||
if (!eighty_ninty_three(drive))
|
||||
mode = min_t(u8, mode, 1);
|
||||
return mode;
|
||||
}
|
||||
|
||||
static u8 slc90e66_dma_2_pio (u8 xfer_rate) {
|
||||
switch(xfer_rate) {
|
||||
case XFER_UDMA_4:
|
||||
case XFER_UDMA_3:
|
||||
case XFER_UDMA_2:
|
||||
case XFER_UDMA_1:
|
||||
case XFER_UDMA_0:
|
||||
case XFER_MW_DMA_2:
|
||||
case XFER_PIO_4:
|
||||
return 4;
|
||||
case XFER_MW_DMA_1:
|
||||
case XFER_PIO_3:
|
||||
return 3;
|
||||
case XFER_SW_DMA_2:
|
||||
case XFER_PIO_2:
|
||||
return 2;
|
||||
case XFER_MW_DMA_0:
|
||||
case XFER_SW_DMA_1:
|
||||
case XFER_SW_DMA_0:
|
||||
case XFER_PIO_1:
|
||||
case XFER_PIO_0:
|
||||
case XFER_PIO_SLOW:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void slc90e66_tune_pio (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
int is_slave = drive->dn & 1;
|
||||
int master_port = hwif->channel ? 0x42 : 0x40;
|
||||
int slave_port = 0x44;
|
||||
unsigned long flags;
|
||||
u16 master_data;
|
||||
u8 slave_data;
|
||||
int control = 0;
|
||||
/* ISP RTC */
|
||||
static const u8 timings[][2]= {
|
||||
{ 0, 0 },
|
||||
{ 0, 0 },
|
||||
{ 1, 0 },
|
||||
{ 2, 1 },
|
||||
{ 2, 3 }, };
|
||||
|
||||
spin_lock_irqsave(&ide_lock, flags);
|
||||
pci_read_config_word(dev, master_port, &master_data);
|
||||
|
||||
if (pio > 1)
|
||||
control |= 1; /* Programmable timing on */
|
||||
if (drive->media == ide_disk)
|
||||
control |= 4; /* Prefetch, post write */
|
||||
if (pio > 2)
|
||||
control |= 2; /* IORDY */
|
||||
if (is_slave) {
|
||||
master_data |= 0x4000;
|
||||
master_data &= ~0x0070;
|
||||
if (pio > 1) {
|
||||
/* Set PPE, IE and TIME */
|
||||
master_data |= control << 4;
|
||||
}
|
||||
pci_read_config_byte(dev, slave_port, &slave_data);
|
||||
slave_data &= hwif->channel ? 0x0f : 0xf0;
|
||||
slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) <<
|
||||
(hwif->channel ? 4 : 0);
|
||||
} else {
|
||||
master_data &= ~0x3307;
|
||||
if (pio > 1) {
|
||||
/* enable PPE, IE and TIME */
|
||||
master_data |= control;
|
||||
}
|
||||
master_data |= (timings[pio][0] << 12) | (timings[pio][1] << 8);
|
||||
}
|
||||
pci_write_config_word(dev, master_port, master_data);
|
||||
if (is_slave)
|
||||
pci_write_config_byte(dev, slave_port, slave_data);
|
||||
spin_unlock_irqrestore(&ide_lock, flags);
|
||||
}
|
||||
|
||||
static void slc90e66_tune_drive (ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
|
||||
slc90e66_tune_pio(drive, pio);
|
||||
(void) ide_config_drive_speed(drive, XFER_PIO_0 + pio);
|
||||
}
|
||||
|
||||
static int slc90e66_tune_chipset (ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u8 maslave = hwif->channel ? 0x42 : 0x40;
|
||||
u8 speed = ide_rate_filter(slc90e66_ratemask(drive), xferspeed);
|
||||
int sitre = 0, a_speed = 7 << (drive->dn * 4);
|
||||
int u_speed = 0, u_flag = 1 << drive->dn;
|
||||
u16 reg4042, reg44, reg48, reg4a;
|
||||
|
||||
pci_read_config_word(dev, maslave, ®4042);
|
||||
sitre = (reg4042 & 0x4000) ? 1 : 0;
|
||||
pci_read_config_word(dev, 0x44, ®44);
|
||||
pci_read_config_word(dev, 0x48, ®48);
|
||||
pci_read_config_word(dev, 0x4a, ®4a);
|
||||
|
||||
switch(speed) {
|
||||
case XFER_UDMA_4: u_speed = 4 << (drive->dn * 4); break;
|
||||
case XFER_UDMA_3: u_speed = 3 << (drive->dn * 4); break;
|
||||
case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break;
|
||||
case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break;
|
||||
case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break;
|
||||
case XFER_MW_DMA_2:
|
||||
case XFER_MW_DMA_1:
|
||||
case XFER_SW_DMA_2: break;
|
||||
case XFER_PIO_4:
|
||||
case XFER_PIO_3:
|
||||
case XFER_PIO_2:
|
||||
case XFER_PIO_0: break;
|
||||
default: return -1;
|
||||
}
|
||||
|
||||
if (speed >= XFER_UDMA_0) {
|
||||
if (!(reg48 & u_flag))
|
||||
pci_write_config_word(dev, 0x48, reg48|u_flag);
|
||||
/* FIXME: (reg4a & a_speed) ? */
|
||||
if ((reg4a & u_speed) != u_speed) {
|
||||
pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
|
||||
pci_read_config_word(dev, 0x4a, ®4a);
|
||||
pci_write_config_word(dev, 0x4a, reg4a|u_speed);
|
||||
}
|
||||
} else {
|
||||
if (reg48 & u_flag)
|
||||
pci_write_config_word(dev, 0x48, reg48 & ~u_flag);
|
||||
if (reg4a & a_speed)
|
||||
pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
|
||||
}
|
||||
|
||||
slc90e66_tune_pio(drive, slc90e66_dma_2_pio(speed));
|
||||
return ide_config_drive_speed(drive, speed);
|
||||
}
|
||||
|
||||
static int slc90e66_config_drive_for_dma (ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, slc90e66_ratemask(drive));
|
||||
|
||||
if (!speed)
|
||||
return 0;
|
||||
|
||||
(void) slc90e66_tune_chipset(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
static int slc90e66_config_drive_xfer_rate (ide_drive_t *drive)
|
||||
{
|
||||
drive->init_speed = 0;
|
||||
|
||||
if (ide_use_dma(drive) && slc90e66_config_drive_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
if (ide_use_fast_pio(drive))
|
||||
slc90e66_tune_drive(drive, 255);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_slc90e66 (ide_hwif_t *hwif)
|
||||
{
|
||||
u8 reg47 = 0;
|
||||
u8 mask = hwif->channel ? 0x01 : 0x02; /* bit0:Primary */
|
||||
|
||||
hwif->autodma = 0;
|
||||
|
||||
if (!hwif->irq)
|
||||
hwif->irq = hwif->channel ? 15 : 14;
|
||||
|
||||
hwif->speedproc = &slc90e66_tune_chipset;
|
||||
hwif->tuneproc = &slc90e66_tune_drive;
|
||||
|
||||
pci_read_config_byte(hwif->pci_dev, 0x47, ®47);
|
||||
|
||||
if (!hwif->dma_base) {
|
||||
hwif->drives[0].autotune = 1;
|
||||
hwif->drives[1].autotune = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x1f;
|
||||
hwif->mwdma_mask = 0x06;
|
||||
hwif->swdma_mask = 0x04;
|
||||
|
||||
if (!hwif->udma_four) {
|
||||
/* bit[0(1)]: 0:80, 1:40 */
|
||||
hwif->udma_four = (reg47 & mask) ? 0 : 1;
|
||||
}
|
||||
|
||||
hwif->ide_dma_check = &slc90e66_config_drive_xfer_rate;
|
||||
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
static ide_pci_device_t slc90e66_chipset __devinitdata = {
|
||||
.name = "SLC90E66",
|
||||
.init_hwif = init_hwif_slc90e66,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}},
|
||||
.bootable = ON_BOARD,
|
||||
};
|
||||
|
||||
static int __devinit slc90e66_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &slc90e66_chipset);
|
||||
}
|
||||
|
||||
static struct pci_device_id slc90e66_pci_tbl[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_1), 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, slc90e66_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "SLC90e66_IDE",
|
||||
.id_table = slc90e66_pci_tbl,
|
||||
.probe = slc90e66_init_one,
|
||||
};
|
||||
|
||||
static int __init slc90e66_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(slc90e66_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for SLC90E66 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
299
drivers/ide/pci/tc86c001.c
Normal file
299
drivers/ide/pci/tc86c001.c
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* drivers/ide/pci/tc86c001.c Version 1.00 Dec 12, 2006
|
||||
*
|
||||
* Copyright (C) 2002 Toshiba Corporation
|
||||
* Copyright (C) 2005-2006 MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
static inline u8 tc86c001_ratemask(ide_drive_t *drive)
|
||||
{
|
||||
return eighty_ninty_three(drive) ? 2 : 1;
|
||||
}
|
||||
|
||||
static int tc86c001_tune_chipset(ide_drive_t *drive, u8 speed)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned long scr_port = hwif->config_data + (drive->dn ? 0x02 : 0x00);
|
||||
u16 mode, scr = hwif->INW(scr_port);
|
||||
|
||||
speed = ide_rate_filter(tc86c001_ratemask(drive), speed);
|
||||
|
||||
switch (speed) {
|
||||
case XFER_UDMA_4: mode = 0x00c0; break;
|
||||
case XFER_UDMA_3: mode = 0x00b0; break;
|
||||
case XFER_UDMA_2: mode = 0x00a0; break;
|
||||
case XFER_UDMA_1: mode = 0x0090; break;
|
||||
case XFER_UDMA_0: mode = 0x0080; break;
|
||||
case XFER_MW_DMA_2: mode = 0x0070; break;
|
||||
case XFER_MW_DMA_1: mode = 0x0060; break;
|
||||
case XFER_MW_DMA_0: mode = 0x0050; break;
|
||||
case XFER_PIO_4: mode = 0x0400; break;
|
||||
case XFER_PIO_3: mode = 0x0300; break;
|
||||
case XFER_PIO_2: mode = 0x0200; break;
|
||||
case XFER_PIO_1: mode = 0x0100; break;
|
||||
case XFER_PIO_0:
|
||||
default: mode = 0x0000; break;
|
||||
}
|
||||
|
||||
scr &= (speed < XFER_MW_DMA_0) ? 0xf8ff : 0xff0f;
|
||||
scr |= mode;
|
||||
outw(scr, scr_port);
|
||||
|
||||
return ide_config_drive_speed(drive, speed);
|
||||
}
|
||||
|
||||
static void tc86c001_tune_drive(ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
|
||||
(void) tc86c001_tune_chipset(drive, XFER_PIO_0 + pio);
|
||||
}
|
||||
|
||||
/*
|
||||
* HACKITY HACK
|
||||
*
|
||||
* This is a workaround for the limitation 5 of the TC86C001 IDE controller:
|
||||
* if a DMA transfer terminates prematurely, the controller leaves the device's
|
||||
* interrupt request (INTRQ) pending and does not generate a PCI interrupt (or
|
||||
* set the interrupt bit in the DMA status register), thus no PCI interrupt
|
||||
* will occur until a DMA transfer has been successfully completed.
|
||||
*
|
||||
* We work around this by initiating dummy, zero-length DMA transfer on
|
||||
* a DMA timeout expiration. I found no better way to do this with the current
|
||||
* IDE core than to temporarily replace a higher level driver's timer expiry
|
||||
* handler with our own backing up to that handler in case our recovery fails.
|
||||
*/
|
||||
static int tc86c001_timer_expiry(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
ide_expiry_t *expiry = ide_get_hwifdata(hwif);
|
||||
ide_hwgroup_t *hwgroup = HWGROUP(drive);
|
||||
u8 dma_stat = hwif->INB(hwif->dma_status);
|
||||
|
||||
/* Restore a higher level driver's expiry handler first. */
|
||||
hwgroup->expiry = expiry;
|
||||
|
||||
if ((dma_stat & 5) == 1) { /* DMA active and no interrupt */
|
||||
unsigned long sc_base = hwif->config_data;
|
||||
unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04);
|
||||
u8 dma_cmd = hwif->INB(hwif->dma_command);
|
||||
|
||||
printk(KERN_WARNING "%s: DMA interrupt possibly stuck, "
|
||||
"attempting recovery...\n", drive->name);
|
||||
|
||||
/* Stop DMA */
|
||||
outb(dma_cmd & ~0x01, hwif->dma_command);
|
||||
|
||||
/* Setup the dummy DMA transfer */
|
||||
outw(0, sc_base + 0x0a); /* Sector Count */
|
||||
outw(0, twcr_port); /* Transfer Word Count 1 or 2 */
|
||||
|
||||
/* Start the dummy DMA transfer */
|
||||
outb(0x00, hwif->dma_command); /* clear R_OR_WCTR for write */
|
||||
outb(0x01, hwif->dma_command); /* set START_STOPBM */
|
||||
|
||||
/*
|
||||
* If an interrupt was pending, it should come thru shortly.
|
||||
* If not, a higher level driver's expiry handler should
|
||||
* eventually cause some kind of recovery from the DMA stall.
|
||||
*/
|
||||
return WAIT_MIN_SLEEP;
|
||||
}
|
||||
|
||||
/* Chain to the restored expiry handler if DMA wasn't active. */
|
||||
if (likely(expiry != NULL))
|
||||
return expiry(drive);
|
||||
|
||||
/* If there was no handler, "emulate" that for ide_timer_expiry()... */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void tc86c001_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
ide_hwgroup_t *hwgroup = HWGROUP(drive);
|
||||
unsigned long sc_base = hwif->config_data;
|
||||
unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04);
|
||||
unsigned long nsectors = hwgroup->rq->nr_sectors;
|
||||
|
||||
/*
|
||||
* We have to manually load the sector count and size into
|
||||
* the appropriate system control registers for DMA to work
|
||||
* with LBA48 and ATAPI devices...
|
||||
*/
|
||||
outw(nsectors, sc_base + 0x0a); /* Sector Count */
|
||||
outw(SECTOR_SIZE / 2, twcr_port); /* Transfer Word Count 1/2 */
|
||||
|
||||
/* Install our timeout expiry hook, saving the current handler... */
|
||||
ide_set_hwifdata(hwif, hwgroup->expiry);
|
||||
hwgroup->expiry = &tc86c001_timer_expiry;
|
||||
|
||||
ide_dma_start(drive);
|
||||
}
|
||||
|
||||
static int tc86c001_busproc(ide_drive_t *drive, int state)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
unsigned long sc_base = hwif->config_data;
|
||||
u16 scr1;
|
||||
|
||||
/* System Control 1 Register bit 11 (ATA Hard Reset) read */
|
||||
scr1 = hwif->INW(sc_base + 0x00);
|
||||
|
||||
switch (state) {
|
||||
case BUSSTATE_ON:
|
||||
if (!(scr1 & 0x0800))
|
||||
return 0;
|
||||
scr1 &= ~0x0800;
|
||||
|
||||
hwif->drives[0].failures = hwif->drives[1].failures = 0;
|
||||
break;
|
||||
case BUSSTATE_OFF:
|
||||
if (scr1 & 0x0800)
|
||||
return 0;
|
||||
scr1 |= 0x0800;
|
||||
|
||||
hwif->drives[0].failures = hwif->drives[0].max_failures + 1;
|
||||
hwif->drives[1].failures = hwif->drives[1].max_failures + 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* System Control 1 Register bit 11 (ATA Hard Reset) write */
|
||||
outw(scr1, sc_base + 0x00);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_chipset_for_dma(ide_drive_t *drive)
|
||||
{
|
||||
u8 speed = ide_dma_speed(drive, tc86c001_ratemask(drive));
|
||||
|
||||
if (!speed)
|
||||
return 0;
|
||||
|
||||
(void) tc86c001_tune_chipset(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
static int tc86c001_config_drive_xfer_rate(ide_drive_t *drive)
|
||||
{
|
||||
if (ide_use_dma(drive) && config_chipset_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
if (ide_use_fast_pio(drive))
|
||||
tc86c001_tune_drive(drive, 255);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_tc86c001(ide_hwif_t *hwif)
|
||||
{
|
||||
unsigned long sc_base = pci_resource_start(hwif->pci_dev, 5);
|
||||
u16 scr1 = hwif->INW(sc_base + 0x00);;
|
||||
|
||||
/* System Control 1 Register bit 15 (Soft Reset) set */
|
||||
outw(scr1 | 0x8000, sc_base + 0x00);
|
||||
|
||||
/* System Control 1 Register bit 14 (FIFO Reset) set */
|
||||
outw(scr1 | 0x4000, sc_base + 0x00);
|
||||
|
||||
/* System Control 1 Register: reset clear */
|
||||
outw(scr1 & ~0xc000, sc_base + 0x00);
|
||||
|
||||
/* Store the system control register base for convenience... */
|
||||
hwif->config_data = sc_base;
|
||||
|
||||
hwif->tuneproc = &tc86c001_tune_drive;
|
||||
hwif->speedproc = &tc86c001_tune_chipset;
|
||||
hwif->busproc = &tc86c001_busproc;
|
||||
|
||||
hwif->drives[0].autotune = hwif->drives[1].autotune = 1;
|
||||
|
||||
if (!hwif->dma_base)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Sector Count Control Register bits 0 and 1 set:
|
||||
* software sets Sector Count Register for master and slave device
|
||||
*/
|
||||
outw(0x0003, sc_base + 0x0c);
|
||||
|
||||
/* Sector Count Register limit */
|
||||
hwif->rqsize = 0xffff;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x1f;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
|
||||
hwif->ide_dma_check = &tc86c001_config_drive_xfer_rate;
|
||||
hwif->dma_start = &tc86c001_dma_start;
|
||||
|
||||
if (!hwif->udma_four) {
|
||||
/*
|
||||
* System Control 1 Register bit 13 (PDIAGN):
|
||||
* 0=80-pin cable, 1=40-pin cable
|
||||
*/
|
||||
scr1 = hwif->INW(sc_base + 0x00);
|
||||
hwif->udma_four = (scr1 & 0x2000) ? 0 : 1;
|
||||
}
|
||||
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
static unsigned int __devinit init_chipset_tc86c001(struct pci_dev *dev,
|
||||
const char *name)
|
||||
{
|
||||
int err = pci_request_region(dev, 5, name);
|
||||
|
||||
if (err)
|
||||
printk(KERN_ERR "%s: system control regs already in use", name);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ide_pci_device_t tc86c001_chipset __devinitdata = {
|
||||
.name = "TC86C001",
|
||||
.init_chipset = init_chipset_tc86c001,
|
||||
.init_hwif = init_hwif_tc86c001,
|
||||
.channels = 1,
|
||||
.autodma = AUTODMA,
|
||||
.bootable = OFF_BOARD
|
||||
};
|
||||
|
||||
static int __devinit tc86c001_init_one(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &tc86c001_chipset);
|
||||
}
|
||||
|
||||
static struct pci_device_id tc86c001_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, tc86c001_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "TC86C001",
|
||||
.id_table = tc86c001_pci_tbl,
|
||||
.probe = tc86c001_init_one
|
||||
};
|
||||
|
||||
static int __init tc86c001_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
module_init(tc86c001_ide_init);
|
||||
|
||||
MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
|
||||
MODULE_DESCRIPTION("PCI driver module for TC86C001 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
179
drivers/ide/pci/triflex.c
Normal file
179
drivers/ide/pci/triflex.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* triflex.c
|
||||
*
|
||||
* IDE Chipset driver for the Compaq TriFlex IDE controller.
|
||||
*
|
||||
* Known to work with the Compaq Workstation 5x00 series.
|
||||
*
|
||||
* Copyright (C) 2002 Hewlett-Packard Development Group, L.P.
|
||||
* Author: Torben Mathiasen <torben.mathiasen@hp.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Loosely based on the piix & svwks drivers.
|
||||
*
|
||||
* Documentation:
|
||||
* Not publically available.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.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/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
static int triflex_tune_chipset(ide_drive_t *drive, u8 xferspeed)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
u8 channel_offset = hwif->channel ? 0x74 : 0x70;
|
||||
u16 timing = 0;
|
||||
u32 triflex_timings = 0;
|
||||
u8 unit = (drive->select.b.unit & 0x01);
|
||||
u8 speed = ide_rate_filter(0, xferspeed);
|
||||
|
||||
pci_read_config_dword(dev, channel_offset, &triflex_timings);
|
||||
|
||||
switch(speed) {
|
||||
case XFER_MW_DMA_2:
|
||||
timing = 0x0103;
|
||||
break;
|
||||
case XFER_MW_DMA_1:
|
||||
timing = 0x0203;
|
||||
break;
|
||||
case XFER_MW_DMA_0:
|
||||
timing = 0x0808;
|
||||
break;
|
||||
case XFER_SW_DMA_2:
|
||||
case XFER_SW_DMA_1:
|
||||
case XFER_SW_DMA_0:
|
||||
timing = 0x0f0f;
|
||||
break;
|
||||
case XFER_PIO_4:
|
||||
timing = 0x0202;
|
||||
break;
|
||||
case XFER_PIO_3:
|
||||
timing = 0x0204;
|
||||
break;
|
||||
case XFER_PIO_2:
|
||||
timing = 0x0404;
|
||||
break;
|
||||
case XFER_PIO_1:
|
||||
timing = 0x0508;
|
||||
break;
|
||||
case XFER_PIO_0:
|
||||
timing = 0x0808;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
triflex_timings &= ~(0xFFFF << (16 * unit));
|
||||
triflex_timings |= (timing << (16 * unit));
|
||||
|
||||
pci_write_config_dword(dev, channel_offset, triflex_timings);
|
||||
|
||||
return (ide_config_drive_speed(drive, speed));
|
||||
}
|
||||
|
||||
static void triflex_tune_drive(ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
int use_pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
|
||||
(void) triflex_tune_chipset(drive, (XFER_PIO_0 + use_pio));
|
||||
}
|
||||
|
||||
static int triflex_config_drive_for_dma(ide_drive_t *drive)
|
||||
{
|
||||
int speed = ide_dma_speed(drive, 0); /* No ultra speeds */
|
||||
|
||||
if (!speed)
|
||||
return 0;
|
||||
|
||||
(void) triflex_tune_chipset(drive, speed);
|
||||
return ide_dma_enable(drive);
|
||||
}
|
||||
|
||||
static int triflex_config_drive_xfer_rate(ide_drive_t *drive)
|
||||
{
|
||||
if (ide_use_dma(drive) && triflex_config_drive_for_dma(drive))
|
||||
return 0;
|
||||
|
||||
triflex_tune_drive(drive, 255);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_triflex(ide_hwif_t *hwif)
|
||||
{
|
||||
hwif->tuneproc = &triflex_tune_drive;
|
||||
hwif->speedproc = &triflex_tune_chipset;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
hwif->swdma_mask = 0x07;
|
||||
hwif->ide_dma_check = &triflex_config_drive_xfer_rate;
|
||||
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
static ide_pci_device_t triflex_device __devinitdata = {
|
||||
.name = "TRIFLEX",
|
||||
.init_hwif = init_hwif_triflex,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.enablebits = {{0x80, 0x01, 0x01}, {0x80, 0x02, 0x02}},
|
||||
.bootable = ON_BOARD,
|
||||
};
|
||||
|
||||
static int __devinit triflex_init_one(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &triflex_device);
|
||||
}
|
||||
|
||||
static struct pci_device_id triflex_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, triflex_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "TRIFLEX_IDE",
|
||||
.id_table = triflex_pci_tbl,
|
||||
.probe = triflex_init_one,
|
||||
};
|
||||
|
||||
static int __init triflex_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(triflex_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Torben Mathiasen");
|
||||
MODULE_DESCRIPTION("PCI driver module for Compaq Triflex IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
361
drivers/ide/pci/trm290.c
Normal file
361
drivers/ide/pci/trm290.c
Normal file
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* linux/drivers/ide/pci/trm290.c Version 1.02 Mar. 18, 2000
|
||||
*
|
||||
* Copyright (c) 1997-1998 Mark Lord
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*
|
||||
* June 22, 2004 - get rid of check_region
|
||||
* - Jesper Juhl
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This module provides support for the bus-master IDE DMA function
|
||||
* of the Tekram TRM290 chip, used on a variety of PCI IDE add-on boards,
|
||||
* including a "Precision Instruments" board. The TRM290 pre-dates
|
||||
* the sff-8038 standard (ide-dma.c) by a few months, and differs
|
||||
* significantly enough to warrant separate routines for some functions,
|
||||
* while re-using others from ide-dma.c.
|
||||
*
|
||||
* EXPERIMENTAL! It works for me (a sample of one).
|
||||
*
|
||||
* Works reliably for me in DMA mode (READs only),
|
||||
* DMA WRITEs are disabled by default (see #define below);
|
||||
*
|
||||
* DMA is not enabled automatically for this chipset,
|
||||
* but can be turned on manually (with "hdparm -d1") at run time.
|
||||
*
|
||||
* I need volunteers with "spare" drives for further testing
|
||||
* and development, and maybe to help figure out the peculiarities.
|
||||
* Even knowing the registers (below), some things behave strangely.
|
||||
*/
|
||||
|
||||
#define TRM290_NO_DMA_WRITES /* DMA writes seem unreliable sometimes */
|
||||
|
||||
/*
|
||||
* TRM-290 PCI-IDE2 Bus Master Chip
|
||||
* ================================
|
||||
* The configuration registers are addressed in normal I/O port space
|
||||
* and are used as follows:
|
||||
*
|
||||
* trm290_base depends on jumper settings, and is probed for by ide-dma.c
|
||||
*
|
||||
* trm290_base+2 when WRITTEN: chiptest register (byte, write-only)
|
||||
* bit7 must always be written as "1"
|
||||
* bits6-2 undefined
|
||||
* bit1 1=legacy_compatible_mode, 0=native_pci_mode
|
||||
* bit0 1=test_mode, 0=normal(default)
|
||||
*
|
||||
* trm290_base+2 when READ: status register (byte, read-only)
|
||||
* bits7-2 undefined
|
||||
* bit1 channel0 busmaster interrupt status 0=none, 1=asserted
|
||||
* bit0 channel0 interrupt status 0=none, 1=asserted
|
||||
*
|
||||
* trm290_base+3 Interrupt mask register
|
||||
* bits7-5 undefined
|
||||
* bit4 legacy_header: 1=present, 0=absent
|
||||
* bit3 channel1 busmaster interrupt status 0=none, 1=asserted (read only)
|
||||
* bit2 channel1 interrupt status 0=none, 1=asserted (read only)
|
||||
* bit1 channel1 interrupt mask: 1=masked, 0=unmasked(default)
|
||||
* bit0 channel0 interrupt mask: 1=masked, 0=unmasked(default)
|
||||
*
|
||||
* trm290_base+1 "CPR" Config Pointer Register (byte)
|
||||
* bit7 1=autoincrement CPR bits 2-0 after each access of CDR
|
||||
* bit6 1=min. 1 wait-state posted write cycle (default), 0=0 wait-state
|
||||
* bit5 0=enabled master burst access (default), 1=disable (write only)
|
||||
* bit4 PCI DEVSEL# timing select: 1=medium(default), 0=fast
|
||||
* bit3 0=primary IDE channel, 1=secondary IDE channel
|
||||
* bits2-0 register index for accesses through CDR port
|
||||
*
|
||||
* trm290_base+0 "CDR" Config Data Register (word)
|
||||
* two sets of seven config registers,
|
||||
* selected by CPR bit 3 (channel) and CPR bits 2-0 (index 0 to 6),
|
||||
* each index defined below:
|
||||
*
|
||||
* Index-0 Base address register for command block (word)
|
||||
* defaults: 0x1f0 for primary, 0x170 for secondary
|
||||
*
|
||||
* Index-1 general config register (byte)
|
||||
* bit7 1=DMA enable, 0=DMA disable
|
||||
* bit6 1=activate IDE_RESET, 0=no action (default)
|
||||
* bit5 1=enable IORDY, 0=disable IORDY (default)
|
||||
* bit4 0=16-bit data port(default), 1=8-bit (XT) data port
|
||||
* bit3 interrupt polarity: 1=active_low, 0=active_high(default)
|
||||
* bit2 power-saving-mode(?): 1=enable, 0=disable(default) (write only)
|
||||
* bit1 bus_master_mode(?): 1=enable, 0=disable(default)
|
||||
* bit0 enable_io_ports: 1=enable(default), 0=disable
|
||||
*
|
||||
* Index-2 read-ahead counter preload bits 0-7 (byte, write only)
|
||||
* bits7-0 bits7-0 of readahead count
|
||||
*
|
||||
* Index-3 read-ahead config register (byte, write only)
|
||||
* bit7 1=enable_readahead, 0=disable_readahead(default)
|
||||
* bit6 1=clear_FIFO, 0=no_action
|
||||
* bit5 undefined
|
||||
* bit4 mode4 timing control: 1=enable, 0=disable(default)
|
||||
* bit3 undefined
|
||||
* bit2 undefined
|
||||
* bits1-0 bits9-8 of read-ahead count
|
||||
*
|
||||
* Index-4 base address register for control block (word)
|
||||
* defaults: 0x3f6 for primary, 0x376 for secondary
|
||||
*
|
||||
* Index-5 data port timings (shared by both drives) (byte)
|
||||
* standard PCI "clk" (clock) counts, default value = 0xf5
|
||||
*
|
||||
* bits7-6 setup time: 00=1clk, 01=2clk, 10=3clk, 11=4clk
|
||||
* bits5-3 hold time: 000=1clk, 001=2clk, 010=3clk,
|
||||
* 011=4clk, 100=5clk, 101=6clk,
|
||||
* 110=8clk, 111=12clk
|
||||
* bits2-0 active time: 000=2clk, 001=3clk, 010=4clk,
|
||||
* 011=5clk, 100=6clk, 101=8clk,
|
||||
* 110=12clk, 111=16clk
|
||||
*
|
||||
* Index-6 command/control port timings (shared by both drives) (byte)
|
||||
* same layout as Index-5, default value = 0xde
|
||||
*
|
||||
* Suggested CDR programming for PIO mode0 (600ns):
|
||||
* 0x01f0,0x21,0xff,0x80,0x03f6,0xf5,0xde ; primary
|
||||
* 0x0170,0x21,0xff,0x80,0x0376,0xf5,0xde ; secondary
|
||||
*
|
||||
* Suggested CDR programming for PIO mode3 (180ns):
|
||||
* 0x01f0,0x21,0xff,0x80,0x03f6,0x09,0xde ; primary
|
||||
* 0x0170,0x21,0xff,0x80,0x0376,0x09,0xde ; secondary
|
||||
*
|
||||
* Suggested CDR programming for PIO mode4 (120ns):
|
||||
* 0x01f0,0x21,0xff,0x80,0x03f6,0x00,0xde ; primary
|
||||
* 0x0170,0x21,0xff,0x80,0x0376,0x00,0xde ; secondary
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u16 reg = 0;
|
||||
unsigned long flags;
|
||||
|
||||
/* select PIO or DMA */
|
||||
reg = use_dma ? (0x21 | 0x82) : (0x21 & ~0x82);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
if (reg != hwif->select_data) {
|
||||
hwif->select_data = reg;
|
||||
/* set PIO/DMA */
|
||||
outb(0x51 | (hwif->channel << 3), hwif->config_data + 1);
|
||||
outw(reg & 0xff, hwif->config_data);
|
||||
}
|
||||
|
||||
/* enable IRQ if not probing */
|
||||
if (drive->present) {
|
||||
reg = inw(hwif->config_data + 3);
|
||||
reg &= 0x13;
|
||||
reg &= ~(1 << hwif->channel);
|
||||
outw(reg, hwif->config_data + 3);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void trm290_selectproc (ide_drive_t *drive)
|
||||
{
|
||||
trm290_prepare_drive(drive, drive->using_dma);
|
||||
}
|
||||
|
||||
static void trm290_ide_dma_exec_cmd(ide_drive_t *drive, u8 command)
|
||||
{
|
||||
BUG_ON(HWGROUP(drive)->handler != NULL); /* paranoia check */
|
||||
ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);
|
||||
/* issue cmd to drive */
|
||||
outb(command, IDE_COMMAND_REG);
|
||||
}
|
||||
|
||||
static int trm290_ide_dma_setup(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct request *rq = hwif->hwgroup->rq;
|
||||
unsigned int count, rw;
|
||||
|
||||
if (rq_data_dir(rq)) {
|
||||
#ifdef TRM290_NO_DMA_WRITES
|
||||
/* always use PIO for writes */
|
||||
trm290_prepare_drive(drive, 0); /* select PIO xfer */
|
||||
return 1;
|
||||
#endif
|
||||
rw = 1;
|
||||
} else
|
||||
rw = 2;
|
||||
|
||||
if (!(count = ide_build_dmatable(drive, rq))) {
|
||||
/* try PIO instead of DMA */
|
||||
trm290_prepare_drive(drive, 0); /* select PIO xfer */
|
||||
return 1;
|
||||
}
|
||||
/* select DMA xfer */
|
||||
trm290_prepare_drive(drive, 1);
|
||||
outl(hwif->dmatable_dma | rw, hwif->dma_command);
|
||||
drive->waiting_for_dma = 1;
|
||||
/* start DMA */
|
||||
outw((count * 2) - 1, hwif->dma_status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void trm290_ide_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
}
|
||||
|
||||
static int trm290_ide_dma_end (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u16 status = 0;
|
||||
|
||||
drive->waiting_for_dma = 0;
|
||||
/* purge DMA mappings */
|
||||
ide_destroy_dmatable(drive);
|
||||
status = inw(hwif->dma_status);
|
||||
return (status != 0x00ff);
|
||||
}
|
||||
|
||||
static int trm290_ide_dma_test_irq (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
u16 status = 0;
|
||||
|
||||
status = inw(hwif->dma_status);
|
||||
return (status == 0x00ff);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invoked from ide-dma.c at boot time.
|
||||
*/
|
||||
static void __devinit init_hwif_trm290(ide_hwif_t *hwif)
|
||||
{
|
||||
unsigned int cfgbase = 0;
|
||||
unsigned long flags;
|
||||
u8 reg = 0;
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
hwif->no_lba48 = 1;
|
||||
hwif->chipset = ide_trm290;
|
||||
cfgbase = pci_resource_start(dev, 4);
|
||||
if ((dev->class & 5) && cfgbase) {
|
||||
hwif->config_data = cfgbase;
|
||||
printk(KERN_INFO "TRM290: chip config base at 0x%04lx\n",
|
||||
hwif->config_data);
|
||||
} else {
|
||||
hwif->config_data = 0x3df0;
|
||||
printk(KERN_INFO "TRM290: using default config base at 0x%04lx\n",
|
||||
hwif->config_data);
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
/* put config reg into first byte of hwif->select_data */
|
||||
outb(0x51 | (hwif->channel << 3), hwif->config_data + 1);
|
||||
/* select PIO as default */
|
||||
hwif->select_data = 0x21;
|
||||
outb(hwif->select_data, hwif->config_data);
|
||||
/* get IRQ info */
|
||||
reg = inb(hwif->config_data + 3);
|
||||
/* mask IRQs for both ports */
|
||||
reg = (reg & 0x10) | 0x03;
|
||||
outb(reg, hwif->config_data + 3);
|
||||
local_irq_restore(flags);
|
||||
|
||||
if ((reg & 0x10))
|
||||
/* legacy mode */
|
||||
hwif->irq = hwif->channel ? 15 : 14;
|
||||
else if (!hwif->irq && hwif->mate && hwif->mate->irq)
|
||||
/* sharing IRQ with mate */
|
||||
hwif->irq = hwif->mate->irq;
|
||||
|
||||
ide_setup_dma(hwif, (hwif->config_data + 4) ^ (hwif->channel ? 0x0080 : 0x0000), 3);
|
||||
|
||||
hwif->dma_setup = &trm290_ide_dma_setup;
|
||||
hwif->dma_exec_cmd = &trm290_ide_dma_exec_cmd;
|
||||
hwif->dma_start = &trm290_ide_dma_start;
|
||||
hwif->ide_dma_end = &trm290_ide_dma_end;
|
||||
hwif->ide_dma_test_irq = &trm290_ide_dma_test_irq;
|
||||
|
||||
hwif->selectproc = &trm290_selectproc;
|
||||
hwif->autodma = 0; /* play it safe for now */
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
#if 1
|
||||
{
|
||||
/*
|
||||
* My trm290-based card doesn't seem to work with all possible values
|
||||
* for the control basereg, so this kludge ensures that we use only
|
||||
* values that are known to work. Ugh. -ml
|
||||
*/
|
||||
u16 new, old, compat = hwif->channel ? 0x374 : 0x3f4;
|
||||
static u16 next_offset = 0;
|
||||
u8 old_mask;
|
||||
|
||||
outb(0x54 | (hwif->channel << 3), hwif->config_data + 1);
|
||||
old = inw(hwif->config_data);
|
||||
old &= ~1;
|
||||
old_mask = inb(old + 2);
|
||||
if (old != compat && old_mask == 0xff) {
|
||||
/* leave lower 10 bits untouched */
|
||||
compat += (next_offset += 0x400);
|
||||
hwif->io_ports[IDE_CONTROL_OFFSET] = compat + 2;
|
||||
outw(compat | 1, hwif->config_data);
|
||||
new = inw(hwif->config_data);
|
||||
printk(KERN_INFO "%s: control basereg workaround: "
|
||||
"old=0x%04x, new=0x%04x\n",
|
||||
hwif->name, old, new & ~1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static ide_pci_device_t trm290_chipset __devinitdata = {
|
||||
.name = "TRM290",
|
||||
.init_hwif = init_hwif_trm290,
|
||||
.channels = 2,
|
||||
.autodma = NOAUTODMA,
|
||||
.bootable = ON_BOARD,
|
||||
};
|
||||
|
||||
static int __devinit trm290_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_setup_pci_device(dev, &trm290_chipset);
|
||||
}
|
||||
|
||||
static struct pci_device_id trm290_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_TEKRAM, PCI_DEVICE_ID_TEKRAM_DC290, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, trm290_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "TRM290_IDE",
|
||||
.id_table = trm290_pci_tbl,
|
||||
.probe = trm290_init_one,
|
||||
};
|
||||
|
||||
static int __init trm290_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(trm290_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Mark Lord");
|
||||
MODULE_DESCRIPTION("PCI driver module for Tekram TRM290 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
530
drivers/ide/pci/via82cxxx.c
Normal file
530
drivers/ide/pci/via82cxxx.c
Normal file
@@ -0,0 +1,530 @@
|
||||
/*
|
||||
*
|
||||
* Version 3.38
|
||||
*
|
||||
* VIA IDE driver for Linux. Supported southbridges:
|
||||
*
|
||||
* vt82c576, vt82c586, vt82c586a, vt82c586b, vt82c596a, vt82c596b,
|
||||
* vt82c686, vt82c686a, vt82c686b, vt8231, vt8233, vt8233c, vt8233a,
|
||||
* vt8235, vt8237, vt8237a
|
||||
*
|
||||
* Copyright (c) 2000-2002 Vojtech Pavlik
|
||||
*
|
||||
* Based on the work of:
|
||||
* Michel Aubry
|
||||
* Jeff Garzik
|
||||
* Andre Hedrick
|
||||
*
|
||||
* Documentation:
|
||||
* Obsolete device documentation publically available from via.com.tw
|
||||
* Current device documentation available under NDA only
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#ifdef CONFIG_PPC_CHRP
|
||||
#include <asm/processor.h>
|
||||
#endif
|
||||
|
||||
#include "ide-timing.h"
|
||||
|
||||
#define DISPLAY_VIA_TIMINGS
|
||||
|
||||
#define VIA_IDE_ENABLE 0x40
|
||||
#define VIA_IDE_CONFIG 0x41
|
||||
#define VIA_FIFO_CONFIG 0x43
|
||||
#define VIA_MISC_1 0x44
|
||||
#define VIA_MISC_2 0x45
|
||||
#define VIA_MISC_3 0x46
|
||||
#define VIA_DRIVE_TIMING 0x48
|
||||
#define VIA_8BIT_TIMING 0x4e
|
||||
#define VIA_ADDRESS_SETUP 0x4c
|
||||
#define VIA_UDMA_TIMING 0x50
|
||||
|
||||
#define VIA_UDMA 0x007
|
||||
#define VIA_UDMA_NONE 0x000
|
||||
#define VIA_UDMA_33 0x001
|
||||
#define VIA_UDMA_66 0x002
|
||||
#define VIA_UDMA_100 0x003
|
||||
#define VIA_UDMA_133 0x004
|
||||
#define VIA_BAD_PREQ 0x010 /* Crashes if PREQ# till DDACK# set */
|
||||
#define VIA_BAD_CLK66 0x020 /* 66 MHz clock doesn't work correctly */
|
||||
#define VIA_SET_FIFO 0x040 /* Needs to have FIFO split set */
|
||||
#define VIA_NO_UNMASK 0x080 /* Doesn't work with IRQ unmasking on */
|
||||
#define VIA_BAD_ID 0x100 /* Has wrong vendor ID (0x1107) */
|
||||
#define VIA_BAD_AST 0x200 /* Don't touch Address Setup Timing */
|
||||
|
||||
/*
|
||||
* VIA SouthBridge chips.
|
||||
*/
|
||||
|
||||
static struct via_isa_bridge {
|
||||
char *name;
|
||||
u16 id;
|
||||
u8 rev_min;
|
||||
u8 rev_max;
|
||||
u16 flags;
|
||||
} via_isa_bridges[] = {
|
||||
{ "cx700", PCI_DEVICE_ID_VIA_CX700, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST },
|
||||
{ "vt8237s", PCI_DEVICE_ID_VIA_8237S, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST },
|
||||
{ "vt6410", PCI_DEVICE_ID_VIA_6410, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST },
|
||||
{ "vt8251", PCI_DEVICE_ID_VIA_8251, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST },
|
||||
{ "vt8237", PCI_DEVICE_ID_VIA_8237, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST },
|
||||
{ "vt8237a", PCI_DEVICE_ID_VIA_8237A, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST },
|
||||
{ "vt8235", PCI_DEVICE_ID_VIA_8235, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST },
|
||||
{ "vt8233a", PCI_DEVICE_ID_VIA_8233A, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST },
|
||||
{ "vt8233c", PCI_DEVICE_ID_VIA_8233C_0, 0x00, 0x2f, VIA_UDMA_100 },
|
||||
{ "vt8233", PCI_DEVICE_ID_VIA_8233_0, 0x00, 0x2f, VIA_UDMA_100 },
|
||||
{ "vt8231", PCI_DEVICE_ID_VIA_8231, 0x00, 0x2f, VIA_UDMA_100 },
|
||||
{ "vt82c686b", PCI_DEVICE_ID_VIA_82C686, 0x40, 0x4f, VIA_UDMA_100 },
|
||||
{ "vt82c686a", PCI_DEVICE_ID_VIA_82C686, 0x10, 0x2f, VIA_UDMA_66 },
|
||||
{ "vt82c686", PCI_DEVICE_ID_VIA_82C686, 0x00, 0x0f, VIA_UDMA_33 | VIA_BAD_CLK66 },
|
||||
{ "vt82c596b", PCI_DEVICE_ID_VIA_82C596, 0x10, 0x2f, VIA_UDMA_66 },
|
||||
{ "vt82c596a", PCI_DEVICE_ID_VIA_82C596, 0x00, 0x0f, VIA_UDMA_33 | VIA_BAD_CLK66 },
|
||||
{ "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x47, 0x4f, VIA_UDMA_33 | VIA_SET_FIFO },
|
||||
{ "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x40, 0x46, VIA_UDMA_33 | VIA_SET_FIFO | VIA_BAD_PREQ },
|
||||
{ "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x30, 0x3f, VIA_UDMA_33 | VIA_SET_FIFO },
|
||||
{ "vt82c586a", PCI_DEVICE_ID_VIA_82C586_0, 0x20, 0x2f, VIA_UDMA_33 | VIA_SET_FIFO },
|
||||
{ "vt82c586", PCI_DEVICE_ID_VIA_82C586_0, 0x00, 0x0f, VIA_UDMA_NONE | VIA_SET_FIFO },
|
||||
{ "vt82c576", PCI_DEVICE_ID_VIA_82C576, 0x00, 0x2f, VIA_UDMA_NONE | VIA_SET_FIFO | VIA_NO_UNMASK },
|
||||
{ "vt82c576", PCI_DEVICE_ID_VIA_82C576, 0x00, 0x2f, VIA_UDMA_NONE | VIA_SET_FIFO | VIA_NO_UNMASK | VIA_BAD_ID },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static unsigned int via_clock;
|
||||
static char *via_dma[] = { "MWDMA16", "UDMA33", "UDMA66", "UDMA100", "UDMA133" };
|
||||
|
||||
struct via82cxxx_dev
|
||||
{
|
||||
struct via_isa_bridge *via_config;
|
||||
unsigned int via_80w;
|
||||
};
|
||||
|
||||
/**
|
||||
* via_set_speed - write timing registers
|
||||
* @dev: PCI device
|
||||
* @dn: device
|
||||
* @timing: IDE timing data to use
|
||||
*
|
||||
* via_set_speed writes timing values to the chipset registers
|
||||
*/
|
||||
|
||||
static void via_set_speed(ide_hwif_t *hwif, u8 dn, struct ide_timing *timing)
|
||||
{
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
struct via82cxxx_dev *vdev = pci_get_drvdata(hwif->pci_dev);
|
||||
u8 t;
|
||||
|
||||
if (~vdev->via_config->flags & VIA_BAD_AST) {
|
||||
pci_read_config_byte(dev, VIA_ADDRESS_SETUP, &t);
|
||||
t = (t & ~(3 << ((3 - dn) << 1))) | ((FIT(timing->setup, 1, 4) - 1) << ((3 - dn) << 1));
|
||||
pci_write_config_byte(dev, VIA_ADDRESS_SETUP, t);
|
||||
}
|
||||
|
||||
pci_write_config_byte(dev, VIA_8BIT_TIMING + (1 - (dn >> 1)),
|
||||
((FIT(timing->act8b, 1, 16) - 1) << 4) | (FIT(timing->rec8b, 1, 16) - 1));
|
||||
|
||||
pci_write_config_byte(dev, VIA_DRIVE_TIMING + (3 - dn),
|
||||
((FIT(timing->active, 1, 16) - 1) << 4) | (FIT(timing->recover, 1, 16) - 1));
|
||||
|
||||
switch (vdev->via_config->flags & VIA_UDMA) {
|
||||
case VIA_UDMA_33: t = timing->udma ? (0xe0 | (FIT(timing->udma, 2, 5) - 2)) : 0x03; break;
|
||||
case VIA_UDMA_66: t = timing->udma ? (0xe8 | (FIT(timing->udma, 2, 9) - 2)) : 0x0f; break;
|
||||
case VIA_UDMA_100: t = timing->udma ? (0xe0 | (FIT(timing->udma, 2, 9) - 2)) : 0x07; break;
|
||||
case VIA_UDMA_133: t = timing->udma ? (0xe0 | (FIT(timing->udma, 2, 9) - 2)) : 0x07; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
pci_write_config_byte(dev, VIA_UDMA_TIMING + (3 - dn), t);
|
||||
}
|
||||
|
||||
/**
|
||||
* via_set_drive - configure transfer mode
|
||||
* @drive: Drive to set up
|
||||
* @speed: desired speed
|
||||
*
|
||||
* via_set_drive() computes timing values configures the drive and
|
||||
* the chipset to a desired transfer mode. It also can be called
|
||||
* by upper layers.
|
||||
*/
|
||||
|
||||
static int via_set_drive(ide_drive_t *drive, u8 speed)
|
||||
{
|
||||
ide_drive_t *peer = HWIF(drive)->drives + (~drive->dn & 1);
|
||||
struct via82cxxx_dev *vdev = pci_get_drvdata(drive->hwif->pci_dev);
|
||||
struct ide_timing t, p;
|
||||
unsigned int T, UT;
|
||||
|
||||
if (speed != XFER_PIO_SLOW)
|
||||
ide_config_drive_speed(drive, speed);
|
||||
|
||||
T = 1000000000 / via_clock;
|
||||
|
||||
switch (vdev->via_config->flags & VIA_UDMA) {
|
||||
case VIA_UDMA_33: UT = T; break;
|
||||
case VIA_UDMA_66: UT = T/2; break;
|
||||
case VIA_UDMA_100: UT = T/3; break;
|
||||
case VIA_UDMA_133: UT = T/4; break;
|
||||
default: UT = T;
|
||||
}
|
||||
|
||||
ide_timing_compute(drive, speed, &t, T, UT);
|
||||
|
||||
if (peer->present) {
|
||||
ide_timing_compute(peer, peer->current_speed, &p, T, UT);
|
||||
ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT);
|
||||
}
|
||||
|
||||
via_set_speed(HWIF(drive), drive->dn, &t);
|
||||
|
||||
if (!drive->init_speed)
|
||||
drive->init_speed = speed;
|
||||
drive->current_speed = speed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* via82cxxx_tune_drive - PIO setup
|
||||
* @drive: drive to set up
|
||||
* @pio: mode to use (255 for 'best possible')
|
||||
*
|
||||
* A callback from the upper layers for PIO-only tuning.
|
||||
*/
|
||||
|
||||
static void via82cxxx_tune_drive(ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
if (pio == 255) {
|
||||
via_set_drive(drive,
|
||||
ide_find_best_mode(drive, XFER_PIO | XFER_EPIO));
|
||||
return;
|
||||
}
|
||||
|
||||
via_set_drive(drive, XFER_PIO_0 + min_t(u8, pio, 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* via82cxxx_ide_dma_check - set up for DMA if possible
|
||||
* @drive: IDE drive to set up
|
||||
*
|
||||
* Set up the drive for the highest supported speed considering the
|
||||
* driver, controller and cable
|
||||
*/
|
||||
|
||||
static int via82cxxx_ide_dma_check (ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = HWIF(drive);
|
||||
struct via82cxxx_dev *vdev = pci_get_drvdata(hwif->pci_dev);
|
||||
u16 w80 = hwif->udma_four;
|
||||
|
||||
u16 speed = ide_find_best_mode(drive,
|
||||
XFER_PIO | XFER_EPIO | XFER_SWDMA | XFER_MWDMA |
|
||||
(vdev->via_config->flags & VIA_UDMA ? XFER_UDMA : 0) |
|
||||
(w80 && (vdev->via_config->flags & VIA_UDMA) >= VIA_UDMA_66 ? XFER_UDMA_66 : 0) |
|
||||
(w80 && (vdev->via_config->flags & VIA_UDMA) >= VIA_UDMA_100 ? XFER_UDMA_100 : 0) |
|
||||
(w80 && (vdev->via_config->flags & VIA_UDMA) >= VIA_UDMA_133 ? XFER_UDMA_133 : 0));
|
||||
|
||||
via_set_drive(drive, speed);
|
||||
|
||||
if (drive->autodma && (speed & XFER_MODE) != XFER_PIO)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct via_isa_bridge *via_config_find(struct pci_dev **isa)
|
||||
{
|
||||
struct via_isa_bridge *via_config;
|
||||
u8 t;
|
||||
|
||||
for (via_config = via_isa_bridges; via_config->id; via_config++)
|
||||
if ((*isa = pci_get_device(PCI_VENDOR_ID_VIA +
|
||||
!!(via_config->flags & VIA_BAD_ID),
|
||||
via_config->id, NULL))) {
|
||||
|
||||
pci_read_config_byte(*isa, PCI_REVISION_ID, &t);
|
||||
if (t >= via_config->rev_min &&
|
||||
t <= via_config->rev_max)
|
||||
break;
|
||||
pci_dev_put(*isa);
|
||||
}
|
||||
|
||||
return via_config;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check and handle 80-wire cable presence
|
||||
*/
|
||||
static void __devinit via_cable_detect(struct via82cxxx_dev *vdev, u32 u)
|
||||
{
|
||||
int i;
|
||||
|
||||
switch (vdev->via_config->flags & VIA_UDMA) {
|
||||
case VIA_UDMA_66:
|
||||
for (i = 24; i >= 0; i -= 8)
|
||||
if (((u >> (i & 16)) & 8) &&
|
||||
((u >> i) & 0x20) &&
|
||||
(((u >> i) & 7) < 2)) {
|
||||
/*
|
||||
* 2x PCI clock and
|
||||
* UDMA w/ < 3T/cycle
|
||||
*/
|
||||
vdev->via_80w |= (1 << (1 - (i >> 4)));
|
||||
}
|
||||
break;
|
||||
|
||||
case VIA_UDMA_100:
|
||||
for (i = 24; i >= 0; i -= 8)
|
||||
if (((u >> i) & 0x10) ||
|
||||
(((u >> i) & 0x20) &&
|
||||
(((u >> i) & 7) < 4))) {
|
||||
/* BIOS 80-wire bit or
|
||||
* UDMA w/ < 60ns/cycle
|
||||
*/
|
||||
vdev->via_80w |= (1 << (1 - (i >> 4)));
|
||||
}
|
||||
break;
|
||||
|
||||
case VIA_UDMA_133:
|
||||
for (i = 24; i >= 0; i -= 8)
|
||||
if (((u >> i) & 0x10) ||
|
||||
(((u >> i) & 0x20) &&
|
||||
(((u >> i) & 7) < 6))) {
|
||||
/* BIOS 80-wire bit or
|
||||
* UDMA w/ < 60ns/cycle
|
||||
*/
|
||||
vdev->via_80w |= (1 << (1 - (i >> 4)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* init_chipset_via82cxxx - initialization handler
|
||||
* @dev: PCI device
|
||||
* @name: Name of interface
|
||||
*
|
||||
* The initialization callback. Here we determine the IDE chip type
|
||||
* and initialize its drive independent registers.
|
||||
*/
|
||||
|
||||
static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev, const char *name)
|
||||
{
|
||||
struct pci_dev *isa = NULL;
|
||||
struct via82cxxx_dev *vdev;
|
||||
struct via_isa_bridge *via_config;
|
||||
u8 t, v;
|
||||
u32 u;
|
||||
|
||||
vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
|
||||
if (!vdev) {
|
||||
printk(KERN_ERR "VP_IDE: out of memory :(\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
pci_set_drvdata(dev, vdev);
|
||||
|
||||
/*
|
||||
* Find the ISA bridge to see how good the IDE is.
|
||||
*/
|
||||
vdev->via_config = via_config = via_config_find(&isa);
|
||||
|
||||
/* We checked this earlier so if it fails here deeep badness
|
||||
is involved */
|
||||
|
||||
BUG_ON(!via_config->id);
|
||||
|
||||
/*
|
||||
* Detect cable and configure Clk66
|
||||
*/
|
||||
pci_read_config_dword(dev, VIA_UDMA_TIMING, &u);
|
||||
|
||||
via_cable_detect(vdev, u);
|
||||
|
||||
if ((via_config->flags & VIA_UDMA) == VIA_UDMA_66) {
|
||||
/* Enable Clk66 */
|
||||
pci_write_config_dword(dev, VIA_UDMA_TIMING, u|0x80008);
|
||||
} else if (via_config->flags & VIA_BAD_CLK66) {
|
||||
/* Would cause trouble on 596a and 686 */
|
||||
pci_write_config_dword(dev, VIA_UDMA_TIMING, u & ~0x80008);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether interfaces are enabled.
|
||||
*/
|
||||
|
||||
pci_read_config_byte(dev, VIA_IDE_ENABLE, &v);
|
||||
|
||||
/*
|
||||
* Set up FIFO sizes and thresholds.
|
||||
*/
|
||||
|
||||
pci_read_config_byte(dev, VIA_FIFO_CONFIG, &t);
|
||||
|
||||
/* Disable PREQ# till DDACK# */
|
||||
if (via_config->flags & VIA_BAD_PREQ) {
|
||||
/* Would crash on 586b rev 41 */
|
||||
t &= 0x7f;
|
||||
}
|
||||
|
||||
/* Fix FIFO split between channels */
|
||||
if (via_config->flags & VIA_SET_FIFO) {
|
||||
t &= (t & 0x9f);
|
||||
switch (v & 3) {
|
||||
case 2: t |= 0x00; break; /* 16 on primary */
|
||||
case 1: t |= 0x60; break; /* 16 on secondary */
|
||||
case 3: t |= 0x20; break; /* 8 pri 8 sec */
|
||||
}
|
||||
}
|
||||
|
||||
pci_write_config_byte(dev, VIA_FIFO_CONFIG, t);
|
||||
|
||||
/*
|
||||
* Determine system bus clock.
|
||||
*/
|
||||
|
||||
via_clock = system_bus_clock() * 1000;
|
||||
|
||||
switch (via_clock) {
|
||||
case 33000: via_clock = 33333; break;
|
||||
case 37000: via_clock = 37500; break;
|
||||
case 41000: via_clock = 41666; break;
|
||||
}
|
||||
|
||||
if (via_clock < 20000 || via_clock > 50000) {
|
||||
printk(KERN_WARNING "VP_IDE: User given PCI clock speed "
|
||||
"impossible (%d), using 33 MHz instead.\n", via_clock);
|
||||
printk(KERN_WARNING "VP_IDE: Use ide0=ata66 if you want "
|
||||
"to assume 80-wire cable.\n");
|
||||
via_clock = 33333;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the boot message.
|
||||
*/
|
||||
|
||||
pci_read_config_byte(isa, PCI_REVISION_ID, &t);
|
||||
printk(KERN_INFO "VP_IDE: VIA %s (rev %02x) IDE %s "
|
||||
"controller on pci%s\n",
|
||||
via_config->name, t,
|
||||
via_dma[via_config->flags & VIA_UDMA],
|
||||
pci_name(dev));
|
||||
|
||||
pci_dev_put(isa);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devinit init_hwif_via82cxxx(ide_hwif_t *hwif)
|
||||
{
|
||||
struct via82cxxx_dev *vdev = pci_get_drvdata(hwif->pci_dev);
|
||||
int i;
|
||||
|
||||
hwif->autodma = 0;
|
||||
|
||||
hwif->tuneproc = &via82cxxx_tune_drive;
|
||||
hwif->speedproc = &via_set_drive;
|
||||
|
||||
|
||||
#ifdef CONFIG_PPC_CHRP
|
||||
if(machine_is(chrp) && _chrp_type == _CHRP_Pegasos) {
|
||||
hwif->irq = hwif->channel ? 15 : 14;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
hwif->drives[i].io_32bit = 1;
|
||||
hwif->drives[i].unmask = (vdev->via_config->flags & VIA_NO_UNMASK) ? 0 : 1;
|
||||
hwif->drives[i].autotune = 1;
|
||||
hwif->drives[i].dn = hwif->channel * 2 + i;
|
||||
}
|
||||
|
||||
if (!hwif->dma_base)
|
||||
return;
|
||||
|
||||
hwif->atapi_dma = 1;
|
||||
hwif->ultra_mask = 0x7f;
|
||||
hwif->mwdma_mask = 0x07;
|
||||
hwif->swdma_mask = 0x07;
|
||||
|
||||
if (!hwif->udma_four)
|
||||
hwif->udma_four = (vdev->via_80w >> hwif->channel) & 1;
|
||||
hwif->ide_dma_check = &via82cxxx_ide_dma_check;
|
||||
if (!noautodma)
|
||||
hwif->autodma = 1;
|
||||
hwif->drives[0].autodma = hwif->autodma;
|
||||
hwif->drives[1].autodma = hwif->autodma;
|
||||
}
|
||||
|
||||
static ide_pci_device_t via82cxxx_chipsets[] __devinitdata = {
|
||||
{ /* 0 */
|
||||
.name = "VP_IDE",
|
||||
.init_chipset = init_chipset_via82cxxx,
|
||||
.init_hwif = init_hwif_via82cxxx,
|
||||
.channels = 2,
|
||||
.autodma = NOAUTODMA,
|
||||
.enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}},
|
||||
.bootable = ON_BOARD
|
||||
},{ /* 1 */
|
||||
.name = "VP_IDE",
|
||||
.init_chipset = init_chipset_via82cxxx,
|
||||
.init_hwif = init_hwif_via82cxxx,
|
||||
.channels = 2,
|
||||
.autodma = AUTODMA,
|
||||
.enablebits = {{0x00,0x00,0x00}, {0x00,0x00,0x00}},
|
||||
.bootable = ON_BOARD,
|
||||
}
|
||||
};
|
||||
|
||||
static int __devinit via_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct pci_dev *isa = NULL;
|
||||
struct via_isa_bridge *via_config;
|
||||
/*
|
||||
* Find the ISA bridge and check we know what it is.
|
||||
*/
|
||||
via_config = via_config_find(&isa);
|
||||
pci_dev_put(isa);
|
||||
if (!via_config->id) {
|
||||
printk(KERN_WARNING "VP_IDE: Unknown VIA SouthBridge, disabling DMA.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
return ide_setup_pci_device(dev, &via82cxxx_chipsets[id->driver_data]);
|
||||
}
|
||||
|
||||
static struct pci_device_id via_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C576_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_6410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
|
||||
{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_SATA_EIDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, via_pci_tbl);
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "VIA_IDE",
|
||||
.id_table = via_pci_tbl,
|
||||
.probe = via_init_one,
|
||||
};
|
||||
|
||||
static int __init via_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(via_ide_init);
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik, Michel Aubry, Jeff Garzik, Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for VIA IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
853
drivers/ide/ppc/mpc8xx.c
Normal file
853
drivers/ide/ppc/mpc8xx.c
Normal file
@@ -0,0 +1,853 @@
|
||||
/*
|
||||
* linux/drivers/ide/ppc/ide-m8xx.c
|
||||
*
|
||||
* Copyright (C) 2000, 2001 Wolfgang Denk, wd@denx.de
|
||||
* Modified for direct IDE interface
|
||||
* by Thomas Lange, thomas@corelatus.com
|
||||
* Modified for direct IDE interface on 8xx without using the PCMCIA
|
||||
* controller
|
||||
* by Steven.Scholz@imc-berlin.de
|
||||
* Moved out of arch/ppc/kernel/m8xx_setup.c, other minor cleanups
|
||||
* by Mathew Locke <mattl@mvista.com>
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/a.out.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/bootmem.h>
|
||||
|
||||
#include <asm/mpc8xx.h>
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/residual.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/ide.h>
|
||||
#include <asm/8xx_immap.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
static int identify (volatile u8 *p);
|
||||
static void print_fixed (volatile u8 *p);
|
||||
static void print_funcid (int func);
|
||||
static int check_ide_device (unsigned long base);
|
||||
|
||||
static void ide_interrupt_ack (void *dev);
|
||||
static void m8xx_ide_tuneproc(ide_drive_t *drive, u8 pio);
|
||||
|
||||
typedef struct ide_ioport_desc {
|
||||
unsigned long base_off; /* Offset to PCMCIA memory */
|
||||
unsigned long reg_off[IDE_NR_PORTS]; /* controller register offsets */
|
||||
int irq; /* IRQ */
|
||||
} ide_ioport_desc_t;
|
||||
|
||||
ide_ioport_desc_t ioport_dsc[MAX_HWIFS] = {
|
||||
#ifdef IDE0_BASE_OFFSET
|
||||
{ IDE0_BASE_OFFSET,
|
||||
{
|
||||
IDE0_DATA_REG_OFFSET,
|
||||
IDE0_ERROR_REG_OFFSET,
|
||||
IDE0_NSECTOR_REG_OFFSET,
|
||||
IDE0_SECTOR_REG_OFFSET,
|
||||
IDE0_LCYL_REG_OFFSET,
|
||||
IDE0_HCYL_REG_OFFSET,
|
||||
IDE0_SELECT_REG_OFFSET,
|
||||
IDE0_STATUS_REG_OFFSET,
|
||||
IDE0_CONTROL_REG_OFFSET,
|
||||
IDE0_IRQ_REG_OFFSET,
|
||||
},
|
||||
IDE0_INTERRUPT,
|
||||
},
|
||||
#ifdef IDE1_BASE_OFFSET
|
||||
{ IDE1_BASE_OFFSET,
|
||||
{
|
||||
IDE1_DATA_REG_OFFSET,
|
||||
IDE1_ERROR_REG_OFFSET,
|
||||
IDE1_NSECTOR_REG_OFFSET,
|
||||
IDE1_SECTOR_REG_OFFSET,
|
||||
IDE1_LCYL_REG_OFFSET,
|
||||
IDE1_HCYL_REG_OFFSET,
|
||||
IDE1_SELECT_REG_OFFSET,
|
||||
IDE1_STATUS_REG_OFFSET,
|
||||
IDE1_CONTROL_REG_OFFSET,
|
||||
IDE1_IRQ_REG_OFFSET,
|
||||
},
|
||||
IDE1_INTERRUPT,
|
||||
},
|
||||
#endif /* IDE1_BASE_OFFSET */
|
||||
#endif /* IDE0_BASE_OFFSET */
|
||||
};
|
||||
|
||||
ide_pio_timings_t ide_pio_clocks[6];
|
||||
int hold_time[6] = {30, 20, 15, 10, 10, 10 }; /* PIO Mode 5 with IORDY (nonstandard) */
|
||||
|
||||
/*
|
||||
* Warning: only 1 (ONE) PCMCIA slot supported here,
|
||||
* which must be correctly initialized by the firmware (PPCBoot).
|
||||
*/
|
||||
static int _slot_ = -1; /* will be read from PCMCIA registers */
|
||||
|
||||
/* Make clock cycles and always round up */
|
||||
#define PCMCIA_MK_CLKS( t, T ) (( (t) * ((T)/1000000) + 999U ) / 1000U )
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* IDE stuff.
|
||||
*/
|
||||
static int
|
||||
m8xx_ide_default_irq(unsigned long base)
|
||||
{
|
||||
#ifdef CONFIG_BLK_DEV_MPC8xx_IDE
|
||||
if (base >= MAX_HWIFS)
|
||||
return 0;
|
||||
|
||||
printk("[%d] m8xx_ide_default_irq %d\n",__LINE__,ioport_dsc[base].irq);
|
||||
|
||||
return (ioport_dsc[base].irq);
|
||||
#else
|
||||
return 9;
|
||||
#endif
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
m8xx_ide_default_io_base(int index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
#define M8XX_PCMCIA_CD2(slot) (0x10000000 >> (slot << 4))
|
||||
#define M8XX_PCMCIA_CD1(slot) (0x08000000 >> (slot << 4))
|
||||
|
||||
/*
|
||||
* The TQM850L hardware has two pins swapped! Grrrrgh!
|
||||
*/
|
||||
#ifdef CONFIG_TQM850L
|
||||
#define __MY_PCMCIA_GCRX_CXRESET PCMCIA_GCRX_CXOE
|
||||
#define __MY_PCMCIA_GCRX_CXOE PCMCIA_GCRX_CXRESET
|
||||
#else
|
||||
#define __MY_PCMCIA_GCRX_CXRESET PCMCIA_GCRX_CXRESET
|
||||
#define __MY_PCMCIA_GCRX_CXOE PCMCIA_GCRX_CXOE
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BLK_DEV_MPC8xx_IDE) && defined(CONFIG_IDE_8xx_PCCARD)
|
||||
#define PCMCIA_SCHLVL IDE0_INTERRUPT /* Status Change Interrupt Level */
|
||||
static int pcmcia_schlvl = PCMCIA_SCHLVL;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* See include/linux/ide.h for definition of hw_regs_t (p, base)
|
||||
*/
|
||||
|
||||
/*
|
||||
* m8xx_ide_init_hwif_ports for a direct IDE interface _using_
|
||||
*/
|
||||
#if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT)
|
||||
static void
|
||||
m8xx_ide_init_hwif_ports(hw_regs_t *hw, unsigned long data_port,
|
||||
unsigned long ctrl_port, int *irq)
|
||||
{
|
||||
unsigned long *p = hw->io_ports;
|
||||
int i;
|
||||
|
||||
typedef struct {
|
||||
ulong br;
|
||||
ulong or;
|
||||
} pcmcia_win_t;
|
||||
volatile pcmcia_win_t *win;
|
||||
volatile pcmconf8xx_t *pcmp;
|
||||
|
||||
uint *pgcrx;
|
||||
u32 pcmcia_phy_base;
|
||||
u32 pcmcia_phy_end;
|
||||
static unsigned long pcmcia_base = 0;
|
||||
unsigned long base;
|
||||
|
||||
*p = 0;
|
||||
if (irq)
|
||||
*irq = 0;
|
||||
|
||||
pcmp = (pcmconf8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_pcmcia));
|
||||
|
||||
if (!pcmcia_base) {
|
||||
/*
|
||||
* Read out PCMCIA registers. Since the reset values
|
||||
* are undefined, we sure hope that they have been
|
||||
* set up by firmware
|
||||
*/
|
||||
|
||||
/* Scan all registers for valid settings */
|
||||
pcmcia_phy_base = 0xFFFFFFFF;
|
||||
pcmcia_phy_end = 0;
|
||||
/* br0 is start of brX and orX regs */
|
||||
win = (pcmcia_win_t *) \
|
||||
(&(((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0));
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (win->or & 1) { /* This bank is marked as valid */
|
||||
if (win->br < pcmcia_phy_base) {
|
||||
pcmcia_phy_base = win->br;
|
||||
}
|
||||
if ((win->br + PCMCIA_MEM_SIZE) > pcmcia_phy_end) {
|
||||
pcmcia_phy_end = win->br + PCMCIA_MEM_SIZE;
|
||||
}
|
||||
/* Check which slot that has been defined */
|
||||
_slot_ = (win->or >> 2) & 1;
|
||||
|
||||
} /* Valid bank */
|
||||
win++;
|
||||
} /* for */
|
||||
|
||||
printk ("PCMCIA slot %c: phys mem %08x...%08x (size %08x)\n",
|
||||
'A' + _slot_,
|
||||
pcmcia_phy_base, pcmcia_phy_end,
|
||||
pcmcia_phy_end - pcmcia_phy_base);
|
||||
|
||||
pcmcia_base=(unsigned long)ioremap(pcmcia_phy_base,
|
||||
pcmcia_phy_end-pcmcia_phy_base);
|
||||
|
||||
#ifdef DEBUG
|
||||
printk ("PCMCIA virt base: %08lx\n", pcmcia_base);
|
||||
#endif
|
||||
/* Compute clock cycles for PIO timings */
|
||||
for (i=0; i<6; ++i) {
|
||||
bd_t *binfo = (bd_t *)__res;
|
||||
|
||||
hold_time[i] =
|
||||
PCMCIA_MK_CLKS (hold_time[i],
|
||||
binfo->bi_busfreq);
|
||||
ide_pio_clocks[i].setup_time =
|
||||
PCMCIA_MK_CLKS (ide_pio_timings[i].setup_time,
|
||||
binfo->bi_busfreq);
|
||||
ide_pio_clocks[i].active_time =
|
||||
PCMCIA_MK_CLKS (ide_pio_timings[i].active_time,
|
||||
binfo->bi_busfreq);
|
||||
ide_pio_clocks[i].cycle_time =
|
||||
PCMCIA_MK_CLKS (ide_pio_timings[i].cycle_time,
|
||||
binfo->bi_busfreq);
|
||||
#if 0
|
||||
printk ("PIO mode %d timings: %d/%d/%d => %d/%d/%d\n",
|
||||
i,
|
||||
ide_pio_clocks[i].setup_time,
|
||||
ide_pio_clocks[i].active_time,
|
||||
ide_pio_clocks[i].hold_time,
|
||||
ide_pio_clocks[i].cycle_time,
|
||||
ide_pio_timings[i].setup_time,
|
||||
ide_pio_timings[i].active_time,
|
||||
ide_pio_timings[i].hold_time,
|
||||
ide_pio_timings[i].cycle_time);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (data_port >= MAX_HWIFS)
|
||||
return;
|
||||
|
||||
if (_slot_ == -1) {
|
||||
printk ("PCMCIA slot has not been defined! Using A as default\n");
|
||||
_slot_ = 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IDE_8xx_PCCARD
|
||||
|
||||
#ifdef DEBUG
|
||||
printk ("PIPR = 0x%08X slot %c ==> mask = 0x%X\n",
|
||||
pcmp->pcmc_pipr,
|
||||
'A' + _slot_,
|
||||
M8XX_PCMCIA_CD1(_slot_) | M8XX_PCMCIA_CD2(_slot_) );
|
||||
#endif /* DEBUG */
|
||||
|
||||
if (pcmp->pcmc_pipr & (M8XX_PCMCIA_CD1(_slot_)|M8XX_PCMCIA_CD2(_slot_))) {
|
||||
printk ("No card in slot %c: PIPR=%08x\n",
|
||||
'A' + _slot_, (u32) pcmp->pcmc_pipr);
|
||||
return; /* No card in slot */
|
||||
}
|
||||
|
||||
check_ide_device (pcmcia_base);
|
||||
|
||||
#endif /* CONFIG_IDE_8xx_PCCARD */
|
||||
|
||||
base = pcmcia_base + ioport_dsc[data_port].base_off;
|
||||
#ifdef DEBUG
|
||||
printk ("base: %08x + %08x = %08x\n",
|
||||
pcmcia_base, ioport_dsc[data_port].base_off, base);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < IDE_NR_PORTS; ++i) {
|
||||
#ifdef DEBUG
|
||||
printk ("port[%d]: %08x + %08x = %08x\n",
|
||||
i,
|
||||
base,
|
||||
ioport_dsc[data_port].reg_off[i],
|
||||
i, base + ioport_dsc[data_port].reg_off[i]);
|
||||
#endif
|
||||
*p++ = base + ioport_dsc[data_port].reg_off[i];
|
||||
}
|
||||
|
||||
if (irq) {
|
||||
#ifdef CONFIG_IDE_8xx_PCCARD
|
||||
unsigned int reg;
|
||||
|
||||
*irq = ioport_dsc[data_port].irq;
|
||||
if (_slot_)
|
||||
pgcrx = &((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pgcrb;
|
||||
else
|
||||
pgcrx = &((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pgcra;
|
||||
|
||||
reg = *pgcrx;
|
||||
reg |= mk_int_int_mask (pcmcia_schlvl) << 24;
|
||||
reg |= mk_int_int_mask (pcmcia_schlvl) << 16;
|
||||
*pgcrx = reg;
|
||||
#else /* direct connected IDE drive, i.e. external IRQ, not the PCMCIA irq */
|
||||
*irq = ioport_dsc[data_port].irq;
|
||||
#endif /* CONFIG_IDE_8xx_PCCARD */
|
||||
}
|
||||
|
||||
/* register routine to tune PIO mode */
|
||||
ide_hwifs[data_port].tuneproc = m8xx_ide_tuneproc;
|
||||
|
||||
hw->ack_intr = (ide_ack_intr_t *) ide_interrupt_ack;
|
||||
/* Enable Harddisk Interrupt,
|
||||
* and make it edge sensitive
|
||||
*/
|
||||
/* (11-18) Set edge detect for irq, no wakeup from low power mode */
|
||||
((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel |=
|
||||
(0x80000000 >> ioport_dsc[data_port].irq);
|
||||
|
||||
#ifdef CONFIG_IDE_8xx_PCCARD
|
||||
/* Make sure we don't get garbage irq */
|
||||
((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pscr = 0xFFFF;
|
||||
|
||||
/* Enable falling edge irq */
|
||||
pcmp->pcmc_per = 0x100000 >> (16 * _slot_);
|
||||
#endif /* CONFIG_IDE_8xx_PCCARD */
|
||||
} /* m8xx_ide_init_hwif_ports() using 8xx internal PCMCIA interface */
|
||||
#endif /* CONFIG_IDE_8xx_PCCARD || CONFIG_IDE_8xx_DIRECT */
|
||||
|
||||
/*
|
||||
* m8xx_ide_init_hwif_ports for a direct IDE interface _not_ using
|
||||
* MPC8xx's internal PCMCIA interface
|
||||
*/
|
||||
#if defined(CONFIG_IDE_EXT_DIRECT)
|
||||
void m8xx_ide_init_hwif_ports (hw_regs_t *hw,
|
||||
unsigned long data_port, unsigned long ctrl_port, int *irq)
|
||||
{
|
||||
unsigned long *p = hw->io_ports;
|
||||
int i;
|
||||
|
||||
u32 ide_phy_base;
|
||||
u32 ide_phy_end;
|
||||
static unsigned long ide_base = 0;
|
||||
unsigned long base;
|
||||
|
||||
*p = 0;
|
||||
if (irq)
|
||||
*irq = 0;
|
||||
|
||||
if (!ide_base) {
|
||||
|
||||
/* TODO:
|
||||
* - add code to read ORx, BRx
|
||||
*/
|
||||
ide_phy_base = CFG_ATA_BASE_ADDR;
|
||||
ide_phy_end = CFG_ATA_BASE_ADDR + 0x200;
|
||||
|
||||
printk ("IDE phys mem : %08x...%08x (size %08x)\n",
|
||||
ide_phy_base, ide_phy_end,
|
||||
ide_phy_end - ide_phy_base);
|
||||
|
||||
ide_base=(unsigned long)ioremap(ide_phy_base,
|
||||
ide_phy_end-ide_phy_base);
|
||||
|
||||
#ifdef DEBUG
|
||||
printk ("IDE virt base: %08lx\n", ide_base);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (data_port >= MAX_HWIFS)
|
||||
return;
|
||||
|
||||
base = ide_base + ioport_dsc[data_port].base_off;
|
||||
#ifdef DEBUG
|
||||
printk ("base: %08x + %08x = %08x\n",
|
||||
ide_base, ioport_dsc[data_port].base_off, base);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < IDE_NR_PORTS; ++i) {
|
||||
#ifdef DEBUG
|
||||
printk ("port[%d]: %08x + %08x = %08x\n",
|
||||
i,
|
||||
base,
|
||||
ioport_dsc[data_port].reg_off[i],
|
||||
i, base + ioport_dsc[data_port].reg_off[i]);
|
||||
#endif
|
||||
*p++ = base + ioport_dsc[data_port].reg_off[i];
|
||||
}
|
||||
|
||||
if (irq) {
|
||||
/* direct connected IDE drive, i.e. external IRQ */
|
||||
*irq = ioport_dsc[data_port].irq;
|
||||
}
|
||||
|
||||
/* register routine to tune PIO mode */
|
||||
ide_hwifs[data_port].tuneproc = m8xx_ide_tuneproc;
|
||||
|
||||
hw->ack_intr = (ide_ack_intr_t *) ide_interrupt_ack;
|
||||
/* Enable Harddisk Interrupt,
|
||||
* and make it edge sensitive
|
||||
*/
|
||||
/* (11-18) Set edge detect for irq, no wakeup from low power mode */
|
||||
((immap_t *) IMAP_ADDR)->im_siu_conf.sc_siel |=
|
||||
(0x80000000 >> ioport_dsc[data_port].irq);
|
||||
} /* m8xx_ide_init_hwif_ports() for CONFIG_IDE_8xx_DIRECT */
|
||||
|
||||
#endif /* CONFIG_IDE_8xx_DIRECT */
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
|
||||
/* PCMCIA Timing */
|
||||
#ifndef PCMCIA_SHT
|
||||
#define PCMCIA_SHT(t) ((t & 0x0F)<<16) /* Strobe Hold Time */
|
||||
#define PCMCIA_SST(t) ((t & 0x0F)<<12) /* Strobe Setup Time */
|
||||
#define PCMCIA_SL(t) ((t==32) ? 0 : ((t & 0x1F)<<7)) /* Strobe Length */
|
||||
#endif
|
||||
|
||||
|
||||
/* Calculate PIO timings */
|
||||
static void
|
||||
m8xx_ide_tuneproc(ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
ide_pio_data_t d;
|
||||
#if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT)
|
||||
volatile pcmconf8xx_t *pcmp;
|
||||
ulong timing, mask, reg;
|
||||
#endif
|
||||
|
||||
pio = ide_get_best_pio_mode(drive, pio, 4, &d);
|
||||
|
||||
#if 1
|
||||
printk("%s[%d] %s: best PIO mode: %d\n",
|
||||
__FILE__,__LINE__,__FUNCTION__, pio);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT)
|
||||
pcmp = (pcmconf8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_pcmcia));
|
||||
|
||||
mask = ~(PCMCIA_SHT(0xFF) | PCMCIA_SST(0xFF) | PCMCIA_SL(0xFF));
|
||||
|
||||
timing = PCMCIA_SHT(hold_time[pio] )
|
||||
| PCMCIA_SST(ide_pio_clocks[pio].setup_time )
|
||||
| PCMCIA_SL (ide_pio_clocks[pio].active_time)
|
||||
;
|
||||
|
||||
#if 1
|
||||
printk ("Setting timing bits 0x%08lx in PCMCIA controller\n", timing);
|
||||
#endif
|
||||
if ((reg = pcmp->pcmc_por0 & mask) != 0)
|
||||
pcmp->pcmc_por0 = reg | timing;
|
||||
|
||||
if ((reg = pcmp->pcmc_por1 & mask) != 0)
|
||||
pcmp->pcmc_por1 = reg | timing;
|
||||
|
||||
if ((reg = pcmp->pcmc_por2 & mask) != 0)
|
||||
pcmp->pcmc_por2 = reg | timing;
|
||||
|
||||
if ((reg = pcmp->pcmc_por3 & mask) != 0)
|
||||
pcmp->pcmc_por3 = reg | timing;
|
||||
|
||||
if ((reg = pcmp->pcmc_por4 & mask) != 0)
|
||||
pcmp->pcmc_por4 = reg | timing;
|
||||
|
||||
if ((reg = pcmp->pcmc_por5 & mask) != 0)
|
||||
pcmp->pcmc_por5 = reg | timing;
|
||||
|
||||
if ((reg = pcmp->pcmc_por6 & mask) != 0)
|
||||
pcmp->pcmc_por6 = reg | timing;
|
||||
|
||||
if ((reg = pcmp->pcmc_por7 & mask) != 0)
|
||||
pcmp->pcmc_por7 = reg | timing;
|
||||
|
||||
#elif defined(CONFIG_IDE_EXT_DIRECT)
|
||||
|
||||
printk("%s[%d] %s: not implemented yet!\n",
|
||||
__FILE__,__LINE__,__FUNCTION__);
|
||||
#endif /* defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_PCMCIA */
|
||||
}
|
||||
|
||||
static void
|
||||
ide_interrupt_ack (void *dev)
|
||||
{
|
||||
#ifdef CONFIG_IDE_8xx_PCCARD
|
||||
u_int pscr, pipr;
|
||||
|
||||
#if (PCMCIA_SOCKETS_NO == 2)
|
||||
u_int _slot_;
|
||||
#endif
|
||||
|
||||
/* get interrupt sources */
|
||||
|
||||
pscr = ((volatile immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr;
|
||||
pipr = ((volatile immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pipr;
|
||||
|
||||
/*
|
||||
* report only if both card detect signals are the same
|
||||
* not too nice done,
|
||||
* we depend on that CD2 is the bit to the left of CD1...
|
||||
*/
|
||||
|
||||
if(_slot_==-1){
|
||||
printk("PCMCIA slot has not been defined! Using A as default\n");
|
||||
_slot_=0;
|
||||
}
|
||||
|
||||
if(((pipr & M8XX_PCMCIA_CD2(_slot_)) >> 1) ^
|
||||
(pipr & M8XX_PCMCIA_CD1(_slot_)) ) {
|
||||
printk ("card detect interrupt\n");
|
||||
}
|
||||
/* clear the interrupt sources */
|
||||
((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr = pscr;
|
||||
|
||||
#else /* ! CONFIG_IDE_8xx_PCCARD */
|
||||
/*
|
||||
* Only CONFIG_IDE_8xx_PCCARD is using the interrupt of the
|
||||
* MPC8xx's PCMCIA controller, so there is nothing to be done here
|
||||
* for CONFIG_IDE_8xx_DIRECT and CONFIG_IDE_EXT_DIRECT.
|
||||
* The interrupt is handled somewhere else. -- Steven
|
||||
*/
|
||||
#endif /* CONFIG_IDE_8xx_PCCARD */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* CIS Tupel codes
|
||||
*/
|
||||
#define CISTPL_NULL 0x00
|
||||
#define CISTPL_DEVICE 0x01
|
||||
#define CISTPL_LONGLINK_CB 0x02
|
||||
#define CISTPL_INDIRECT 0x03
|
||||
#define CISTPL_CONFIG_CB 0x04
|
||||
#define CISTPL_CFTABLE_ENTRY_CB 0x05
|
||||
#define CISTPL_LONGLINK_MFC 0x06
|
||||
#define CISTPL_BAR 0x07
|
||||
#define CISTPL_PWR_MGMNT 0x08
|
||||
#define CISTPL_EXTDEVICE 0x09
|
||||
#define CISTPL_CHECKSUM 0x10
|
||||
#define CISTPL_LONGLINK_A 0x11
|
||||
#define CISTPL_LONGLINK_C 0x12
|
||||
#define CISTPL_LINKTARGET 0x13
|
||||
#define CISTPL_NO_LINK 0x14
|
||||
#define CISTPL_VERS_1 0x15
|
||||
#define CISTPL_ALTSTR 0x16
|
||||
#define CISTPL_DEVICE_A 0x17
|
||||
#define CISTPL_JEDEC_C 0x18
|
||||
#define CISTPL_JEDEC_A 0x19
|
||||
#define CISTPL_CONFIG 0x1a
|
||||
#define CISTPL_CFTABLE_ENTRY 0x1b
|
||||
#define CISTPL_DEVICE_OC 0x1c
|
||||
#define CISTPL_DEVICE_OA 0x1d
|
||||
#define CISTPL_DEVICE_GEO 0x1e
|
||||
#define CISTPL_DEVICE_GEO_A 0x1f
|
||||
#define CISTPL_MANFID 0x20
|
||||
#define CISTPL_FUNCID 0x21
|
||||
#define CISTPL_FUNCE 0x22
|
||||
#define CISTPL_SWIL 0x23
|
||||
#define CISTPL_END 0xff
|
||||
|
||||
/*
|
||||
* CIS Function ID codes
|
||||
*/
|
||||
#define CISTPL_FUNCID_MULTI 0x00
|
||||
#define CISTPL_FUNCID_MEMORY 0x01
|
||||
#define CISTPL_FUNCID_SERIAL 0x02
|
||||
#define CISTPL_FUNCID_PARALLEL 0x03
|
||||
#define CISTPL_FUNCID_FIXED 0x04
|
||||
#define CISTPL_FUNCID_VIDEO 0x05
|
||||
#define CISTPL_FUNCID_NETWORK 0x06
|
||||
#define CISTPL_FUNCID_AIMS 0x07
|
||||
#define CISTPL_FUNCID_SCSI 0x08
|
||||
|
||||
/*
|
||||
* Fixed Disk FUNCE codes
|
||||
*/
|
||||
#define CISTPL_IDE_INTERFACE 0x01
|
||||
|
||||
#define CISTPL_FUNCE_IDE_IFACE 0x01
|
||||
#define CISTPL_FUNCE_IDE_MASTER 0x02
|
||||
#define CISTPL_FUNCE_IDE_SLAVE 0x03
|
||||
|
||||
/* First feature byte */
|
||||
#define CISTPL_IDE_SILICON 0x04
|
||||
#define CISTPL_IDE_UNIQUE 0x08
|
||||
#define CISTPL_IDE_DUAL 0x10
|
||||
|
||||
/* Second feature byte */
|
||||
#define CISTPL_IDE_HAS_SLEEP 0x01
|
||||
#define CISTPL_IDE_HAS_STANDBY 0x02
|
||||
#define CISTPL_IDE_HAS_IDLE 0x04
|
||||
#define CISTPL_IDE_LOW_POWER 0x08
|
||||
#define CISTPL_IDE_REG_INHIBIT 0x10
|
||||
#define CISTPL_IDE_HAS_INDEX 0x20
|
||||
#define CISTPL_IDE_IOIS16 0x40
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#define MAX_TUPEL_SZ 512
|
||||
#define MAX_FEATURES 4
|
||||
|
||||
static int check_ide_device (unsigned long base)
|
||||
{
|
||||
volatile u8 *ident = NULL;
|
||||
volatile u8 *feature_p[MAX_FEATURES];
|
||||
volatile u8 *p, *start;
|
||||
int n_features = 0;
|
||||
u8 func_id = ~0;
|
||||
u8 code, len;
|
||||
unsigned short config_base = 0;
|
||||
int found = 0;
|
||||
int i;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk ("PCMCIA MEM: %08lX\n", base);
|
||||
#endif
|
||||
start = p = (volatile u8 *) base;
|
||||
|
||||
while ((p - start) < MAX_TUPEL_SZ) {
|
||||
|
||||
code = *p; p += 2;
|
||||
|
||||
if (code == 0xFF) { /* End of chain */
|
||||
break;
|
||||
}
|
||||
|
||||
len = *p; p += 2;
|
||||
#ifdef DEBUG_PCMCIA
|
||||
{ volatile u8 *q = p;
|
||||
printk ("\nTuple code %02x length %d\n\tData:",
|
||||
code, len);
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
printk (" %02x", *q);
|
||||
q+= 2;
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG_PCMCIA */
|
||||
switch (code) {
|
||||
case CISTPL_VERS_1:
|
||||
ident = p + 4;
|
||||
break;
|
||||
case CISTPL_FUNCID:
|
||||
func_id = *p;
|
||||
break;
|
||||
case CISTPL_FUNCE:
|
||||
if (n_features < MAX_FEATURES)
|
||||
feature_p[n_features++] = p;
|
||||
break;
|
||||
case CISTPL_CONFIG:
|
||||
config_base = (*(p+6) << 8) + (*(p+4));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
p += 2 * len;
|
||||
}
|
||||
|
||||
found = identify (ident);
|
||||
|
||||
if (func_id != ((u8)~0)) {
|
||||
print_funcid (func_id);
|
||||
|
||||
if (func_id == CISTPL_FUNCID_FIXED)
|
||||
found = 1;
|
||||
else
|
||||
return (1); /* no disk drive */
|
||||
}
|
||||
|
||||
for (i=0; i<n_features; ++i) {
|
||||
print_fixed (feature_p[i]);
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
printk ("unknown card type\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* set level mode irq and I/O mapped device in config reg*/
|
||||
*((u8 *)(base + config_base)) = 0x41;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void print_funcid (int func)
|
||||
{
|
||||
switch (func) {
|
||||
case CISTPL_FUNCID_MULTI:
|
||||
printk (" Multi-Function");
|
||||
break;
|
||||
case CISTPL_FUNCID_MEMORY:
|
||||
printk (" Memory");
|
||||
break;
|
||||
case CISTPL_FUNCID_SERIAL:
|
||||
printk (" Serial Port");
|
||||
break;
|
||||
case CISTPL_FUNCID_PARALLEL:
|
||||
printk (" Parallel Port");
|
||||
break;
|
||||
case CISTPL_FUNCID_FIXED:
|
||||
printk (" Fixed Disk");
|
||||
break;
|
||||
case CISTPL_FUNCID_VIDEO:
|
||||
printk (" Video Adapter");
|
||||
break;
|
||||
case CISTPL_FUNCID_NETWORK:
|
||||
printk (" Network Adapter");
|
||||
break;
|
||||
case CISTPL_FUNCID_AIMS:
|
||||
printk (" AIMS Card");
|
||||
break;
|
||||
case CISTPL_FUNCID_SCSI:
|
||||
printk (" SCSI Adapter");
|
||||
break;
|
||||
default:
|
||||
printk (" Unknown");
|
||||
break;
|
||||
}
|
||||
printk (" Card\n");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void print_fixed (volatile u8 *p)
|
||||
{
|
||||
if (p == NULL)
|
||||
return;
|
||||
|
||||
switch (*p) {
|
||||
case CISTPL_FUNCE_IDE_IFACE:
|
||||
{ u8 iface = *(p+2);
|
||||
|
||||
printk ((iface == CISTPL_IDE_INTERFACE) ? " IDE" : " unknown");
|
||||
printk (" interface ");
|
||||
break;
|
||||
}
|
||||
case CISTPL_FUNCE_IDE_MASTER:
|
||||
case CISTPL_FUNCE_IDE_SLAVE:
|
||||
{ u8 f1 = *(p+2);
|
||||
u8 f2 = *(p+4);
|
||||
|
||||
printk ((f1 & CISTPL_IDE_SILICON) ? " [silicon]" : " [rotating]");
|
||||
|
||||
if (f1 & CISTPL_IDE_UNIQUE)
|
||||
printk (" [unique]");
|
||||
|
||||
printk ((f1 & CISTPL_IDE_DUAL) ? " [dual]" : " [single]");
|
||||
|
||||
if (f2 & CISTPL_IDE_HAS_SLEEP)
|
||||
printk (" [sleep]");
|
||||
|
||||
if (f2 & CISTPL_IDE_HAS_STANDBY)
|
||||
printk (" [standby]");
|
||||
|
||||
if (f2 & CISTPL_IDE_HAS_IDLE)
|
||||
printk (" [idle]");
|
||||
|
||||
if (f2 & CISTPL_IDE_LOW_POWER)
|
||||
printk (" [low power]");
|
||||
|
||||
if (f2 & CISTPL_IDE_REG_INHIBIT)
|
||||
printk (" [reg inhibit]");
|
||||
|
||||
if (f2 & CISTPL_IDE_HAS_INDEX)
|
||||
printk (" [index]");
|
||||
|
||||
if (f2 & CISTPL_IDE_IOIS16)
|
||||
printk (" [IOis16]");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
printk ("\n");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#define MAX_IDENT_CHARS 64
|
||||
#define MAX_IDENT_FIELDS 4
|
||||
|
||||
static u8 *known_cards[] = {
|
||||
"ARGOSY PnPIDE D5",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int identify (volatile u8 *p)
|
||||
{
|
||||
u8 id_str[MAX_IDENT_CHARS];
|
||||
u8 data;
|
||||
u8 *t;
|
||||
u8 **card;
|
||||
int i, done;
|
||||
|
||||
if (p == NULL)
|
||||
return (0); /* Don't know */
|
||||
|
||||
t = id_str;
|
||||
done =0;
|
||||
|
||||
for (i=0; i<=4 && !done; ++i, p+=2) {
|
||||
while ((data = *p) != '\0') {
|
||||
if (data == 0xFF) {
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
*t++ = data;
|
||||
if (t == &id_str[MAX_IDENT_CHARS-1]) {
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
p += 2;
|
||||
}
|
||||
if (!done)
|
||||
*t++ = ' ';
|
||||
}
|
||||
*t = '\0';
|
||||
while (--t > id_str) {
|
||||
if (*t == ' ')
|
||||
*t = '\0';
|
||||
else
|
||||
break;
|
||||
}
|
||||
printk ("Card ID: %s\n", id_str);
|
||||
|
||||
for (card=known_cards; *card; ++card) {
|
||||
if (strcmp(*card, id_str) == 0) { /* found! */
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
return (0); /* don't know */
|
||||
}
|
||||
|
||||
void m8xx_ide_init(void)
|
||||
{
|
||||
ppc_ide_md.default_irq = m8xx_ide_default_irq;
|
||||
ppc_ide_md.default_io_base = m8xx_ide_default_io_base;
|
||||
ppc_ide_md.ide_init_hwif = m8xx_ide_init_hwif_ports;
|
||||
}
|
||||
2087
drivers/ide/ppc/pmac.c
Normal file
2087
drivers/ide/ppc/pmac.c
Normal file
File diff suppressed because it is too large
Load Diff
863
drivers/ide/setup-pci.c
Normal file
863
drivers/ide/setup-pci.c
Normal file
@@ -0,0 +1,863 @@
|
||||
/*
|
||||
* linux/drivers/ide/setup-pci.c Version 1.10 2002/08/19
|
||||
*
|
||||
* Copyright (c) 1998-2000 Andre Hedrick <andre@linux-ide.org>
|
||||
*
|
||||
* Copyright (c) 1995-1998 Mark Lord
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*
|
||||
* Recent Changes
|
||||
* Split the set up function into multiple functions
|
||||
* Use pci_set_master
|
||||
* Fix misreporting of I/O v MMIO problems
|
||||
* Initial fixups for simplex devices
|
||||
*/
|
||||
|
||||
/*
|
||||
* This module provides support for automatic detection and
|
||||
* configuration of all PCI IDE interfaces present in a system.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
|
||||
/**
|
||||
* ide_match_hwif - match a PCI IDE against an ide_hwif
|
||||
* @io_base: I/O base of device
|
||||
* @bootable: set if its bootable
|
||||
* @name: name of device
|
||||
*
|
||||
* Match a PCI IDE port against an entry in ide_hwifs[],
|
||||
* based on io_base port if possible. Return the matching hwif,
|
||||
* or a new hwif. If we find an error (clashing, out of devices, etc)
|
||||
* return NULL
|
||||
*
|
||||
* FIXME: we need to handle mmio matches here too
|
||||
*/
|
||||
|
||||
static ide_hwif_t *ide_match_hwif(unsigned long io_base, u8 bootable, const char *name)
|
||||
{
|
||||
int h;
|
||||
ide_hwif_t *hwif;
|
||||
|
||||
/*
|
||||
* Look for a hwif with matching io_base specified using
|
||||
* parameters to ide_setup().
|
||||
*/
|
||||
for (h = 0; h < MAX_HWIFS; ++h) {
|
||||
hwif = &ide_hwifs[h];
|
||||
if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
|
||||
if (hwif->chipset == ide_forced)
|
||||
return hwif; /* a perfect match */
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Look for a hwif with matching io_base default value.
|
||||
* If chipset is "ide_unknown", then claim that hwif slot.
|
||||
* Otherwise, some other chipset has already claimed it.. :(
|
||||
*/
|
||||
for (h = 0; h < MAX_HWIFS; ++h) {
|
||||
hwif = &ide_hwifs[h];
|
||||
if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
|
||||
if (hwif->chipset == ide_unknown)
|
||||
return hwif; /* match */
|
||||
printk(KERN_ERR "%s: port 0x%04lx already claimed by %s\n",
|
||||
name, io_base, hwif->name);
|
||||
return NULL; /* already claimed */
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Okay, there is no hwif matching our io_base,
|
||||
* so we'll just claim an unassigned slot.
|
||||
* Give preference to claiming other slots before claiming ide0/ide1,
|
||||
* just in case there's another interface yet-to-be-scanned
|
||||
* which uses ports 1f0/170 (the ide0/ide1 defaults).
|
||||
*
|
||||
* Unless there is a bootable card that does not use the standard
|
||||
* ports 1f0/170 (the ide0/ide1 defaults). The (bootable) flag.
|
||||
*/
|
||||
if (bootable) {
|
||||
for (h = 0; h < MAX_HWIFS; ++h) {
|
||||
hwif = &ide_hwifs[h];
|
||||
if (hwif->chipset == ide_unknown)
|
||||
return hwif; /* pick an unused entry */
|
||||
}
|
||||
} else {
|
||||
for (h = 2; h < MAX_HWIFS; ++h) {
|
||||
hwif = ide_hwifs + h;
|
||||
if (hwif->chipset == ide_unknown)
|
||||
return hwif; /* pick an unused entry */
|
||||
}
|
||||
}
|
||||
for (h = 0; h < 2 && h < MAX_HWIFS; ++h) {
|
||||
hwif = ide_hwifs + h;
|
||||
if (hwif->chipset == ide_unknown)
|
||||
return hwif; /* pick an unused entry */
|
||||
}
|
||||
printk(KERN_ERR "%s: too many IDE interfaces, no room in table\n", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_setup_pci_baseregs - place a PCI IDE controller native
|
||||
* @dev: PCI device of interface to switch native
|
||||
* @name: Name of interface
|
||||
*
|
||||
* We attempt to place the PCI interface into PCI native mode. If
|
||||
* we succeed the BARs are ok and the controller is in PCI mode.
|
||||
* Returns 0 on success or an errno code.
|
||||
*
|
||||
* FIXME: if we program the interface and then fail to set the BARS
|
||||
* we don't switch it back to legacy mode. Do we actually care ??
|
||||
*/
|
||||
|
||||
static int ide_setup_pci_baseregs (struct pci_dev *dev, const char *name)
|
||||
{
|
||||
u8 progif = 0;
|
||||
|
||||
/*
|
||||
* Place both IDE interfaces into PCI "native" mode:
|
||||
*/
|
||||
if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) ||
|
||||
(progif & 5) != 5) {
|
||||
if ((progif & 0xa) != 0xa) {
|
||||
printk(KERN_INFO "%s: device not capable of full "
|
||||
"native PCI mode\n", name);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
printk("%s: placing both ports into native PCI mode\n", name);
|
||||
(void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5);
|
||||
if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) ||
|
||||
(progif & 5) != 5) {
|
||||
printk(KERN_ERR "%s: rewrite of PROGIF failed, wanted "
|
||||
"0x%04x, got 0x%04x\n",
|
||||
name, progif|5, progif);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDEDMA_PCI
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDEDMA_FORCED
|
||||
/*
|
||||
* Long lost data from 2.0.34 that is now in 2.0.39
|
||||
*
|
||||
* This was used in ./drivers/block/triton.c to do DMA Base address setup
|
||||
* when PnP failed. Oh the things we forget. I believe this was part
|
||||
* of SFF-8038i that has been withdrawn from public access... :-((
|
||||
*/
|
||||
#define DEFAULT_BMIBA 0xe800 /* in case BIOS did not init it */
|
||||
#define DEFAULT_BMCRBA 0xcc00 /* VIA's default value */
|
||||
#define DEFAULT_BMALIBA 0xd400 /* ALI's default value */
|
||||
#endif /* CONFIG_BLK_DEV_IDEDMA_FORCED */
|
||||
|
||||
/**
|
||||
* ide_get_or_set_dma_base - setup BMIBA
|
||||
* @hwif: Interface
|
||||
*
|
||||
* Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space:
|
||||
* If need be we set up the DMA base. Where a device has a partner that
|
||||
* is already in DMA mode we check and enforce IDE simplex rules.
|
||||
*/
|
||||
|
||||
static unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif)
|
||||
{
|
||||
unsigned long dma_base = 0;
|
||||
struct pci_dev *dev = hwif->pci_dev;
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDEDMA_FORCED
|
||||
int second_chance = 0;
|
||||
|
||||
second_chance_to_dma:
|
||||
#endif /* CONFIG_BLK_DEV_IDEDMA_FORCED */
|
||||
|
||||
if (hwif->mmio)
|
||||
return hwif->dma_base;
|
||||
|
||||
if (hwif->mate && hwif->mate->dma_base) {
|
||||
dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8);
|
||||
} else {
|
||||
dma_base = pci_resource_start(dev, 4);
|
||||
if (!dma_base) {
|
||||
printk(KERN_ERR "%s: dma_base is invalid\n",
|
||||
hwif->cds->name);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDEDMA_FORCED
|
||||
/* FIXME - should use pci_assign_resource surely */
|
||||
if ((!dma_base) && (!second_chance)) {
|
||||
unsigned long set_bmiba = 0;
|
||||
second_chance++;
|
||||
switch(dev->vendor) {
|
||||
case PCI_VENDOR_ID_AL:
|
||||
set_bmiba = DEFAULT_BMALIBA; break;
|
||||
case PCI_VENDOR_ID_VIA:
|
||||
set_bmiba = DEFAULT_BMCRBA; break;
|
||||
case PCI_VENDOR_ID_INTEL:
|
||||
set_bmiba = DEFAULT_BMIBA; break;
|
||||
default:
|
||||
return dma_base;
|
||||
}
|
||||
pci_write_config_dword(dev, 0x20, set_bmiba|1);
|
||||
goto second_chance_to_dma;
|
||||
}
|
||||
#endif /* CONFIG_BLK_DEV_IDEDMA_FORCED */
|
||||
|
||||
if (dma_base) {
|
||||
u8 simplex_stat = 0;
|
||||
dma_base += hwif->channel ? 8 : 0;
|
||||
|
||||
switch(dev->device) {
|
||||
case PCI_DEVICE_ID_AL_M5219:
|
||||
case PCI_DEVICE_ID_AL_M5229:
|
||||
case PCI_DEVICE_ID_AMD_VIPER_7409:
|
||||
case PCI_DEVICE_ID_CMD_643:
|
||||
case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE:
|
||||
case PCI_DEVICE_ID_REVOLUTION:
|
||||
simplex_stat = hwif->INB(dma_base + 2);
|
||||
hwif->OUTB((simplex_stat&0x60),(dma_base + 2));
|
||||
simplex_stat = hwif->INB(dma_base + 2);
|
||||
if (simplex_stat & 0x80) {
|
||||
printk(KERN_INFO "%s: simplex device: "
|
||||
"DMA forced\n",
|
||||
hwif->cds->name);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* If the device claims "simplex" DMA,
|
||||
* this means only one of the two interfaces
|
||||
* can be trusted with DMA at any point in time.
|
||||
* So we should enable DMA only on one of the
|
||||
* two interfaces.
|
||||
*/
|
||||
simplex_stat = hwif->INB(dma_base + 2);
|
||||
if (simplex_stat & 0x80) {
|
||||
/* simplex device? */
|
||||
/*
|
||||
* At this point we haven't probed the drives so we can't make the
|
||||
* appropriate decision. Really we should defer this problem
|
||||
* until we tune the drive then try to grab DMA ownership if we want
|
||||
* to be the DMA end. This has to be become dynamic to handle hot
|
||||
* plug.
|
||||
*/
|
||||
if (hwif->mate && hwif->mate->dma_base) {
|
||||
printk(KERN_INFO "%s: simplex device: "
|
||||
"DMA disabled\n",
|
||||
hwif->cds->name);
|
||||
dma_base = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dma_base;
|
||||
}
|
||||
#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */
|
||||
|
||||
void ide_setup_pci_noise (struct pci_dev *dev, ide_pci_device_t *d)
|
||||
{
|
||||
printk(KERN_INFO "%s: IDE controller at PCI slot %s\n",
|
||||
d->name, pci_name(dev));
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_setup_pci_noise);
|
||||
|
||||
|
||||
/**
|
||||
* ide_pci_enable - do PCI enables
|
||||
* @dev: PCI device
|
||||
* @d: IDE pci device data
|
||||
*
|
||||
* Enable the IDE PCI device. We attempt to enable the device in full
|
||||
* but if that fails then we only need BAR4 so we will enable that.
|
||||
*
|
||||
* Returns zero on success or an error code
|
||||
*/
|
||||
|
||||
static int ide_pci_enable(struct pci_dev *dev, ide_pci_device_t *d)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pci_enable_device(dev)) {
|
||||
ret = pci_enable_device_bars(dev, 1 << 4);
|
||||
if (ret < 0) {
|
||||
printk(KERN_WARNING "%s: (ide_setup_pci_device:) "
|
||||
"Could not enable device.\n", d->name);
|
||||
goto out;
|
||||
}
|
||||
printk(KERN_WARNING "%s: BIOS configuration fixed.\n", d->name);
|
||||
}
|
||||
|
||||
/*
|
||||
* assume all devices can do 32-bit dma for now. we can add a
|
||||
* dma mask field to the ide_pci_device_t if we need it (or let
|
||||
* lower level driver set the dma mask)
|
||||
*/
|
||||
ret = pci_set_dma_mask(dev, DMA_32BIT_MASK);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "%s: can't set dma mask\n", d->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* FIXME: Temporary - until we put in the hotplug interface logic
|
||||
Check that the bits we want are not in use by someone else. */
|
||||
ret = pci_request_region(dev, 4, "ide_tmp");
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
pci_release_region(dev, 4);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_pci_configure - configure an unconfigured device
|
||||
* @dev: PCI device
|
||||
* @d: IDE pci device data
|
||||
*
|
||||
* Enable and configure the PCI device we have been passed.
|
||||
* Returns zero on success or an error code.
|
||||
*/
|
||||
|
||||
static int ide_pci_configure(struct pci_dev *dev, ide_pci_device_t *d)
|
||||
{
|
||||
u16 pcicmd = 0;
|
||||
/*
|
||||
* PnP BIOS was *supposed* to have setup this device, but we
|
||||
* can do it ourselves, so long as the BIOS has assigned an IRQ
|
||||
* (or possibly the device is using a "legacy header" for IRQs).
|
||||
* Maybe the user deliberately *disabled* the device,
|
||||
* but we'll eventually ignore it again if no drives respond.
|
||||
*/
|
||||
if (ide_setup_pci_baseregs(dev, d->name) || pci_write_config_word(dev, PCI_COMMAND, pcicmd|PCI_COMMAND_IO))
|
||||
{
|
||||
printk(KERN_INFO "%s: device disabled (BIOS)\n", d->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd)) {
|
||||
printk(KERN_ERR "%s: error accessing PCI regs\n", d->name);
|
||||
return -EIO;
|
||||
}
|
||||
if (!(pcicmd & PCI_COMMAND_IO)) {
|
||||
printk(KERN_ERR "%s: unable to enable IDE controller\n", d->name);
|
||||
return -ENXIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_pci_check_iomem - check a register is I/O
|
||||
* @dev: pci device
|
||||
* @d: ide_pci_device
|
||||
* @bar: bar number
|
||||
*
|
||||
* Checks if a BAR is configured and points to MMIO space. If so
|
||||
* print an error and return an error code. Otherwise return 0
|
||||
*/
|
||||
|
||||
static int ide_pci_check_iomem(struct pci_dev *dev, ide_pci_device_t *d, int bar)
|
||||
{
|
||||
ulong flags = pci_resource_flags(dev, bar);
|
||||
|
||||
/* Unconfigured ? */
|
||||
if (!flags || pci_resource_len(dev, bar) == 0)
|
||||
return 0;
|
||||
|
||||
/* I/O space */
|
||||
if(flags & PCI_BASE_ADDRESS_IO_MASK)
|
||||
return 0;
|
||||
|
||||
/* Bad */
|
||||
printk(KERN_ERR "%s: IO baseregs (BIOS) are reported "
|
||||
"as MEM, report to "
|
||||
"<andre@linux-ide.org>.\n", d->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_hwif_configure - configure an IDE interface
|
||||
* @dev: PCI device holding interface
|
||||
* @d: IDE pci data
|
||||
* @mate: Paired interface if any
|
||||
*
|
||||
* Perform the initial set up for the hardware interface structure. This
|
||||
* is done per interface port rather than per PCI device. There may be
|
||||
* more than one port per device.
|
||||
*
|
||||
* Returns the new hardware interface structure, or NULL on a failure
|
||||
*/
|
||||
|
||||
static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, ide_pci_device_t *d, ide_hwif_t *mate, int port, int irq)
|
||||
{
|
||||
unsigned long ctl = 0, base = 0;
|
||||
ide_hwif_t *hwif;
|
||||
|
||||
if ((d->flags & IDEPCI_FLAG_ISA_PORTS) == 0) {
|
||||
/* Possibly we should fail if these checks report true */
|
||||
ide_pci_check_iomem(dev, d, 2*port);
|
||||
ide_pci_check_iomem(dev, d, 2*port+1);
|
||||
|
||||
ctl = pci_resource_start(dev, 2*port+1);
|
||||
base = pci_resource_start(dev, 2*port);
|
||||
if ((ctl && !base) || (base && !ctl)) {
|
||||
printk(KERN_ERR "%s: inconsistent baseregs (BIOS) "
|
||||
"for port %d, skipping\n", d->name, port);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (!ctl)
|
||||
{
|
||||
/* Use default values */
|
||||
ctl = port ? 0x374 : 0x3f4;
|
||||
base = port ? 0x170 : 0x1f0;
|
||||
}
|
||||
if ((hwif = ide_match_hwif(base, d->bootable, d->name)) == NULL)
|
||||
return NULL; /* no room in ide_hwifs[] */
|
||||
if (hwif->io_ports[IDE_DATA_OFFSET] != base ||
|
||||
hwif->io_ports[IDE_CONTROL_OFFSET] != (ctl | 2)) {
|
||||
memset(&hwif->hw, 0, sizeof(hwif->hw));
|
||||
#ifndef IDE_ARCH_OBSOLETE_INIT
|
||||
ide_std_init_ports(&hwif->hw, base, (ctl | 2));
|
||||
hwif->hw.io_ports[IDE_IRQ_OFFSET] = 0;
|
||||
#else
|
||||
ide_init_hwif_ports(&hwif->hw, base, (ctl | 2), NULL);
|
||||
#endif
|
||||
memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
|
||||
hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
|
||||
}
|
||||
hwif->chipset = ide_pci;
|
||||
hwif->pci_dev = dev;
|
||||
hwif->cds = (struct ide_pci_device_s *) d;
|
||||
hwif->channel = port;
|
||||
|
||||
if (!hwif->irq)
|
||||
hwif->irq = irq;
|
||||
if (mate) {
|
||||
hwif->mate = mate;
|
||||
mate->mate = hwif;
|
||||
}
|
||||
return hwif;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_hwif_setup_dma - configure DMA interface
|
||||
* @dev: PCI device
|
||||
* @d: IDE pci data
|
||||
* @hwif: Hardware interface we are configuring
|
||||
*
|
||||
* Set up the DMA base for the interface. Enable the master bits as
|
||||
* necessary and attempt to bring the device DMA into a ready to use
|
||||
* state
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_BLK_DEV_IDEDMA_PCI
|
||||
static void ide_hwif_setup_dma(struct pci_dev *dev, ide_pci_device_t *d, ide_hwif_t *hwif)
|
||||
{
|
||||
}
|
||||
#else
|
||||
static void ide_hwif_setup_dma(struct pci_dev *dev, ide_pci_device_t *d, ide_hwif_t *hwif)
|
||||
{
|
||||
u16 pcicmd;
|
||||
pci_read_config_word(dev, PCI_COMMAND, &pcicmd);
|
||||
|
||||
if ((d->autodma == AUTODMA) ||
|
||||
((dev->class >> 8) == PCI_CLASS_STORAGE_IDE &&
|
||||
(dev->class & 0x80))) {
|
||||
unsigned long dma_base = ide_get_or_set_dma_base(hwif);
|
||||
if (dma_base && !(pcicmd & PCI_COMMAND_MASTER)) {
|
||||
/*
|
||||
* Set up BM-DMA capability
|
||||
* (PnP BIOS should have done this)
|
||||
*/
|
||||
/* default DMA off if we had to configure it here */
|
||||
hwif->autodma = 0;
|
||||
pci_set_master(dev);
|
||||
if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd) || !(pcicmd & PCI_COMMAND_MASTER)) {
|
||||
printk(KERN_ERR "%s: %s error updating PCICMD\n",
|
||||
hwif->name, d->name);
|
||||
dma_base = 0;
|
||||
}
|
||||
}
|
||||
if (dma_base) {
|
||||
if (d->init_dma) {
|
||||
d->init_dma(hwif, dma_base);
|
||||
} else {
|
||||
ide_setup_dma(hwif, dma_base, 8);
|
||||
}
|
||||
} else {
|
||||
printk(KERN_INFO "%s: %s Bus-Master DMA disabled "
|
||||
"(BIOS)\n", hwif->name, d->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BLK_DEV_IDEDMA_PCI*/
|
||||
|
||||
/**
|
||||
* ide_setup_pci_controller - set up IDE PCI
|
||||
* @dev: PCI device
|
||||
* @d: IDE PCI data
|
||||
* @noisy: verbose flag
|
||||
* @config: returned as 1 if we configured the hardware
|
||||
*
|
||||
* Set up the PCI and controller side of the IDE interface. This brings
|
||||
* up the PCI side of the device, checks that the device is enabled
|
||||
* and enables it if need be
|
||||
*/
|
||||
|
||||
static int ide_setup_pci_controller(struct pci_dev *dev, ide_pci_device_t *d, int noisy, int *config)
|
||||
{
|
||||
int ret;
|
||||
u32 class_rev;
|
||||
u16 pcicmd;
|
||||
|
||||
if (noisy)
|
||||
ide_setup_pci_noise(dev, d);
|
||||
|
||||
ret = ide_pci_enable(dev, d);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = pci_read_config_word(dev, PCI_COMMAND, &pcicmd);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "%s: error accessing PCI regs\n", d->name);
|
||||
goto out;
|
||||
}
|
||||
if (!(pcicmd & PCI_COMMAND_IO)) { /* is device disabled? */
|
||||
ret = ide_pci_configure(dev, d);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
*config = 1;
|
||||
printk(KERN_INFO "%s: device enabled (Linux)\n", d->name);
|
||||
}
|
||||
|
||||
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
|
||||
class_rev &= 0xff;
|
||||
if (noisy)
|
||||
printk(KERN_INFO "%s: chipset revision %d\n", d->name, class_rev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_pci_setup_ports - configure ports/devices on PCI IDE
|
||||
* @dev: PCI device
|
||||
* @d: IDE pci device info
|
||||
* @pciirq: IRQ line
|
||||
* @index: ata index to update
|
||||
*
|
||||
* Scan the interfaces attached to this device and do any
|
||||
* necessary per port setup. Attach the devices and ask the
|
||||
* generic DMA layer to do its work for us.
|
||||
*
|
||||
* Normally called automaticall from do_ide_pci_setup_device,
|
||||
* but is also used directly as a helper function by some controllers
|
||||
* where the chipset setup is not the default PCI IDE one.
|
||||
*/
|
||||
|
||||
void ide_pci_setup_ports(struct pci_dev *dev, ide_pci_device_t *d, int pciirq, ata_index_t *index)
|
||||
{
|
||||
int port;
|
||||
int at_least_one_hwif_enabled = 0;
|
||||
ide_hwif_t *hwif, *mate = NULL;
|
||||
u8 tmp;
|
||||
|
||||
index->all = 0xf0f0;
|
||||
|
||||
/*
|
||||
* Set up the IDE ports
|
||||
*/
|
||||
|
||||
for (port = 0; port <= 1; ++port) {
|
||||
ide_pci_enablebit_t *e = &(d->enablebits[port]);
|
||||
|
||||
if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) ||
|
||||
(tmp & e->mask) != e->val))
|
||||
continue; /* port not enabled */
|
||||
|
||||
if (d->channels <= port)
|
||||
break;
|
||||
|
||||
if ((hwif = ide_hwif_configure(dev, d, mate, port, pciirq)) == NULL)
|
||||
continue;
|
||||
|
||||
/* setup proper ancestral information */
|
||||
hwif->gendev.parent = &dev->dev;
|
||||
|
||||
if (hwif->channel) {
|
||||
index->b.high = hwif->index;
|
||||
} else {
|
||||
index->b.low = hwif->index;
|
||||
}
|
||||
|
||||
|
||||
if (d->init_iops)
|
||||
d->init_iops(hwif);
|
||||
|
||||
if (d->autodma == NODMA)
|
||||
goto bypass_legacy_dma;
|
||||
|
||||
if(d->init_setup_dma)
|
||||
d->init_setup_dma(dev, d, hwif);
|
||||
else
|
||||
ide_hwif_setup_dma(dev, d, hwif);
|
||||
bypass_legacy_dma:
|
||||
if (d->init_hwif)
|
||||
/* Call chipset-specific routine
|
||||
* for each enabled hwif
|
||||
*/
|
||||
d->init_hwif(hwif);
|
||||
|
||||
mate = hwif;
|
||||
at_least_one_hwif_enabled = 1;
|
||||
}
|
||||
if (!at_least_one_hwif_enabled)
|
||||
printk(KERN_INFO "%s: neither IDE port enabled (BIOS)\n", d->name);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_pci_setup_ports);
|
||||
|
||||
/*
|
||||
* ide_setup_pci_device() looks at the primary/secondary interfaces
|
||||
* on a PCI IDE device and, if they are enabled, prepares the IDE driver
|
||||
* for use with them. This generic code works for most PCI chipsets.
|
||||
*
|
||||
* One thing that is not standardized is the location of the
|
||||
* primary/secondary interface "enable/disable" bits. For chipsets that
|
||||
* we "know" about, this information is in the ide_pci_device_t struct;
|
||||
* for all other chipsets, we just assume both interfaces are enabled.
|
||||
*/
|
||||
static int do_ide_setup_pci_device(struct pci_dev *dev, ide_pci_device_t *d,
|
||||
ata_index_t *index, u8 noisy)
|
||||
{
|
||||
static ata_index_t ata_index = { .b = { .low = 0xff, .high = 0xff } };
|
||||
int tried_config = 0;
|
||||
int pciirq, ret;
|
||||
|
||||
ret = ide_setup_pci_controller(dev, d, noisy, &tried_config);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Can we trust the reported IRQ?
|
||||
*/
|
||||
pciirq = dev->irq;
|
||||
|
||||
/* Is it an "IDE storage" device in non-PCI mode? */
|
||||
if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 5) != 5) {
|
||||
if (noisy)
|
||||
printk(KERN_INFO "%s: not 100%% native mode: "
|
||||
"will probe irqs later\n", d->name);
|
||||
/*
|
||||
* This allows offboard ide-pci cards the enable a BIOS,
|
||||
* verify interrupt settings of split-mirror pci-config
|
||||
* space, place chipset into init-mode, and/or preserve
|
||||
* an interrupt if the card is not native ide support.
|
||||
*/
|
||||
ret = d->init_chipset ? d->init_chipset(dev, d->name) : 0;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
pciirq = ret;
|
||||
} else if (tried_config) {
|
||||
if (noisy)
|
||||
printk(KERN_INFO "%s: will probe irqs later\n", d->name);
|
||||
pciirq = 0;
|
||||
} else if (!pciirq) {
|
||||
if (noisy)
|
||||
printk(KERN_WARNING "%s: bad irq (%d): will probe later\n",
|
||||
d->name, pciirq);
|
||||
pciirq = 0;
|
||||
} else {
|
||||
if (d->init_chipset) {
|
||||
ret = d->init_chipset(dev, d->name);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
if (noisy)
|
||||
printk(KERN_INFO "%s: 100%% native mode on irq %d\n",
|
||||
d->name, pciirq);
|
||||
}
|
||||
|
||||
/* FIXME: silent failure can happen */
|
||||
|
||||
*index = ata_index;
|
||||
ide_pci_setup_ports(dev, d, pciirq, index);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ide_setup_pci_device(struct pci_dev *dev, ide_pci_device_t *d)
|
||||
{
|
||||
ata_index_t index_list;
|
||||
int ret;
|
||||
|
||||
ret = do_ide_setup_pci_device(dev, d, &index_list, 1);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if ((index_list.b.low & 0xf0) != 0xf0)
|
||||
probe_hwif_init_with_fixup(&ide_hwifs[index_list.b.low], d->fixup);
|
||||
if ((index_list.b.high & 0xf0) != 0xf0)
|
||||
probe_hwif_init_with_fixup(&ide_hwifs[index_list.b.high], d->fixup);
|
||||
|
||||
create_proc_ide_interfaces();
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_setup_pci_device);
|
||||
|
||||
int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2,
|
||||
ide_pci_device_t *d)
|
||||
{
|
||||
struct pci_dev *pdev[] = { dev1, dev2 };
|
||||
ata_index_t index_list[2];
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
ret = do_ide_setup_pci_device(pdev[i], d, index_list + i, !i);
|
||||
/*
|
||||
* FIXME: Mom, mom, they stole me the helper function to undo
|
||||
* do_ide_setup_pci_device() on the first device!
|
||||
*/
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
u8 idx[2] = { index_list[i].b.low, index_list[i].b.high };
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 2; j++) {
|
||||
if ((idx[j] & 0xf0) != 0xf0)
|
||||
probe_hwif_init(ide_hwifs + idx[j]);
|
||||
}
|
||||
}
|
||||
|
||||
create_proc_ide_interfaces();
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ide_setup_pci_devices);
|
||||
|
||||
/*
|
||||
* Module interfaces
|
||||
*/
|
||||
|
||||
static int pre_init = 1; /* Before first ordered IDE scan */
|
||||
static LIST_HEAD(ide_pci_drivers);
|
||||
|
||||
/*
|
||||
* __ide_pci_register_driver - attach IDE driver
|
||||
* @driver: pci driver
|
||||
* @module: owner module of the driver
|
||||
*
|
||||
* Registers a driver with the IDE layer. The IDE layer arranges that
|
||||
* boot time setup is done in the expected device order and then
|
||||
* hands the controllers off to the core PCI code to do the rest of
|
||||
* the work.
|
||||
*
|
||||
* The driver_data of the driver table must point to an ide_pci_device_t
|
||||
* describing the interface.
|
||||
*
|
||||
* Returns are the same as for pci_register_driver
|
||||
*/
|
||||
|
||||
int __ide_pci_register_driver(struct pci_driver *driver, struct module *module,
|
||||
const char *mod_name)
|
||||
{
|
||||
if(!pre_init)
|
||||
return __pci_register_driver(driver, module, mod_name);
|
||||
driver->driver.owner = module;
|
||||
list_add_tail(&driver->node, &ide_pci_drivers);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(__ide_pci_register_driver);
|
||||
|
||||
/**
|
||||
* ide_scan_pcidev - find an IDE driver for a device
|
||||
* @dev: PCI device to check
|
||||
*
|
||||
* Look for an IDE driver to handle the device we are considering.
|
||||
* This is only used during boot up to get the ordering correct. After
|
||||
* boot up the pci layer takes over the job.
|
||||
*/
|
||||
|
||||
static int __init ide_scan_pcidev(struct pci_dev *dev)
|
||||
{
|
||||
struct list_head *l;
|
||||
struct pci_driver *d;
|
||||
|
||||
list_for_each(l, &ide_pci_drivers)
|
||||
{
|
||||
d = list_entry(l, struct pci_driver, node);
|
||||
if(d->id_table)
|
||||
{
|
||||
const struct pci_device_id *id = pci_match_id(d->id_table, dev);
|
||||
if(id != NULL)
|
||||
{
|
||||
if(d->probe(dev, id) >= 0)
|
||||
{
|
||||
dev->driver = d;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_scan_pcibus - perform the initial IDE driver scan
|
||||
* @scan_direction: set for reverse order scanning
|
||||
*
|
||||
* Perform the initial bus rather than driver ordered scan of the
|
||||
* PCI drivers. After this all IDE pci handling becomes standard
|
||||
* module ordering not traditionally ordered.
|
||||
*/
|
||||
|
||||
void __init ide_scan_pcibus (int scan_direction)
|
||||
{
|
||||
struct pci_dev *dev = NULL;
|
||||
struct pci_driver *d;
|
||||
struct list_head *l, *n;
|
||||
|
||||
pre_init = 0;
|
||||
if (!scan_direction) {
|
||||
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
|
||||
ide_scan_pcidev(dev);
|
||||
}
|
||||
} else {
|
||||
while ((dev = pci_get_device_reverse(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
|
||||
ide_scan_pcidev(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Hand the drivers over to the PCI layer now we
|
||||
* are post init.
|
||||
*/
|
||||
|
||||
list_for_each_safe(l, n, &ide_pci_drivers)
|
||||
{
|
||||
list_del(l);
|
||||
d = list_entry(l, struct pci_driver, node);
|
||||
__pci_register_driver(d, d->driver.owner, d->driver.mod_name);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user