patch-2.4.0-test2 linux/drivers/ide/qd6580.c
Next file: linux/drivers/ide/sis5513.c
Previous file: linux/drivers/ide/piix.c
Back to the patch index
Back to the overall index
- Lines: 468
- Date:
Tue Jun 20 07:52:36 2000
- Orig file:
v2.4.0-test1/linux/drivers/ide/qd6580.c
- Orig date:
Tue May 23 15:31:34 2000
diff -u --recursive --new-file v2.4.0-test1/linux/drivers/ide/qd6580.c linux/drivers/ide/qd6580.c
@@ -1,16 +1,19 @@
/*
- * linux/drivers/ide/qd6580.c Version 0.03 May 13, 2000
+ * linux/drivers/ide/qd6580.c Version 0.04 June 4, 2000
*
* Copyright (C) 1996-2000 Linus Torvalds & author (see below)
*/
/*
* Version 0.03 Cleaned auto-tune, added probe
- *
+ * Version 0.04 Added second channel tuning
+ *
* QDI QD6580 EIDE controller fast support
- *
+ *
* To activate controller support use kernel parameter "ide0=qd6580"
* To enable tuning use kernel parameter "ide0=autotune"
+ * To enable tuning second channel (not really tested),
+ * use parameter "ide1=autotune"
*/
/*
@@ -36,146 +39,254 @@
#include "ide_modes.h"
/*
- * I/O ports are 0xb0 0xb1 0xb2 and 0xb3
- * or 0x30 0x31 0x32 and 0x33
+ * I/O ports are 0xb0-0xb3
+ * or 0x30-0x33
* -- this is a dual IDE interface with I/O chips
*
* More research on qd6580 being done by willmore@cig.mot.com (David)
+ * More Information given by Petr Sourcek (petr@ryston.cz)
+ * http://www.ryston.cz/petr/vlb
*/
-/*
+/*
* 0xb0: Timer1
*
- *
- * 0xb1: Status
*
- * && 0xf0 is either 0b1010000 or 0b01010000, or else it isn't a qd6580
- * bit 3 & 2: unknown (useless ?) I have 0 & 1, respectively
- * bit 1: 1 if qd6580 baseport is 0xb0
- * 0 if qd6580 baseport is 0x30
- * bit 0: 1 if ide baseport is 0x1f0
- * 0 if ide baseport is 0x170
+ * 0xb1: Config
+ *
+ * bit 0: ide baseport: 1 = 0x1f0 ; 0 = 0x170
* (? Strange: the Dos driver uses it, and then forces baseport to 0x1f0 ?)
- *
- *
+ * bit 1: qd baseport: 1 = 0xb0 ; 0 = 0x30
+ * bit 2: ID3: bus speed: 1 = <=33MHz ; 0 = >33MHz
+ * bit 3: 1 for qd6580
+ * upper nibble is either 1010 or 0101, or else it isn't a qd6580
+ *
+ *
* 0xb2: Timer2
*
- *
+ *
* 0xb3: Control
*
- * bits 0-3 are always set 1
- * bit 6 : if 1, must be set 1
- * bit 1 : if 1, bit 7 must be set 1
- * bit 0 : if 1, drives are independant, we can have two different timers for
- * the two drives.
- * if 0, we have to take the slowest drive into account,
- * but we may tune the second hwif ?
+ * 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 (read-ahead and post-write buffer ?)
*/
+/* truncates a in [b,c] */
+#define IDE_IN(a,b,c) ( ((a)<(b)) ? (b) : ( (a)>(c) ? (c) : (a)) )
+
typedef struct ide_hd_timings_s {
int active_time; /* Active pulse (ns) minimum */
int recovery_time; /* Recovery pulse (ns) minimum */
} ide_hd_timings_t;
static int basePort; /* base port address (0x30 or 0xb0) */
-static byte status; /* status register of qd6580 */
+static byte config; /* config register of qd6580 */
static byte control; /* control register of qd6580 */
-/* truncates a in [b,c] */
-#define IDE_IN(a,b,c) ( ((a)<(b)) ? (b) : ( (a)>(c) ? (c) : (a)) )
-
static int bus_clock; /* Vesa local bus clock (ns) */
static int tuned=0; /* to remember whether we've already been tuned */
+static int snd_tuned=0; /* to remember whether we've already been tuned */
+static int nb_disks_prim=0; /* number of disk drives on primary port */
+
+/*
+ * write_reg
+ *
+ * writes the specified byte on the specified register
+ */
+
+static void write_reg ( byte content, byte reg )
+{
+ unsigned long flags;
+
+ save_flags(flags); /* all CPUs */
+ cli(); /* all CPUs */
+ outb_p(content,reg);
+ inb(0x3f6);
+ restore_flags(flags); /* all CPUs */
+}
/*
* tune_drive
*
- * Finds timings for the specified drive, returns it in struc t
+ * Finds timings for the specified drive, returns it in struct t
*/
-static void tune_drive ( ide_drive_t *drive, byte pio, ide_hd_timings_t *t)
+static void tune_drive ( ide_drive_t *drive, byte pio, ide_hd_timings_t *t )
{
ide_pio_data_t d;
- t->active_time = 0xaf;
- t->recovery_time = 0x19f; /* worst cases values from the dos driver */
+ t->active_time = 175;
+ t->recovery_time = 415; /* worst cases values from the dos driver */
- if (drive->present == 0) { /* not present : free to give any timing */
- t->active_time = 0x0;
- t->recovery_time = 0x0;
+ if (!drive->present) { /* not present : free to give any timing */
+ t->active_time = 0;
+ t->recovery_time = 0;
return;
}
-
- pio = ide_get_best_pio_mode(drive, pio, 4, &d);
- if (pio) {
-
- switch (pio) {
- case 0: break;
- case 3: t->active_time = 0x56;
- t->recovery_time = d.cycle_time-0x66;
- break;
- case 4: t->active_time = 0x46;
- t->recovery_time = d.cycle_time-0x3d;
- break;
- default: if (d.cycle_time >= 0xb4) {
- t->active_time = 0x6e;
- t->recovery_time = d.cycle_time - 0x78;
- } else {
- t->active_time = ide_pio_timings[pio].active_time;
- t->recovery_time = d.cycle_time
- -t->active_time
- -ide_pio_timings[pio].setup_time;
- }
- }
- }
+ pio = ide_get_best_pio_mode(drive, pio, 255, &d);
+ pio = IDE_MIN(pio,4);
+
+ switch (pio) {
+ case 0: break;
+ case 3:
+ if (d.cycle_time >= 110) {
+ t->active_time = 86;
+ t->recovery_time = d.cycle_time-102;
+ } else {
+ printk("%s: Strange recovery time !\n",drive->name);
+ return;
+ }
+ break;
+ case 4:
+ if (d.cycle_time >= 69) {
+ t->active_time = 70;
+ t->recovery_time = d.cycle_time-61;
+ } else {
+ printk("%s: Strange recovery time !\n",drive->name);
+ return;
+ }
+ break;
+ default:
+ if (d.cycle_time >= 180) {
+ t->active_time = 110;
+ t->recovery_time = d.cycle_time - 120;
+ } else {
+ t->active_time = ide_pio_timings[pio].active_time;
+ t->recovery_time = d.cycle_time
+ -t->active_time;
+ }
+ }
printk("%s: PIO mode%d, tim1=%dns tim2=%dns\n", drive->name, pio, t->active_time, t->recovery_time);
+
+ if (drive->media == ide_disk)
+ nb_disks_prim++;
+ else {
+/* need to disable read-ahead FIFO and post-write buffer for ATAPI drives*/
+ write_reg(0x5f,basePort+0x03);
+ printk("%s: Warning: please try to connect this drive to secondary IDE port\nto improve data transfer rate on primary IDE port.\n",drive->name);
+ }
}
-/*
+/*
+ * tune_snd_drive
+ *
+ * Finds timings for the specified drive, using second channel rules
+ */
+
+static void tune_snd_drive ( ide_drive_t *drive, byte pio, ide_hd_timings_t *t )
+{
+ ide_pio_data_t d;
+
+ t->active_time = 175;
+ t->recovery_time = 415;
+
+ if (!drive->present) { /* not present : free to give any timing */
+ t->active_time = 0;
+ t->recovery_time = 0;
+ return;
+ }
+
+ pio = ide_get_best_pio_mode(drive, pio, 255, &d);
+
+ if ((pio) && (d.cycle_time >= 180)) {
+ t->active_time = 115;
+ t->recovery_time = d.cycle_time - 115;
+ }
+ printk("%s: PIO mode%d, tim1=%dns tim2=%dns\n", drive->name, pio, t->active_time, t->recovery_time);
+
+ if ((drive->media == ide_disk) && (nb_disks_prim<2)) {
+/* a disk drive on secondary port while there's room on primary, which is the
+ * only one that has read-ahead fifo and post-write buffer ? What a waste !*/
+ printk("%s: Warning: please try to connect this drive to primary IDE port\nto improve data transfer rate.\n",drive->name);
+ }
+}
+
+/*
+ * compute_timing
+ *
+ * computes the timing value where
+ * lower nibble is active time, in count of VLB clocks, 17-(from 2 to 17)
+ * upper nibble is recovery time, in count of VLB clocks, 15-(from 2 to 15)
+ */
+
+static byte compute_timing ( char name[6], ide_hd_timings_t *t )
+{
+ byte active_cycle;
+ byte recovery_cycle;
+ byte parameter;
+
+ active_cycle = 17-IDE_IN(t->active_time / bus_clock + 1, 2, 17);
+ recovery_cycle = 15-IDE_IN(t->recovery_time / bus_clock + 1, 2, 15);
+
+ parameter = active_cycle | (recovery_cycle<<4);
+
+ printk("%s: tim1=%dns tim2=%dns => %#x\n", name, t[0].active_time, t[0].recovery_time, parameter);
+ return(parameter);
+}
+
+/*
* tune_ide
*
- * Tunes the whole ide, ie tunes each drives, and takes the worst timings
- * to tune qd6580
+ * Tunes the whole hwif, ie tunes each drives, and in case we have to share,
+ * takes the worse timings to tune qd6580
*/
static void tune_ide ( ide_hwif_t *hwif, byte pio )
{
unsigned long flags;
ide_hd_timings_t t[2]={{0,0},{0,0}};
-
- byte active_cycle;
- byte recovery_cycle;
- byte parameter;
int bus_speed = ide_system_bus_speed ();
-
+
bus_clock = 1000 / bus_speed;
-
+
save_flags(flags); /* all CPUs */
cli(); /* all CPUs */
outb( (bus_clock<30) ? 0x0 : 0x0a, basePort + 0x02);
outb( 0x40 | ((control & 0x02) ? 0x9f:0x1f), basePort+0x03);
- restore_flags(flags);
+ restore_flags(flags);
tune_drive (&hwif->drives[0], pio, &t[0]);
tune_drive (&hwif->drives[1], pio, &t[1]);
+ if (control & 0x01) { /* only primary port enabled, can tune separately */
+ write_reg(compute_timing (hwif->drives[0].name, &t[0]),basePort);
+ write_reg(compute_timing (hwif->drives[1].name, &t[1]),basePort+0x02);
+ } else { /* both ports enabled, we have to share */
+
+ t[0].active_time = IDE_MAX(t[0].active_time, t[1].active_time);
+ t[0].recovery_time = IDE_MAX(t[0].recovery_time,t[1].recovery_time);
+ write_reg(compute_timing (hwif->name, &t[0]),basePort);
+ }
+}
+
+/*
+ * tune_snd_ide
+ *
+ * Tunes the whole secondary hwif, ie tunes each drives, and takes the worse
+ * timings to tune qd6580
+ */
+
+static void tune_snd_ide ( ide_hwif_t *hwif, byte pio )
+{
+ ide_hd_timings_t t[2]={{0,0},{0,0}};
+
+ tune_snd_drive (&hwif->drives[0], pio, &t[0]);
+ tune_snd_drive (&hwif->drives[1], pio, &t[1]);
+
t[0].active_time = IDE_MAX(t[0].active_time, t[1].active_time);
t[0].recovery_time = IDE_MAX(t[0].recovery_time,t[1].recovery_time);
-
- active_cycle = 17-IDE_IN(t[0].active_time / bus_clock + 1, 2, 17);
- recovery_cycle = 15-IDE_IN(t[0].recovery_time / bus_clock + 1, 2, 15);
-
- parameter=active_cycle | (recovery_cycle<<4);
-
- printk("%s: tim1=%dns tim2=%dns => %#x\n", hwif->name, t[0].active_time, t[0].recovery_time, parameter);
-
- save_flags(flags); /* all CPUs */
- cli(); /* all CPUs */
- outb_p(parameter,0xb0);
- inb(0x3f6);
- restore_flags(flags); /* all CPUs */
-
+
+ write_reg(compute_timing (hwif->name, &t[0]),basePort+0x02);
}
/*
@@ -193,6 +304,20 @@
}
/*
+ * tune_snd_qd6580
+ *
+ * tunes the second hwif if not tuned
+ */
+
+static void tune_snd_qd6580 (ide_drive_t *drive, byte pio)
+{
+ if (! snd_tuned) {
+ tune_snd_ide(HWIF(drive), pio);
+ snd_tuned = 1;
+ }
+}
+
+/*
* testreg
*
* tests if the given port is a register
@@ -203,7 +328,7 @@
byte savereg;
byte readreg;
unsigned long flags;
-
+
save_flags(flags); /* all CPUs */
cli(); /* all CPUs */
savereg = inb(port);
@@ -214,14 +339,15 @@
if (savereg == 0x15) {
printk("Outch ! the probe for qd6580 isn't reliable !\n");
- printk("Please contact samuel.thibault@fnac.net to tell about your hardware\n");
- printk("Assuming qd6580 is present");
+ printk("Please contact maintainers to tell about your hardware\n");
+ printk("Assuming qd6580 is not present.\n");
+ return 0;
}
return (readreg == 0x15);
}
-/*
+/*
* trybase:
*
* tries to find a qd6580 at the given base and save it if found
@@ -233,20 +359,20 @@
save_flags(flags); /* all CPUs */
cli(); /* all CPUs */
- status = inb(base+0x01);
+ config = inb(base+0x01);
control = inb(base+0x03);
restore_flags(flags); /* all CPUs */
- if (((status & 0xf0) != 0x50) && ((status & 0xf0) != 0xa0)) return(0);
- if (! ( ((status & 0x02) == 0x0) == (base == 0x30) ) ) return (0);
+ if (((config & 0xf0) != 0x50) && ((config & 0xf0) != 0xa0)) return(0);
+ if (! ( ((config & 0x02) == 0x0) == (base == 0x30) ) ) return (0);
/* Seems to be OK, let's use it */
-
+
basePort = base;
return(testreg(base));
}
-/*
+/*
* probe:
*
* probes qd6580 at 0xb0 (the default) or 0x30
@@ -257,6 +383,11 @@
return (trybase(0xb0) ? 1 : trybase(0x30));
}
+/*
+ * init_qd6580:
+ *
+ * called at the very beginning of initialization ; should just probe and link
+ */
void __init init_qd6580 (void)
{
@@ -264,13 +395,16 @@
printk("qd6580: not found\n");
return;
}
-
- printk("qd6580: base=%#x, status=%#x, control=%#x\n", basePort, status, control);
-
+
+ printk("qd6580: base=%#x, config=%#x, control=%#x\n", basePort, config, control);
+
ide_hwifs[0].chipset = ide_qd6580;
- ide_hwifs[1].chipset = ide_qd6580;
ide_hwifs[0].tuneproc = &tune_qd6580;
- ide_hwifs[0].mate = &ide_hwifs[1];
- ide_hwifs[1].mate = &ide_hwifs[0];
- ide_hwifs[1].channel = 1;
+ if (!(control & 0x01)) {
+ ide_hwifs[1].chipset = ide_qd6580;
+ ide_hwifs[1].tuneproc = &tune_snd_qd6580;
+ ide_hwifs[0].mate = &ide_hwifs[1];
+ ide_hwifs[1].mate = &ide_hwifs[0];
+ ide_hwifs[1].channel = 1;
+ }
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)