diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-12-16 06:06:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-12-16 06:06:25 +0000 |
commit | aa944aa3453e47706685bc562711a9e87375941e (patch) | |
tree | 8fb37a65f205a90412917ca2b91c429263ef1790 /drivers/block | |
parent | 967c65a99059fd459b956c1588ce0ba227912c4e (diff) |
Merge with Linux 2.1.72, part 2.
The new signal code with exception of the code for the rt signals.
The definitions in <asm/siginfo.h> and <asm/ucontext.h> are currently
just stolen from the Alpha and will need to be overhauled.
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/ide-dma.c | 678 | ||||
-rw-r--r-- | drivers/block/pdc4030.c | 365 | ||||
-rw-r--r-- | drivers/block/pdc4030.h | 44 | ||||
-rw-r--r-- | drivers/block/promise.c | 0 | ||||
-rw-r--r-- | drivers/block/promise.h | 0 | ||||
-rw-r--r-- | drivers/block/triton.c | 0 | ||||
-rw-r--r-- | drivers/block/trm290.c | 227 |
7 files changed, 1314 insertions, 0 deletions
diff --git a/drivers/block/ide-dma.c b/drivers/block/ide-dma.c new file mode 100644 index 000000000..6a2886f78 --- /dev/null +++ b/drivers/block/ide-dma.c @@ -0,0 +1,678 @@ +/* + * linux/drivers/block/ide-dma.c Version 4.06 December 3, 1997 + * + * 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-2.4 (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 some (all?) ACER motherboards/BIOSs. Hopefully the fix + * still works here (?). + * + * 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. + */ +#include <linux/config.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/bios32.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/dma.h> + +#include "ide.h" + +/* + * good_dma_drives() lists the model names (from "hdparm -i") + * of drives which do not support mode2 DMA but which are + * known to work fine with this interface under Linux. + */ +const char *good_dma_drives[] = {"Micropolis 2112A", + "CONNER CTMA 4000", + NULL}; + +/* + * Our Physical Region Descriptor (PRD) table should be large enough + * to handle the biggest I/O request we are likely to see. Since requests + * can have no more than 256 sectors, and since the typical blocksize is + * two or more sectors, we could get by with a limit of 128 entries here for + * the usual worst case. Most requests seem to include some contiguous blocks, + * further reducing the number of table entries required. + * + * The driver reverts to PIO mode for individual requests that exceed + * this limit (possible with 512 byte blocksizes, eg. MSDOS f/s), so handling + * 100% of all crazy scenarios here is not necessary. + * + * As it turns out though, we must allocate a full 4KB page for this, + * so the two PRD tables (ide0 & ide1) will each get half of that, + * allowing each to have about 256 entries (8 bytes each) from this. + */ +#define PRD_BYTES 8 +#define PRD_ENTRIES (PAGE_SIZE / (2 * PRD_BYTES)) + +static int config_drive_for_dma (ide_drive_t *); + +/* + * dma_intr() is the handler for disk read/write DMA interrupts + */ +static void dma_intr (ide_drive_t *drive) +{ + byte stat, dma_stat; + int i; + struct request *rq = HWGROUP(drive)->rq; + unsigned short dma_base = HWIF(drive)->dma_base; + + dma_stat = inb(dma_base+2); /* get DMA status */ + outb(inb(dma_base)&~1, dma_base); /* stop DMA operation */ + stat = GET_STAT(); /* get drive status */ + if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { + if ((dma_stat & 7) == 4) { /* verify good DMA status */ + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, HWGROUP(drive)); + } + return; + } + printk("%s: bad DMA status: 0x%02x\n", drive->name, dma_stat); + } + sti(); + ide_error(drive, "dma_intr", stat); +} + +/* + * ide_build_dmatable() prepares a dma request. + * Returns 0 if all went okay, returns 1 otherwise. + * May also be invoked from trm290.c + */ +int ide_build_dmatable (ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + struct buffer_head *bh = rq->bh; + unsigned long size, addr, *table = HWIF(drive)->dmatable; +#ifdef CONFIG_BLK_DEV_TRM290 + unsigned int is_trm290_chipset = (HWIF(drive)->chipset == ide_trm290); +#else + const int is_trm290_chipset = 0; +#endif + unsigned int count = 0; + + do { + /* + * Determine addr and size of next buffer area. We assume that + * individual virtual buffers are always composed linearly in + * physical memory. For example, we assume that any 8kB buffer + * is always composed of two adjacent physical 4kB pages rather + * than two possibly non-adjacent physical 4kB pages. + */ + if (bh == NULL) { /* paging requests have (rq->bh == NULL) */ + addr = virt_to_bus (rq->buffer); + size = rq->nr_sectors << 9; + } else { + /* group sequential buffers into one large buffer */ + addr = virt_to_bus (bh->b_data); + size = bh->b_size; + while ((bh = bh->b_reqnext) != NULL) { + if ((addr + size) != virt_to_bus (bh->b_data)) + break; + size += bh->b_size; + } + } + /* + * Fill in the dma table, without crossing any 64kB boundaries. + * The hardware requires 16-bit alignment of all blocks + * (trm290 requires 32-bit alignment). + */ + if ((addr & 3)) { + printk("%s: misaligned DMA buffer\n", drive->name); + return 0; + } + while (size) { + if (++count >= PRD_ENTRIES) { + printk("%s: DMA table too small\n", drive->name); + return 0; /* revert to PIO for this request */ + } else { + unsigned long xcount, bcount = 0x10000 - (addr & 0xffff); + if (bcount > size) + bcount = size; + *table++ = addr; + xcount = bcount & 0xffff; + if (is_trm290_chipset) + xcount = ((xcount >> 2) - 1) << 16; + *table++ = xcount; + addr += bcount; + size -= bcount; + } + } + } while (bh != NULL); + if (count) { + if (!is_trm290_chipset) + *--table |= 0x80000000; /* set End-Of-Table (EOT) bit */ + return count; + } + printk("%s: empty DMA table?\n", drive->name); + return 0; +} + +/* + * ide_dmaproc() initiates/aborts DMA read/write operations on a drive. + * + * The caller is assumed to have selected the drive and programmed the drive's + * sector address using CHS or LBA. All that remains is to prepare for DMA + * and then issue the actual read/write DMA/PIO command to the drive. + * + * For ATAPI devices, we just prepare for DMA and return. The caller should + * then issue the packet command to the drive and call us again with + * ide_dma_begin afterwards. + * + * Returns 0 if all went well. + * Returns 1 if DMA read/write could not be started, in which case + * the caller should revert to PIO for the current request. + * May also be invoked from trm290.c + */ +int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned long dma_base = hwif->dma_base; + unsigned int count, reading = 0; + + switch (func) { + case ide_dma_off: + printk("%s: DMA disabled\n", drive->name); + case ide_dma_off_quietly: + case ide_dma_on: + drive->using_dma = (func == ide_dma_on); + return 0; + case ide_dma_abort: + outb(inb(dma_base)&~1, dma_base); /* stop DMA */ + return 0; + case ide_dma_check: + return config_drive_for_dma (drive); + case ide_dma_status_bad: + return ((inb(dma_base+2) & 7) != 4); /* verify good DMA status */ + case ide_dma_transferred: + return 0; /* NOT IMPLEMENTED: number of bytes actually transferred */ + case ide_dma_begin: + outb(inb(dma_base)|1, dma_base); /* begin DMA */ + return 0; + default: + printk("ide_dmaproc: unsupported func: %d\n", func); + return 1; + case ide_dma_read: + reading = 1 << 3; + case ide_dma_write: + if (!(count = ide_build_dmatable(drive))) + return 1; /* try PIO instead of DMA */ + outl(virt_to_bus(hwif->dmatable), dma_base + 4); /* PRD table */ + outb(reading, dma_base); /* specify r/w */ + outb(inb(dma_base+2)|0x06, dma_base+2); /* clear status bits */ + if (drive->media != ide_disk) + return 0; + ide_set_handler(drive, &dma_intr, WAIT_CMD); /* issue cmd to drive */ + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); + outb(inb(dma_base)|1, dma_base); /* begin DMA */ + return 0; + } +} + +static int config_drive_for_dma (ide_drive_t *drive) +{ + const char **list; + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + + if (id && (id->capability & 1)) { + /* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */ + if (id->field_valid & 4) /* UltraDMA */ + if ((id->dma_ultra & (id->dma_ultra >> 8) & 7)) + return hwif->dmaproc(ide_dma_on, drive); + /* 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 hwif->dmaproc(ide_dma_on, drive); + /* Consult the list of known "good" drives */ + list = good_dma_drives; + while (*list) { + if (!strcmp(*list++,id->model)) + return hwif->dmaproc(ide_dma_on, drive); + } + } + return hwif->dmaproc(ide_dma_off_quietly, drive); +} + +void ide_setup_dma (ide_hwif_t *hwif, unsigned short dmabase, unsigned int num_ports) +{ + static unsigned long dmatable = 0; + static unsigned leftover = 0; + + printk(" %s: BM-DMA at 0x%04x-0x%04x", hwif->name, dmabase, dmabase + num_ports - 1); + if (check_region(dmabase, num_ports)) { + printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n"); + return; + } + request_region(dmabase, num_ports, hwif->name); + hwif->dma_base = dmabase; + if (leftover < (PRD_ENTRIES * PRD_BYTES)) { + /* + * The BM-DMA uses full 32bit addr, so we can + * safely use __get_free_page() here instead + * of __get_dma_pages() -- no ISA limitations. + */ + dmatable = __get_free_pages(GFP_KERNEL,1,0); + leftover = dmatable ? PAGE_SIZE : 0; + } + if (!dmatable) { + printk(" -- ERROR, UNABLE TO ALLOCATE PRD TABLE\n"); + } else { + hwif->dmatable = (unsigned long *) dmatable; + dmatable += (PRD_ENTRIES * PRD_BYTES); + leftover -= (PRD_ENTRIES * PRD_BYTES); + hwif->dmaproc = &ide_dmaproc; + if (hwif->chipset != ide_trm290) { + byte dma_stat = inb(dmabase+2); + printk(", BIOS DMA settings: %s:%s %s:%s", + hwif->drives[0].name, (dma_stat & 0x20) ? "yes" : "no ", + hwif->drives[1].name, (dma_stat & 0x40) ? "yes" : "no"); + } + printk("\n"); + } +} + +#ifdef CONFIG_BLK_DEV_TRM290 +extern void ide_init_trm290(byte, byte, ide_hwif_t *); +#define INIT_TRM290 (&ide_init_trm290) +#else +#define INIT_TRM290 (NULL) +#endif /* CONFIG_BLK_DEV_TRM290 */ + +#ifdef CONFIG_BLK_DEV_OPTI621 +extern void ide_init_opti621(byte, byte, ide_hwif_t *); +#define INIT_OPTI (&ide_init_opti621) +#else +#define INIT_OPTI (NULL) +#endif /* CONFIG_BLK_DEV_OPTI621 */ + +#define DEVID_PIIX (PCI_VENDOR_ID_INTEL |(PCI_DEVICE_ID_INTEL_82371_1 <<16)) +#define DEVID_PIIX3 (PCI_VENDOR_ID_INTEL |(PCI_DEVICE_ID_INTEL_82371SB_1 <<16)) +#define DEVID_PIIX4 (PCI_VENDOR_ID_INTEL |(PCI_DEVICE_ID_INTEL_82371AB <<16)) +#define DEVID_VP_IDE (PCI_VENDOR_ID_VIA |(PCI_DEVICE_ID_VIA_82C586_1 <<16)) +#define DEVID_PDC20246 (PCI_VENDOR_ID_PROMISE|(PCI_DEVICE_ID_PROMISE_20246 <<16)) +#define DEVID_RZ1000 (PCI_VENDOR_ID_PCTECH |(PCI_DEVICE_ID_PCTECH_RZ1000 <<16)) +#define DEVID_RZ1001 (PCI_VENDOR_ID_PCTECH |(PCI_DEVICE_ID_PCTECH_RZ1001 <<16)) +#define DEVID_CMD640 (PCI_VENDOR_ID_CMD |(PCI_DEVICE_ID_CMD_640 <<16)) +#define DEVID_CMD646 (PCI_VENDOR_ID_CMD |(PCI_DEVICE_ID_CMD_646 <<16)) +#define DEVID_SIS5513 (PCI_VENDOR_ID_SI |(PCI_DEVICE_ID_SI_5513 <<16)) +#define DEVID_OPTI (PCI_VENDOR_ID_OPTI |(PCI_DEVICE_ID_OPTI_82C621 <<16)) +#define DEVID_OPTI2 (PCI_VENDOR_ID_OPTI |(0xd568 /* from datasheets */ <<16)) +#define DEVID_TRM290 (PCI_VENDOR_ID_TEKRAM |(PCI_DEVICE_ID_TEKRAM_DC290 <<16)) +#define DEVID_NS87410 (PCI_VENDOR_ID_NS |(PCI_DEVICE_ID_NS_87410 <<16)) +#define DEVID_HT6565 (PCI_VENDOR_ID_HOLTEK |(PCI_DEVICE_ID_HOLTEK_6565 <<16)) + +typedef struct ide_pci_enablebit_s { + byte reg; /* byte pci reg holding the enable-bit */ + byte mask; /* mask to isolate the enable-bit */ + byte val; /* value of masked reg when "enabled" */ +} ide_pci_enablebit_t; + +typedef struct ide_pci_device_s { + unsigned int id; + const char *name; + void (*init_hwif)(byte bus, byte fn, ide_hwif_t *hwif); + ide_pci_enablebit_t enablebits[2]; +} ide_pci_device_t; + +static ide_pci_device_t ide_pci_chipsets[] = { + {DEVID_PIIX, "PIIX", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, + {DEVID_PIIX3, "PIIX3", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, + {DEVID_PIIX4, "PIIX4", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, + {DEVID_VP_IDE, "VP_IDE", NULL, {{0x40,0x02,0x02}, {0x40,0x01,0x01}} }, + {DEVID_PDC20246,"PDC20246", NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}} }, + {DEVID_RZ1000, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_RZ1001, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_CMD640, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_OPTI, "OPTI", INIT_OPTI, {{0x45,0x80,0x00}, {0x40,0x08,0x00}} }, + {DEVID_OPTI2, "OPTI2", INIT_OPTI, {{0x45,0x80,0x00}, {0x40,0x08,0x00}} }, + {DEVID_SIS5513, "SIS5513", NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}} }, + {DEVID_CMD646, "CMD646", NULL, {{0x00,0x00,0x00}, {0x51,0x80,0x80}} }, + {DEVID_TRM290, "TRM290", INIT_TRM290, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_NS87410, "NS87410", NULL, {{0x43,0x08,0x08}, {0x47,0x08,0x08}} }, + {DEVID_HT6565, "HT6565", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {0, "PCI_IDE", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }}; + +__initfunc(static ide_pci_device_t *lookup_devid(unsigned int devid)) +{ + ide_pci_device_t *d = ide_pci_chipsets; + while (d->id && d->id != devid) + ++d; + return d; +} + +/* The next two functions were stolen from cmd640.c, with + a few modifications */ + +__initfunc(static void write_pcicfg_dword (byte fn, unsigned short reg, long val)) +{ + unsigned long flags; + + save_flags(flags); + cli(); + outl_p((reg & 0xfc) | ((fn * 0x100) + 0x80000000), 0xcf8); + outl_p(val, (reg & 3) | 0xcfc); + restore_flags(flags); +} + +__initfunc(static long read_pcicfg_dword (byte fn, unsigned short reg)) +{ + long b; + unsigned long flags; + + save_flags(flags); + cli(); + outl_p((reg & 0xfc) | ((fn * 0x100) + 0x80000000), 0xcf8); + b = inl_p((reg & 3) | 0xcfc); + restore_flags(flags); + return b; +} + +/* + * Search for an (apparently) unused block of I/O space + * of "size" bytes in length. + */ +__initfunc(static short find_free_region (unsigned short size)) +{ + unsigned short i, base = 0xe800; + for (base = 0xe800; base > 0; base -= 0x800) { + if (!check_region(base,size)) { + for (i = 0; i < size; i++) { + if (inb(base+i) != 0xff) + goto next; + } + return base; /* success */ + } + next: + } + return 0; /* failure */ +} + +/* + * Fetch the Bus-Master I/O Base-Address (BMIBA) from PCI space: + */ +__initfunc(static unsigned int ide_get_or_set_bmiba (byte bus, byte fn, const char *name)) +{ + unsigned int bmiba = 0; + unsigned short base; + int rc; + + if ((rc = pcibios_read_config_dword(bus, fn, 0x20, &bmiba))) { + printk("%s: failed to read BMIBA\n", name); + } else if ((bmiba &= 0xfff0) == 0) { + printk("%s: BMIBA is invalid (0x%04x, BIOS problem)\n", name, bmiba); + base = find_free_region(16); + if (base) { + printk("%s: setting BMIBA to 0x%04x\n", name, base); + pcibios_write_config_dword(bus, fn, 0x20, base | 1); + pcibios_read_config_dword(bus, fn, 0x20, &bmiba); + bmiba &= 0xfff0; + if (bmiba != base) { + if (bus == 0) { + printk("%s: operation failed, bypassing BIOS to try again\n", name); + write_pcicfg_dword(fn, 0x20, base | 1); + bmiba = read_pcicfg_dword(fn, 0x20) & 0xfff0; + } + if (bmiba != base) { + printk("%s: operation failed, DMA disabled\n", name); + bmiba = 0; + } + } + } + } + return bmiba; +} + +/* + * Match a PCI IDE port against an entry in ide_hwifs[], + * based on io_base port if possible. + */ +__initfunc(ide_hwif_t *ide_match_hwif (unsigned int io_base)) +{ + 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_generic) + 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 */ + 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 ide2/ide3 before ide0/ide1, + * just in case there's another interface yet-to-be-scanned + * which uses ports 1f0/170 (the ide0/ide1 defaults). + */ + for (h = 0; h < MAX_HWIFS; ++h) { + int hwifs[] = {2,3,1,0}; /* assign 3rd/4th before 1st/2nd */ + hwif = &ide_hwifs[hwifs[h]]; + if (hwif->chipset == ide_unknown) + return hwif; /* pick an unused entry */ + } + return NULL; +} + +/* + * 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. + */ +__initfunc(static void ide_setup_pci_device (byte bus, byte fn, unsigned int bmiba, ide_pci_device_t *d)) +{ + unsigned int port, at_least_one_hwif_enabled = 0; + unsigned short base = 0, ctl = 0; + byte tmp = 0; + ide_hwif_t *hwif, *mate = NULL; + + for (port = 0; port <= 1; ++port) { + ide_pci_enablebit_t *e = &(d->enablebits[port]); + if (e->reg) { + if (pcibios_read_config_byte(bus, fn, e->reg, &tmp)) { + printk("%s: unable to read pci reg 0x%x\n", d->name, e->reg); + } else if ((tmp & e->mask) != e->val) + continue; /* port not enabled */ + } + if (pcibios_read_config_word(bus, fn, 0x14+(port*8), &ctl)) + ctl = 0; + if ((ctl &= 0xfffc) == 0) + ctl = 0x3f4 ^ (port << 7); + if (pcibios_read_config_word(bus, fn, 0x10+(port*8), &base)) + base = 0; + if ((base &= 0xfff8) == 0) + base = 0x1F0 ^ (port << 7); + if ((hwif = ide_match_hwif(base)) == NULL) { + printk("%s: no room in hwif table for port %d\n", d->name, port); + continue; + } + hwif->chipset = ide_pci; + hwif->pci_port = port; + if (mate) { + hwif->mate = mate; + mate->mate = hwif; + } + mate = hwif; /* for next iteration */ + if (hwif->io_ports[IDE_DATA_OFFSET] != base) { + ide_init_hwif_ports(hwif->io_ports, base, NULL); + hwif->io_ports[IDE_CONTROL_OFFSET] = ctl + 2; + } + if (bmiba) { + if ((inb(bmiba+2) & 0x80)) { /* simplex DMA only? */ + printk("%s: simplex device: DMA disabled\n", d->name); + } else { /* supports simultaneous DMA on both channels */ + ide_setup_dma(hwif, bmiba + (8 * port), 8); + } + } + if (d->id) { /* For "known" chipsets, allow other irqs during i/o */ + hwif->drives[0].unmask = 1; + hwif->drives[1].unmask = 1; + } + if (d->init_hwif) /* Call chipset-specific routine for each enabled hwif */ + d->init_hwif(bus, fn, hwif); + at_least_one_hwif_enabled = 1; + } + if (!at_least_one_hwif_enabled) + printk("%s: neither IDE port enabled (BIOS)\n", d->name); +} + +/* + * ide_scan_pci_device() examines all functions of a PCI device, + * looking for IDE interfaces and/or devices in ide_pci_chipsets[]. + */ +__initfunc(static inline void ide_scan_pci_device (unsigned int bus, unsigned int fn)) +{ + unsigned int devid, ccode; + unsigned short pcicmd, class; + ide_pci_device_t *d; + byte hedt, progif; + + if (pcibios_read_config_byte(bus, fn, 0x0e, &hedt)) + hedt = 0; + do { + if (pcibios_read_config_dword(bus, fn, 0x00, &devid) + || devid == 0xffffffff + || pcibios_read_config_dword(bus, fn, 0x08, &ccode)) + return; + d = lookup_devid(devid); + if (d->name == NULL) /* some chips (cmd640, rz1000) are handled elsewhere */ + continue; + progif = (ccode >> 8) & 0xff; + class = ccode >> 16; + if (d->id || class == PCI_CLASS_STORAGE_IDE) { + if (d->id) + printk("%s: IDE device on PCI bus %d function %d\n", d->name, bus, fn); + else + printk("%s: unknown IDE device on PCI bus %d function %d, VID=%04x, DID=%04x\n", + d->name, bus, fn, devid & 0xffff, devid >> 16); + /* + * See if IDE ports are enabled + */ + if (pcibios_read_config_word(bus, fn, 0x04, &pcicmd)) { + printk("%s: error accessing PCICMD\n", d->name); + } else if ((pcicmd & 1) == 0) { + printk("%s: device disabled (BIOS)\n", d->name); + } else { + unsigned int bmiba = 0; + /* + * Check for Bus-Master DMA capability + */ + if (d->id == DEVID_PDC20246 || (class == PCI_CLASS_STORAGE_IDE && (progif & 0x80))) { + if ((!(pcicmd & 4) || !(bmiba = ide_get_or_set_bmiba(bus, fn, d->name)))) { + printk("%s: Bus-Master DMA disabled (BIOS), pcicmd=0x%04x, progif=0x%02x, bmiba=0x%04x\n", d->name, pcicmd, progif, bmiba); + } + } + /* + * Setup the IDE ports + */ + ide_setup_pci_device(bus, fn, bmiba, d); + } + } + } while (hedt == 0x80 && (++fn & 7)); +} + +/* + * ide_scan_pcibus() gets invoked at boot time from ide.c + */ +__initfunc(void ide_scan_pcibus (void)) +{ + unsigned int bus, dev; + + if (!pcibios_present()) + return; + for (bus = 0; bus <= 255; ++bus) { + for (dev = 0; dev <= 31; ++dev) { + ide_scan_pci_device(bus, dev << 3); + } + } +} + diff --git a/drivers/block/pdc4030.c b/drivers/block/pdc4030.c new file mode 100644 index 000000000..c161e58b6 --- /dev/null +++ b/drivers/block/pdc4030.c @@ -0,0 +1,365 @@ +/* -*- linux-c -*- + * linux/drivers/block/pdc4030.c Version 0.08 Nov 30, 1997 + * + * Copyright (C) 1995-1998 Linus Torvalds & authors (see below) + */ + +/* + * Principal Author/Maintainer: peterd@pnd-pc.demon.co.uk + * + * This file provides support for the second port and cache of Promise + * IDE interfaces, e.g. DC4030, DC5030. + * + * Thanks are due to Mark Lord for advice and patiently answering stupid + * questions, and all those mugs^H^H^H^Hbrave souls who've tested this. + * + * Version 0.01 Initial version, #include'd in ide.c rather than + * compiled separately. + * Reads use Promise commands, writes as before. Drives + * on second channel are read-only. + * Version 0.02 Writes working on second channel, reads on both + * channels. Writes fail under high load. Suspect + * transfers of >127 sectors don't work. + * Version 0.03 Brought into line with ide.c version 5.27. + * Other minor changes. + * Version 0.04 Updated for ide.c version 5.30 + * Changed initialization strategy + * Version 0.05 Kernel integration. -ml + * Version 0.06 Ooops. Add hwgroup to direct call of ide_intr() -ml + * Version 0.07 Added support for DC4030 variants + * Secondary interface autodetection + * Version 0.08 Renamed to pdc4030.c + */ + +/* + * Once you've compiled it in, you'll have to also enable the interface + * setup routine from the kernel command line, as in + * + * 'linux ide0=dc4030' + * + * As before, it seems that somewhere around 3Megs when writing, bad things + * start to happen [timeouts/retries -ml]. If anyone can give me more feedback, + * I'd really appreciate it. [email: peterd@pnd-pc.demon.co.uk] + * + */ + + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#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 <asm/io.h> +#include <asm/irq.h> +#include "ide.h" +#include "pdc4030.h" + +/* This is needed as the controller may not interrupt if the required data is +available in the cache. We have to simulate an interrupt. Ugh! */ + +extern void ide_intr(int, void *dev_id, struct pt_regs*); + +/* + * promise_selectproc() is invoked by ide.c + * in preparation for access to the specified drive. + */ +static void promise_selectproc (ide_drive_t *drive) +{ + unsigned int number; + + OUT_BYTE(drive->select.all,IDE_SELECT_REG); + udelay(1); /* paranoia */ + number = ((HWIF(drive)->is_pdc4030_2)<<1) + drive->select.b.unit; + OUT_BYTE(number,IDE_FEATURE_REG); +} + +/* + * pdc4030_cmd handles the set of vendor specific commands that are initiated + * by command F0. They all have the same success/failure notification. + */ +int pdc4030_cmd(ide_drive_t *drive, byte cmd) +{ + unsigned long timeout, timer; + byte status_val; + + promise_selectproc(drive); /* redundant? */ + OUT_BYTE(0xF3,IDE_SECTOR_REG); + OUT_BYTE(cmd,IDE_SELECT_REG); + OUT_BYTE(PROMISE_EXTENDED_COMMAND,IDE_COMMAND_REG); + timeout = HZ * 10; + timeout += jiffies; + do { + if(jiffies > timeout) { + return 2; /* device timed out */ + } + /* This is out of delay_10ms() */ + /* Delays at least 10ms to give interface a chance */ + timer = jiffies + (HZ + 99)/100 + 1; + while (timer > jiffies); + status_val = IN_BYTE(IDE_SECTOR_REG); + } while (status_val != 0x50 && status_val != 0x70); + + if(status_val == 0x50) + return 0; /* device returned success */ + else + return 1; /* device returned failure */ +} + +ide_hwif_t *hwif_required = NULL; + +void setup_pdc4030 (ide_hwif_t *hwif) +{ + hwif_required = hwif; +} + +/* +init_pdc4030: Test for presence of a Promise caching controller card. +Returns: 0 if no Promise card present at this io_base + 1 if Promise card found +*/ +int init_pdc4030 (void) +{ + ide_hwif_t *hwif = hwif_required; + ide_drive_t *drive; + ide_hwif_t *second_hwif; + struct dc_ident ident; + int i; + + if (!hwif) return 0; + + drive = &hwif->drives[0]; + second_hwif = &ide_hwifs[hwif->index+1]; + if(hwif->is_pdc4030_2) /* we've already been found ! */ + return 1; + + if(IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF) + { + return 0; + } + OUT_BYTE(0x08,IDE_CONTROL_REG); + if(pdc4030_cmd(drive,PROMISE_GET_CONFIG)) { + return 0; + } + if(ide_wait_stat(drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) { + printk("%s: Failed Promise read config!\n",hwif->name); + return 0; + } + ide_input_data(drive,&ident,SECTOR_WORDS); + if(ident.id[1] != 'P' || ident.id[0] != 'T') { + return 0; + } + printk("%s: Promise caching controller, ",hwif->name); + switch(ident.type) { + case 0x43: printk("DC4030VL-2, "); break; + case 0x41: printk("DC4030VL-1, "); break; + case 0x40: printk("DC4030VL, "); break; + default: printk("unknown - type 0x%02x - please report!\n" + ,ident.type); + return 0; + } + printk("%dKB cache, ",(int)ident.cache_mem); + switch(ident.irq) { + case 0x00: hwif->irq = 14; break; + case 0x01: hwif->irq = 12; break; + default: hwif->irq = 15; break; + } + printk("on IRQ %d\n",hwif->irq); + hwif->chipset = second_hwif->chipset = ide_pdc4030; + hwif->mate = second_hwif; + second_hwif->mate = hwif; + hwif->selectproc = second_hwif->selectproc = &promise_selectproc; +/* Shift the remaining interfaces down by one */ + for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) { + ide_hwif_t *h = &ide_hwifs[i]; + + printk("Shifting i/f %d values to i/f %d\n",i-1,i); + ide_init_hwif_ports(h->io_ports, (h-1)->io_ports[IDE_DATA_OFFSET], NULL); + h->io_ports[IDE_CONTROL_OFFSET] = (h-1)->io_ports[IDE_CONTROL_OFFSET]; + h->noprobe = (h-1)->noprobe; + } + second_hwif->is_pdc4030_2 = 1; + ide_init_hwif_ports(second_hwif->io_ports, hwif->io_ports[IDE_DATA_OFFSET], NULL); + second_hwif->io_ports[IDE_CONTROL_OFFSET] = hwif->io_ports[IDE_CONTROL_OFFSET]; + second_hwif->irq = hwif->irq; + for (i=0; i<2 ; i++) { + hwif->drives[i].io_32bit = 3; + second_hwif->drives[i].io_32bit = 3; + if(!ident.current_tm[i+2].cyl) second_hwif->drives[i].noprobe=1; + } + return 1; +} + +/* + * promise_read_intr() is the handler for disk read/multread interrupts + */ +static void promise_read_intr (ide_drive_t *drive) +{ + byte stat; + int i; + unsigned int sectors_left, sectors_avail, nsect; + struct request *rq; + + if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) { + ide_error(drive, "promise_read_intr", stat); + return; + } + +read_again: + do { + sectors_left = IN_BYTE(IDE_NSECTOR_REG); + IN_BYTE(IDE_SECTOR_REG); + } while (IN_BYTE(IDE_NSECTOR_REG) != sectors_left); + rq = HWGROUP(drive)->rq; + sectors_avail = rq->nr_sectors - sectors_left; + +read_next: + rq = HWGROUP(drive)->rq; + if ((nsect = rq->current_nr_sectors) > sectors_avail) + nsect = sectors_avail; + sectors_avail -= nsect; + ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS); +#ifdef DEBUG + printk("%s: promise_read: sectors(%ld-%ld), buffer=0x%08lx, " + "remaining=%ld\n", drive->name, rq->sector, rq->sector+nsect-1, + (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect); +#endif + rq->sector += nsect; + rq->buffer += nsect<<9; + rq->errors = 0; + i = (rq->nr_sectors -= nsect); + if ((rq->current_nr_sectors -= nsect) <= 0) + ide_end_request(1, HWGROUP(drive)); + if (i > 0) { + if (sectors_avail) + goto read_next; + stat = GET_STAT(); + if(stat & DRQ_STAT) + goto read_again; + if(stat & BUSY_STAT) { + ide_set_handler (drive, &promise_read_intr, WAIT_CMD); + return; + } + printk("Ah! promise read intr: sectors left !DRQ !BUSY\n"); + ide_error(drive, "promise read intr", stat); + } +} + +/* + * promise_write_pollfunc() is the handler for disk write completion polling. + */ +static void promise_write_pollfunc (ide_drive_t *drive) +{ + int i; + ide_hwgroup_t *hwgroup = HWGROUP(drive); + struct request *rq; + + if (IN_BYTE(IDE_NSECTOR_REG) != 0) { + if (jiffies < hwgroup->poll_timeout) { + ide_set_handler (drive, &promise_write_pollfunc, 1); + return; /* continue polling... */ + } + printk("%s: write timed-out!\n",drive->name); + ide_error (drive, "write timeout", GET_STAT()); + return; + } + + ide_multwrite(drive, 4); + rq = hwgroup->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, hwgroup); + } + return; +} + +/* + * promise_write() transfers a block of one or more sectors of data to a + * drive as part of a disk write operation. All but 4 sectors are transfered + * in the first attempt, then the interface is polled (nicely!) for completion + * before the final 4 sectors are transfered. Don't ask me why, but this is + * how it's done in the drivers for other O/Ses. There is no interrupt + * generated on writes, which is why we have to do it like this. + */ +static void promise_write (ide_drive_t *drive) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + struct request *rq = &hwgroup->wrq; + int i; + + if (rq->nr_sectors > 4) { + ide_multwrite(drive, rq->nr_sectors - 4); + hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; + ide_set_handler (drive, &promise_write_pollfunc, 1); + return; + } else { + ide_multwrite(drive, rq->nr_sectors); + rq = hwgroup->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, hwgroup); + } + } +} + +/* + * do_pdc4030_io() is called from do_rw_disk, having had the block number + * already set up. It issues a READ or WRITE command to the Promise + * controller, assuming LBA has been used to set up the block number. + */ +void do_pdc4030_io (ide_drive_t *drive, struct request *rq) +{ + unsigned long timeout; + byte stat; + + if (rq->cmd == READ) { + ide_set_handler(drive, &promise_read_intr, WAIT_CMD); + OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG); +/* The card's behaviour is odd at this point. If the data is + available, DRQ will be true, and no interrupt will be + generated by the card. If this is the case, we need to simulate + an interrupt. Ugh! Otherwise, if an interrupt will occur, bit0 + of the SELECT register will be high, so we can just return and + be interrupted.*/ + timeout = jiffies + HZ/20; /* 50ms wait */ + do { + stat=GET_STAT(); + if(stat & DRQ_STAT) { +/* unsigned long flags; + save_flags(flags); + cli(); + disable_irq(HWIF(drive)->irq); +*/ + ide_intr(HWIF(drive)->irq,HWGROUP(drive),NULL); +/* enable_irq(HWIF(drive)->irq); + restore_flags(flags); +*/ + return; + } + if(IN_BYTE(IDE_SELECT_REG) & 0x01) + return; + udelay(1); + } while (jiffies < timeout); + printk("%s: reading: No DRQ and not waiting - Odd!\n", + drive->name); + return; + } + if (rq->cmd == WRITE) { + OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG); + if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { + printk("%s: no DRQ after issuing PROMISE_WRITE\n", drive->name); + return; + } + if (!drive->unmask) + cli(); + HWGROUP(drive)->wrq = *rq; /* scratchpad */ + promise_write(drive); + return; + } + printk("%s: bad command: %d\n", drive->name, rq->cmd); + ide_end_request(0, HWGROUP(drive)); +} diff --git a/drivers/block/pdc4030.h b/drivers/block/pdc4030.h new file mode 100644 index 000000000..9f08da5aa --- /dev/null +++ b/drivers/block/pdc4030.h @@ -0,0 +1,44 @@ +/* + * linux/drivers/block/pdc4030.h + * + * Copyright (C) 1995-1998 Linus Torvalds & authors + */ + +/* + * Principal author: Peter Denison <peterd@pnd-pc.demon.co.uk> + */ + +#ifndef IDE_PROMISE_H +#define IDE_PROMISE_H + +#define PROMISE_EXTENDED_COMMAND 0xF0 +#define PROMISE_READ 0xF2 +#define PROMISE_WRITE 0xF3 +/* Extended commands - main command code = 0xf0 */ +#define PROMISE_GET_CONFIG 0x10 +#define PROMISE_IDENTIFY 0x20 + +struct translation_mode { + u16 cyl; + u8 head; + u8 sect; +}; + +struct dc_ident { + u8 type; + u8 unknown1; + u8 hw_revision; + u8 firmware_major; + u8 firmware_minor; + u8 bios_address; + u8 irq; + u8 unknown2; + u16 cache_mem; + u16 unknown3; + u8 id[2]; + u16 info; + struct translation_mode current_tm[4]; + u8 pad[SECTOR_WORDS*4 - 32]; +}; + +#endif IDE_PROMISE_H diff --git a/drivers/block/promise.c b/drivers/block/promise.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/block/promise.c +++ /dev/null diff --git a/drivers/block/promise.h b/drivers/block/promise.h deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/block/promise.h +++ /dev/null diff --git a/drivers/block/triton.c b/drivers/block/triton.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/block/triton.c +++ /dev/null diff --git a/drivers/block/trm290.c b/drivers/block/trm290.c new file mode 100644 index 000000000..7b8fb308c --- /dev/null +++ b/drivers/block/trm290.c @@ -0,0 +1,227 @@ +/* + * linux/drivers/block/trm290.c Version 1.00 December 3, 1997 + * + * Copyright (c) 1997-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 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: + * + * 0x3df2 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) + * + * 0x3df2 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 + * + * 0x3df3 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) + * + * 0x3df1 "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 + * + * 0x3df0 "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/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 <asm/io.h> + +#include "ide.h" + +static void select_dma_or_pio(ide_hwif_t *hwif, int dma) +{ + static int previous[2] = {-1,-1}; + unsigned long flags; + + if (previous[hwif->pci_port] != dma) { + unsigned short cfg1 = dma ? 0xa3 : 0x21; + previous[hwif->pci_port] = dma; + save_flags(flags); + cli(); + outb(0x51|(hwif->pci_port<<3),0x3df1); + outw(cfg1,0x3df0); + restore_flags(flags); + } +} + +/* + * trm290_dma_intr() is the handler for trm290 disk read/write DMA interrupts + */ +static void trm290_dma_intr (ide_drive_t *drive) +{ + byte stat; + int i; + struct request *rq = HWGROUP(drive)->rq; + + stat = GET_STAT(); /* get drive status */ + if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { + unsigned short dma_stat = inw(HWIF(drive)->dma_base + 2); + if (dma_stat == 0x00ff) { + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, HWGROUP(drive)); + } + return; + } + printk("%s: bad trm290 DMA status: 0x%04x\n", drive->name, dma_stat); + } + sti(); + ide_error(drive, "dma_intr", stat); +} + +static int trm290_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int count, reading = 1 << 1; + + if (drive->media == ide_disk) { + switch (func) { + case ide_dma_write: + reading = 0; +#ifdef TRM290_NO_DMA_WRITES + break; /* always use PIO for writes */ +#endif + case ide_dma_read: + if (!(count = ide_build_dmatable(drive))) + break; /* try PIO instead of DMA */ + select_dma_or_pio(hwif, 1); /* select DMA mode */ + outl_p(virt_to_bus(hwif->dmatable)|reading, hwif->dma_base); + outw((count * 2) - 1, hwif->dma_base+2); /* start DMA */ + ide_set_handler(drive, &trm290_dma_intr, WAIT_CMD); + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); + return 0; + default: + return ide_dmaproc(func, drive); + } + } + select_dma_or_pio(hwif, 0); /* force PIO mode for this operation */ + return 1; +} + +static void trm290_selectproc (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + + select_dma_or_pio(hwif, drive->using_dma); + OUT_BYTE(drive->select.all, hwif->io_ports[IDE_SELECT_OFFSET]); +} + +/* + * Invoked from ide-dma.c at boot time. + */ +__initfunc(void ide_init_trm290 (byte bus, byte fn, ide_hwif_t *hwif)) +{ + hwif->chipset = ide_trm290; + hwif->selectproc = trm290_selectproc; + hwif->drives[0].autotune = 2; /* play it safe */ + hwif->drives[1].autotune = 2; /* play it safe */ + ide_setup_dma(hwif, hwif->pci_port ? 0x3d74 : 0x3df4, 2); + hwif->dmaproc = &trm290_dmaproc; +} |