Re: SATA exceptions with 2.6.20-rc5
From: Robert Hancock
Date: Sun Jan 21 2007 - 15:01:25 EST
Björn Steinbrink wrote:
All kernels were bad using that approach. So back to square 1. :/
Björn
OK guys, here's a new patch to try against 2.6.20-rc5:
Right now when switching between ADMA mode and legacy mode (i.e. when
going from doing normal DMA reads/writes to doing a FLUSH CACHE) we just
set the ADMA GO register bit appropriately and continue with no delay.
It looks like in some cases the controller doesn't respond to this
immediately, it takes some nanoseconds for the controller's status
registers to reflect the change that was made. It's possible that if we
were trying to issue commands during this time, the controller might not
react properly. This patch adds some code to wait for the status
register to change to the state we asked for before continuing.
--
Robert Hancock Saskatoon, SK, Canada
To email, remove "nospam" from hancockr@xxxxxxxxxxxxx
Home Page: http://www.roberthancock.com/
--- linux-2.6.20-rc5/drivers/ata/sata_nv.c 2007-01-19 19:18:53.000000000 -0600
+++ linux-2.6.20-rc5debug/drivers/ata/sata_nv.c 2007-01-21 13:35:17.000000000 -0600
@@ -509,14 +509,38 @@ static void nv_adma_register_mode(struct
{
void __iomem *mmio = nv_adma_ctl_block(ap);
struct nv_adma_port_priv *pp = ap->private_data;
- u16 tmp;
+ u16 tmp, status;
+ int count = 0;
if (pp->flags & NV_ADMA_PORT_REGISTER_MODE)
return;
+ status = readw(mmio + NV_ADMA_STAT);
+ while(!(status & NV_ADMA_STAT_IDLE) && count < 20) {
+ ndelay(50);
+ status = readw(mmio + NV_ADMA_STAT);
+ count++;
+ }
+ if(count == 20)
+ ata_port_printk(ap, KERN_WARNING,
+ "timeout waiting for ADMA IDLE, stat=0x%hx\n",
+ status);
+
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp & ~NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
+ count = 0;
+ status = readw(mmio + NV_ADMA_STAT);
+ while(!(status & NV_ADMA_STAT_LEGACY) && count < 20) {
+ ndelay(50);
+ status = readw(mmio + NV_ADMA_STAT);
+ count++;
+ }
+ if(count == 20)
+ ata_port_printk(ap, KERN_WARNING,
+ "timeout waiting for ADMA LEGACY, stat=0x%hx\n",
+ status);
+
pp->flags |= NV_ADMA_PORT_REGISTER_MODE;
}
@@ -524,7 +548,8 @@ static void nv_adma_mode(struct ata_port
{
void __iomem *mmio = nv_adma_ctl_block(ap);
struct nv_adma_port_priv *pp = ap->private_data;
- u16 tmp;
+ u16 tmp, status;
+ int count = 0;
if (!(pp->flags & NV_ADMA_PORT_REGISTER_MODE))
return;
@@ -534,6 +559,18 @@ static void nv_adma_mode(struct ata_port
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp | NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
+ status = readw(mmio + NV_ADMA_STAT);
+ while(((status & NV_ADMA_STAT_LEGACY) ||
+ !(status & NV_ADMA_STAT_IDLE)) && count < 20) {
+ ndelay(50);
+ status = readw(mmio + NV_ADMA_STAT);
+ count++;
+ }
+ if(count == 20)
+ ata_port_printk(ap, KERN_WARNING,
+ "timeout waiting for ADMA LEGACY clear and IDLE, stat=0x%hx\n",
+ status);
+
pp->flags &= ~NV_ADMA_PORT_REGISTER_MODE;
}