Subject: Re: wdc driver and very old (40MB conner) disk drive
To: Witold J. Wnuk <witek@wnuk.eu.org>
From: David Laight <dsl@l8s.co.uk>
List: port-i386
Date: 02/17/2002 22:20:23
On Sun, Feb 17, 2002 at 09:37:24PM +0100, Witold J. Wnuk wrote:
> On Sunday 17 February 2002 20:19, you wrote:
> 
> > i found that linux have the same problem, but there is an boot option
> > "hda=slow" that fixes it. linux code fragments for that are below.
> > my question is - how to implement in in NetBSD driver which use bus_*
> > functions.... i tried few times but with no success (same speed) so i must
> > be doing something wrong. i tried replacing bus_*_multi functions with for
> > and non-multi bus function.
> > is another way to do it or maybe it's another thing to do too?
> 
> Linux' sources reveal that inw_p includes additional delay (writing to port 
> 0x80). Adding delay(1) after every IDE data register access may have similiar 
> effect.

I haven't looked at the code in question :-) but it might be suffering
from delayed writes.  Every x86 since the 386, most recent (< 10
year) sparc chips, any PCI bus system, StrongArm etc doesn't do
write (inc IO) cycles (even uncached ones) synchronously.  Basically
the address and data are put through a FIFO, if you need the write to
happen, then you must force the data out of the FIFO.  The only way
that clear ALL the FIFOs is to issue a read cycle that requests the
same address as a queued write.  Depending on the system this MAY
force all previous writes to 'memeory'.

Intel don't document this buffer, ISTR that whether memory writes are
queued in the same Q as IO writes changed at some point!  For safety
you need to flush memory writes and IO writes separately.

Note that problems with buffered writes only tend to show up when
the system bus is busy with DMA transactions - evan a 286 can
break the cycle recovery tine of its interrupt controller (8259A)!

OTOH an ATA driver I wrote a year or two ago (using a recent ATA
spec) does:

    /* issue command */
    *ata->ata_command = cmd;

    /* Wait 400ns for busy to get set */
    drv_usecwait( 1 );

    /* this data must be transferred without waiting for an interrupt */
    if (len1 >>= 1) {
        /* wait for BUSY to go away, DRQ should be set  */
        if (ata_wait( ata->ata_status, 0, ATA_STAT_BUSY, 300 )) {
	    status = *ata->ata_status;
	...

But doesn't have to talk to any 'dodgy' devices.
(The drv_usecwait() loops for (n * 59)/16 counts of the StromgArms
3.6MHz clock here...)

So maybe the delay is in the spec.  Doing a serier of IOW (to a slow
ISA device) is a reasonable way on ensuring your delay isn't cpu
speed dependant.  However you do need to force the writes to happen.

	David