patch-2.3.1 linux/drivers/block/pdc4030.c
Next file: linux/drivers/block/piix.c
Previous file: linux/drivers/block/pdc202xx.c
Back to the patch index
Back to the overall index
- Lines: 485
- Date:
Thu May 13 11:04:54 1999
- Orig file:
v2.3.0/linux/drivers/block/pdc4030.c
- Orig date:
Tue Dec 29 11:24:57 1998
diff -u --recursive --new-file v2.3.0/linux/drivers/block/pdc4030.c linux/drivers/block/pdc4030.c
@@ -1,17 +1,18 @@
/* -*- linux-c -*-
- * linux/drivers/block/pdc4030.c Version 0.08 Nov 30, 1997
+ * linux/drivers/block/pdc4030.c Version 0.10 Jan 25, 1999
*
- * Copyright (C) 1995-1998 Linus Torvalds & authors (see below)
+ * Copyright (C) 1995-1999 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.
+ * IDE interfaces, e.g. DC4030VL, DC4030VL-1 and DC4030VL-2.
*
* 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.
+ * questions, and all those mugs^H^H^H^Hbrave souls who've tested this,
+ * especially Andre Hedrick.
*
* Version 0.01 Initial version, #include'd in ide.c rather than
* compiled separately.
@@ -29,6 +30,9 @@
* Version 0.07 Added support for DC4030 variants
* Secondary interface autodetection
* Version 0.08 Renamed to pdc4030.c
+ * Version 0.09 Obsolete - never released - did manual write request
+ * splitting before max_sectors[major][minor] available.
+ * Version 0.10 Updated for 2.1 series of kernels
*/
/*
@@ -37,12 +41,17 @@
*
* '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]
+ * It should now work as a second controller also ('ide1=dc4030') but only
+ * if you DON'T have BIOS V4.44, which has a bug. If you have this and EPROM
+ * programming facilities, I can tell you what to fix...
*
+ * As of January 1999, Promise Technology Inc. have finally supplied me with
+ * some technical information which has shed a glimmer of light on some of the
+ * problems I was having, especially with writes.
*/
+#define DEBUG_READ
+#define DEBUG_WRITE
#undef REALLY_SLOW_IO /* most systems can safely undef this */
@@ -54,9 +63,11 @@
#include <linux/ioport.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
+#include <linux/ide.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
@@ -124,40 +135,48 @@
{
ide_hwif_t *hwif = hwif_required;
ide_drive_t *drive;
- ide_hwif_t *second_hwif;
+ ide_hwif_t *hwif2;
struct dc_ident ident;
int i;
if (!hwif) return 0;
drive = &hwif->drives[0];
- second_hwif = &ide_hwifs[hwif->index+1];
- if(hwif->chipset == ide_pdc4030) /* we've already been found ! */
- return 1;
-
- if(IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF)
- {
- return 0;
+ hwif2 = &ide_hwifs[hwif->index+1];
+ if (hwif->chipset == ide_pdc4030) /* 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 (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;
+ if (ide_wait_stat(drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) {
+ printk(KERN_INFO
+ "%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;
+ if (ident.id[1] != 'P' || ident.id[0] != 'T') {
+ return 0;
}
- printk("%s: Promise caching controller, ",hwif->name);
+ printk(KERN_INFO "%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"
+ 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);
+ printk("Please e-mail the following data to "
+ "promise@pnd-pc.demon.co.uk along with\n"
+ "a description of your card and drives:\n");
+ for (i=0; i < 0x90; i++) {
+ printk("%02x ", ((unsigned char *)&ident)[i]);
+ if ((i & 0x0f) == 0x0f) printk("\n");
+ }
return 0;
}
printk("%dKB cache, ",(int)ident.cache_mem);
@@ -167,27 +186,35 @@
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;
- second_hwif->channel = 1;
- hwif->selectproc = second_hwif->selectproc = &promise_selectproc;
+ hwif->chipset = hwif2->chipset = ide_pdc4030;
+ hwif->mate = hwif2;
+ hwif2->mate = hwif;
+ hwif2->channel = 1;
+ hwif->selectproc = hwif2->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];
+#ifdef DEBUG
+ printk(KERN_DEBUG "Shifting i/f %d values to i/f %d\n",i-1,i);
+#endif
+ ide_init_hwif_ports(&h->hw, (h-1)->io_ports[IDE_DATA_OFFSET], 0, NULL);
+ memcpy(h->io_ports, h->hw.io_ports, sizeof(h->io_ports));
h->noprobe = (h-1)->noprobe;
}
- 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;
+ ide_init_hwif_ports(&hwif2->hw, hwif->io_ports[IDE_DATA_OFFSET], 0, NULL);
+ memcpy(hwif2->io_ports, hwif->hw.io_ports, sizeof(hwif2->io_ports));
+ hwif2->irq = hwif->irq;
+ hwif2->hw.irq = hwif->hw.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;
+ hwif->drives[i].io_32bit = 3;
+ hwif2->drives[i].io_32bit = 3;
+ hwif->drives[i].keep_settings = 1;
+ hwif2->drives[i].keep_settings = 1;
+ if (!ident.current_tm[i].cyl)
+ hwif->drives[i].noprobe = 1;
+ if (!ident.current_tm[i+2].cyl)
+ hwif2->drives[i].noprobe = 1;
}
return 1;
}
@@ -198,7 +225,7 @@
static void promise_read_intr (ide_drive_t *drive)
{
byte stat;
- int i;
+ int total_remaining;
unsigned int sectors_left, sectors_avail, nsect;
struct request *rq;
@@ -209,99 +236,140 @@
read_again:
do {
- sectors_left = IN_BYTE(IDE_NSECTOR_REG);
- IN_BYTE(IDE_SECTOR_REG);
+ 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;
+ if (!sectors_avail)
+ goto read_again;
read_next:
rq = HWGROUP(drive)->rq;
- if ((nsect = rq->current_nr_sectors) > sectors_avail)
+ nsect = rq->current_nr_sectors;
+ if (nsect > 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);
+#ifdef DEBUG_READ
+ printk(KERN_DEBUG "%s: promise_read: sectors(%ld-%ld), "
+ "buf=0x%08lx, rem=%ld\n", drive->name, rq->sector,
+ rq->sector+nsect-1, (unsigned long) rq->buffer,
+ 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)
+ rq->nr_sectors -= nsect;
+ total_remaining = rq->nr_sectors;
+ if ((rq->current_nr_sectors -= nsect) <= 0) {
ide_end_request(1, HWGROUP(drive));
- if (i > 0) {
+ }
+/*
+ * Now the data has been read in, do the following:
+ *
+ * if there are still sectors left in the request,
+ * if we know there are still sectors available from the interface,
+ * go back and read the next bit of the request.
+ * else if DRQ is asserted, there are more sectors available, so
+ * go back and find out how many, then read them in.
+ * else if BUSY is asserted, we are going to get an interrupt, so
+ * set the handler for the interrupt and just return
+ */
+ if (total_remaining > 0) {
if (sectors_avail)
- goto read_next;
+ 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;
+ if (stat & DRQ_STAT)
+ goto read_again;
+ if (stat & BUSY_STAT) {
+#ifdef DEBUG_READ
+ printk(KERN_DEBUG "%s: promise_read: waiting for"
+ "interrupt\n", drive->name);
+#endif
+ ide_set_handler (drive, &promise_read_intr, WAIT_CMD);
+ return;
}
- printk("Ah! promise read intr: sectors left !DRQ !BUSY\n");
+ printk(KERN_ERR "%s: Eeek! promise_read_intr: sectors left "
+ "!DRQ !BUSY\n", drive->name);
ide_error(drive, "promise read intr", stat);
}
}
/*
+ * promise_write_intr()
+ * This interrupt is called after the particularly odd polling for completion
+ * of the write request, once all the data has been sent.
+ */
+static void promise_write_intr(ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ struct request *rq;
+
+ if (!OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+ ide_error(drive, "promise_write_intr", stat);
+ }
+
+#ifdef DEBUG_WRITE
+ printk(KERN_DEBUG "%s: Write complete - end_request\n", drive->name);
+#endif
+ rq = HWGROUP(drive)->rq;
+ for (i = rq->nr_sectors; i > 0;) {
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, HWGROUP(drive));
+ }
+}
+
+/*
* 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 (time_before(jiffies, HWGROUP(drive)->poll_timeout)) {
+ ide_set_handler (drive, &promise_write_pollfunc, 1);
+ return; /* continue polling... */
+ }
+ printk(KERN_ERR "%s: write timed-out!\n",drive->name);
+ ide_error (drive, "write timeout", GET_STAT());
+ return;
+ }
- if (IN_BYTE(IDE_NSECTOR_REG) != 0) {
- if (time_before(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;
- }
-
+#ifdef DEBUG_WRITE
+ printk(KERN_DEBUG "%s: Doing last 4 sectors\n", drive->name);
+#endif
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;
+ ide_set_handler(drive, &promise_write_intr, WAIT_CMD);
+ 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.
+ * before the final 4 sectors are transfered. The interrupt generated on
+ * writes occurs after this process, which is why I got it wrong for so long!
*/
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);
- }
- }
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq = &hwgroup->wrq;
+
+#ifdef DEBUG_WRITE
+ printk(KERN_DEBUG "%s: promise_write: sectors(%ld-%ld), "
+ "buffer=0x%08lx\n", drive->name, rq->sector,
+ rq->sector + rq->nr_sectors - 1, rq->buffer);
+#endif
+ 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);
+ ide_set_handler(drive, &promise_write_intr, WAIT_CMD);
+ }
}
/*
@@ -315,43 +383,54 @@
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) {
- disable_irq(HWIF(drive)->irq);
- ide_intr(HWIF(drive)->irq,HWGROUP(drive),NULL);
- enable_irq(HWIF(drive)->irq);
- return;
- }
- if(IN_BYTE(IDE_SELECT_REG) & 0x01)
- return;
- udelay(1);
- } while (time_before(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(); /* local CPU only */
- HWGROUP(drive)->wrq = *rq; /* scratchpad */
- promise_write(drive);
- return;
+ 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 call the
+ * "interrupt" handler (promise_read_intr) directly. Otherwise, if
+ * an interrupt is going to occur, bit0 of the SELECT register will
+ * be high, so we can set the handler the just return and be interrupted.
+ * If neither of these is the case, we wait for up to 50ms (badly I'm
+ * afraid!) until one of them is.
+ */
+ timeout = jiffies + HZ/20; /* 50ms wait */
+ do {
+ stat=GET_STAT();
+ if (stat & DRQ_STAT) {
+ udelay(1);
+ promise_read_intr(drive);
+ return;
+ }
+ if (IN_BYTE(IDE_SELECT_REG) & 0x01) {
+#ifdef DEBUG_READ
+ printk(KERN_DEBUG "%s: read: waiting for "
+ "interrupt\n", drive->name);
+#endif
+ ide_set_handler(drive, &promise_read_intr, WAIT_CMD);
+ return;
+ }
+ udelay(1);
+ } while (time_before(jiffies, timeout));
+
+ printk(KERN_ERR "%s: reading: No DRQ and not waiting - Odd!\n",
+ drive->name);
+
+ } else if (rq->cmd == WRITE) {
+ OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG);
+ if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+ printk(KERN_ERR "%s: no DRQ after issuing "
+ "PROMISE_WRITE\n", drive->name);
+ return;
+ }
+ if (!drive->unmask)
+ __cli(); /* local CPU only */
+ HWGROUP(drive)->wrq = *rq; /* scratchpad */
+ promise_write(drive);
+
+ } else {
+ printk("KERN_WARNING %s: bad command: %d\n",
+ drive->name, rq->cmd);
+ ide_end_request(0, HWGROUP(drive));
}
- printk("%s: bad command: %d\n", drive->name, rq->cmd);
- ide_end_request(0, HWGROUP(drive));
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)