summaryrefslogtreecommitdiffstats
path: root/sys/dev/ata
diff options
context:
space:
mode:
authornwhitehorn <nwhitehorn@FreeBSD.org>2010-09-09 13:17:30 +0000
committernwhitehorn <nwhitehorn@FreeBSD.org>2010-09-09 13:17:30 +0000
commit258c3c91359faddd228bdcc316478d993dbd6aa3 (patch)
tree8d288529801cee47b96deafb5f6690a015ed9f45 /sys/dev/ata
parentbf61bd49f55cf9af9d54bf6c18a64e95eec4b29c (diff)
downloadFreeBSD-src-258c3c91359faddd228bdcc316478d993dbd6aa3.zip
FreeBSD-src-258c3c91359faddd228bdcc316478d993dbd6aa3.tar.gz
Fix a problem where device detection would work unreliably on Serverworks
K2 SATA controllers. The chip's status register must be read first, and as a long, for other registers to be correctly updated after a command, and this includes the command sequence in device detection as well as the previously handled case after interrupts. While here, clean up some previous hacks related to this controller. Reported by: many Reviewed by: mav MFC after: 3 weeks
Diffstat (limited to 'sys/dev/ata')
-rw-r--r--sys/dev/ata/ata-all.h1
-rw-r--r--sys/dev/ata/ata-lowlevel.c10
-rw-r--r--sys/dev/ata/chipsets/ata-serverworks.c43
3 files changed, 34 insertions, 20 deletions
diff --git a/sys/dev/ata/ata-all.h b/sys/dev/ata/ata-all.h
index 6132eab..0f09a1f 100644
--- a/sys/dev/ata/ata-all.h
+++ b/sys/dev/ata/ata-all.h
@@ -566,6 +566,7 @@ struct ata_channel {
#define ATA_SATA 0x80
#define ATA_DMA_BEFORE_CMD 0x100
#define ATA_KNOWN_PRESENCE 0x200
+#define ATA_STATUS_IS_LONG 0x400
int pm_level; /* power management level */
int devices; /* what is present */
diff --git a/sys/dev/ata/ata-lowlevel.c b/sys/dev/ata/ata-lowlevel.c
index 9174823..288fd17 100644
--- a/sys/dev/ata/ata-lowlevel.c
+++ b/sys/dev/ata/ata-lowlevel.c
@@ -516,10 +516,13 @@ ata_generic_reset(device_t dev)
if ((mask & 0x01) && (stat0 & ATA_S_BUSY)) {
ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(ATA_MASTER));
DELAY(10);
+ if (ch->flags & ATA_STATUS_IS_LONG)
+ stat0 = ATA_IDX_INL(ch, ATA_STATUS) & 0xff;
+ else
+ stat0 = ATA_IDX_INB(ch, ATA_STATUS);
err = ATA_IDX_INB(ch, ATA_ERROR);
lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
msb = ATA_IDX_INB(ch, ATA_CYL_MSB);
- stat0 = ATA_IDX_INB(ch, ATA_STATUS);
if (bootverbose)
device_printf(dev,
"stat0=0x%02x err=0x%02x lsb=0x%02x msb=0x%02x\n",
@@ -546,10 +549,13 @@ ata_generic_reset(device_t dev)
!((mask & 0x01) && (stat0 & ATA_S_BUSY))) {
ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(ATA_SLAVE));
DELAY(10);
+ if (ch->flags & ATA_STATUS_IS_LONG)
+ stat1 = ATA_IDX_INL(ch, ATA_STATUS) & 0xff;
+ else
+ stat1 = ATA_IDX_INB(ch, ATA_STATUS);
err = ATA_IDX_INB(ch, ATA_ERROR);
lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
msb = ATA_IDX_INB(ch, ATA_CYL_MSB);
- stat1 = ATA_IDX_INB(ch, ATA_STATUS);
if (bootverbose)
device_printf(dev,
"stat1=0x%02x err=0x%02x lsb=0x%02x msb=0x%02x\n",
diff --git a/sys/dev/ata/chipsets/ata-serverworks.c b/sys/dev/ata/chipsets/ata-serverworks.c
index cd6db3d..1771dc4 100644
--- a/sys/dev/ata/chipsets/ata-serverworks.c
+++ b/sys/dev/ata/chipsets/ata-serverworks.c
@@ -58,9 +58,8 @@ static int ata_serverworks_ch_detach(device_t dev);
static void ata_serverworks_tf_read(struct ata_request *request);
static void ata_serverworks_tf_write(struct ata_request *request);
static int ata_serverworks_setmode(device_t dev, int target, int mode);
-#ifdef __powerpc__
+static void ata_serverworks_sata_reset(device_t dev);
static int ata_serverworks_status(device_t dev);
-#endif
/* misc defines */
#define SWKS_33 0
@@ -101,7 +100,6 @@ ata_serverworks_probe(device_t dev)
return (BUS_PROBE_DEFAULT);
}
-#ifdef __powerpc__
static int
ata_serverworks_status(device_t dev)
{
@@ -123,7 +121,6 @@ ata_serverworks_status(device_t dev)
return ata_pci_status(dev);
}
-#endif
static int
ata_serverworks_chipinit(device_t dev)
@@ -145,6 +142,7 @@ ata_serverworks_chipinit(device_t dev)
ctlr->ch_detach = ata_serverworks_ch_detach;
ctlr->setmode = ata_sata_setmode;
ctlr->getrev = ata_sata_getrev;
+ ctlr->reset = ata_serverworks_sata_reset;
return 0;
}
else if (ctlr->chip->cfg1 == SWKS_33) {
@@ -210,30 +208,20 @@ ata_serverworks_ch_attach(device_t dev)
ch->r_io[ATA_SERROR].offset = ch_offset + 0x44;
ch->r_io[ATA_SCONTROL].offset = ch_offset + 0x48;
- ch->flags |= ATA_NO_SLAVE;
- ch->flags |= ATA_SATA;
+ ch->flags |= ATA_NO_SLAVE | ATA_SATA | ATA_KNOWN_PRESENCE;
ata_pci_hw(dev);
ch->hw.tf_read = ata_serverworks_tf_read;
ch->hw.tf_write = ata_serverworks_tf_write;
-#ifdef __powerpc__
- ch->hw.status = ata_serverworks_status;
-#endif
if (ctlr->chip->chipid == ATA_K2) {
/*
- * The revision 1 K2 SATA controller has interesting bugs. Patch them.
- * These magic numbers regulate interrupt delivery in the first few
- * cases and are pure magic in the last case.
- *
- * Values obtained from the Darwin driver.
+ * Set SICR registers to turn off waiting for a status message
+ * before sending FIS. Values obtained from the Darwin driver.
*/
- ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, 0x04);
- ATA_IDX_OUTL(ch, ATA_SERROR, 0xffffffff);
- ATA_IDX_OUTL(ch, ATA_SCONTROL, 0x00000300);
- ATA_OUTL(ctlr->r_res2, ch_offset + 0x88, 0);
ATA_OUTL(ctlr->r_res2, ch_offset + 0x80,
ATA_INL(ctlr->r_res2, ch_offset + 0x80) & ~0x00040000);
+ ATA_OUTL(ctlr->r_res2, ch_offset + 0x88, 0);
/*
* Some controllers have a bug where they will send the command
@@ -244,6 +232,14 @@ ata_serverworks_ch_attach(device_t dev)
*/
ch->flags |= ATA_DMA_BEFORE_CMD;
+
+ /*
+ * The status register must be read as a long to fill the other
+ * registers.
+ */
+
+ ch->hw.status = ata_serverworks_status;
+ ch->flags |= ATA_STATUS_IS_LONG;
}
/* chip does not reliably do 64K DMA transfers */
@@ -404,4 +400,15 @@ ata_serverworks_setmode(device_t dev, int target, int mode)
return (mode);
}
+static void
+ata_serverworks_sata_reset(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (ata_sata_phy_reset(dev, -1, 1))
+ ata_generic_reset(dev);
+ else
+ ch->devices = 0;
+}
+
ATA_DECLARE_DRIVER(ata_serverworks);
OpenPOWER on IntegriCloud