#include <asm/io.h>
#define DRV_NAME "sata_sil24"
-#define DRV_VERSION "0.23"
+#define DRV_VERSION "0.24"
/*
* Port request block (PRB) 32 bytes
/* HOST_SLOT_STAT bits */
HOST_SSTAT_ATTN = (1 << 31),
+ /* HOST_CTRL bits */
+ HOST_CTRL_M66EN = (1 << 16), /* M66EN PCI bus signal */
+ HOST_CTRL_TRDY = (1 << 17), /* latched PCI TRDY */
+ HOST_CTRL_STOP = (1 << 18), /* latched PCI STOP */
+ HOST_CTRL_DEVSEL = (1 << 19), /* latched PCI DEVSEL */
+ HOST_CTRL_REQ64 = (1 << 20), /* latched PCI REQ64 */
+
/*
* Port registers
* (8192 bytes @ +0x0000, +0x2000, +0x4000 and +0x6000 @ BAR2)
PORT_IRQ_PWR_CHG = (1 << 3), /* power management change */
PORT_IRQ_PHYRDY_CHG = (1 << 4), /* PHY ready change */
PORT_IRQ_COMWAKE = (1 << 5), /* COMWAKE received */
- PORT_IRQ_UNK_FIS = (1 << 6), /* Unknown FIS received */
- PORT_IRQ_SDB_FIS = (1 << 11), /* SDB FIS received */
+ PORT_IRQ_UNK_FIS = (1 << 6), /* unknown FIS received */
+ PORT_IRQ_DEV_XCHG = (1 << 7), /* device exchanged */
+ PORT_IRQ_8B10B = (1 << 8), /* 8b/10b decode error threshold */
+ PORT_IRQ_CRC = (1 << 9), /* CRC error threshold */
+ PORT_IRQ_HANDSHAKE = (1 << 10), /* handshake error threshold */
+ PORT_IRQ_SDB_NOTIFY = (1 << 11), /* SDB notify received */
/* bits[27:16] are unmasked (raw) */
PORT_IRQ_RAW_SHIFT = 16,
PORT_CERR_CMD_PCIPERR = 27, /* ctrl[15:13] 110 - PCI parity err while fetching PRB */
PORT_CERR_XFR_UNDEF = 32, /* PSD ecode 00 - undefined */
PORT_CERR_XFR_TGTABRT = 33, /* PSD ecode 01 - target abort */
- PORT_CERR_XFR_MSGABRT = 34, /* PSD ecode 10 - master abort */
+ PORT_CERR_XFR_MSTABRT = 34, /* PSD ecode 10 - master abort */
PORT_CERR_XFR_PCIPERR = 35, /* PSD ecode 11 - PCI prity err during transfer */
PORT_CERR_SENDSERVICE = 36, /* FIS received while sending service */
BID_SIL3132 = 1,
BID_SIL3131 = 2,
+ /* host flags */
+ SIL24_COMMON_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA,
+ SIL24_FLAG_PCIX_IRQ_WOC = (1 << 24), /* IRQ loss errata on PCI-X */
+
IRQ_STAT_4PORTS = 0xf,
};
.name = DRV_NAME,
.ioctl = ata_scsi_ioctl,
.queuecommand = ata_scsi_queuecmd,
- .eh_strategy_handler = ata_scsi_error,
.can_queue = ATA_DEF_QUEUE,
.this_id = ATA_SHT_THIS_ID,
.sg_tablesize = LIBATA_MAX_PRD,
/* sil_3124 */
{
.sht = &sil24_sht,
- .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
- SIL24_NPORTS2FLAG(4),
+ .host_flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(4) |
+ SIL24_FLAG_PCIX_IRQ_WOC,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = 0x3f, /* udma0-5 */
.port_ops = &sil24_ops,
},
- /* sil_3132 */
+ /* sil_3132 */
{
.sht = &sil24_sht,
- .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
- SIL24_NPORTS2FLAG(2),
+ .host_flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(2),
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = 0x3f, /* udma0-5 */
/* sil_3131/sil_3531 */
{
.sht = &sil24_sht,
- .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
- SIL24_NPORTS2FLAG(1),
+ .host_flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(1),
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = 0x3f, /* udma0-5 */
*tf = pp->tf;
}
-static int sil24_softreset(struct ata_port *ap, int verbose,
- unsigned int *class)
+static int sil24_init_port(struct ata_port *ap)
+{
+ void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
+ u32 tmp;
+
+ writel(PORT_CS_INIT, port + PORT_CTRL_STAT);
+ ata_wait_register(port + PORT_CTRL_STAT,
+ PORT_CS_INIT, PORT_CS_INIT, 10, 100);
+ tmp = ata_wait_register(port + PORT_CTRL_STAT,
+ PORT_CS_RDY, 0, 10, 100);
+
+ if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY)
+ return -EIO;
+ return 0;
+}
+
+static int sil24_softreset(struct ata_port *ap, unsigned int *class)
{
void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
struct sil24_port_priv *pp = ap->private_data;
struct sil24_prb *prb = &pp->cmd_block[0].ata.prb;
dma_addr_t paddr = pp->cmd_block_dma;
- unsigned long timeout = jiffies + ATA_TMOUT_BOOT * HZ;
- u32 irq_enable, irq_stat;
+ u32 mask, irq_enable, irq_stat;
+ const char *reason;
DPRINTK("ENTER\n");
irq_enable = readl(port + PORT_IRQ_ENABLE_SET);
writel(irq_enable, port + PORT_IRQ_ENABLE_CLR);
+ /* put the port into known state */
+ if (sil24_init_port(ap)) {
+ reason ="port not ready";
+ goto err;
+ }
+
/*
* XXX: Not sure whether the following sleep is needed or not.
* The original driver had it. So....
writel((u32)paddr, port + PORT_CMD_ACTIVATE);
- do {
- irq_stat = readl(port + PORT_IRQ_STAT);
- writel(irq_stat, port + PORT_IRQ_STAT); /* clear irq */
+ mask = (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR) << PORT_IRQ_RAW_SHIFT;
+ irq_stat = ata_wait_register(port + PORT_IRQ_STAT, mask, 0x0,
+ 100, ATA_TMOUT_BOOT / HZ * 1000);
- irq_stat >>= PORT_IRQ_RAW_SHIFT;
- if (irq_stat & (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR))
- break;
-
- msleep(100);
- } while (time_before(jiffies, timeout));
+ writel(irq_stat, port + PORT_IRQ_STAT); /* clear IRQs */
+ irq_stat >>= PORT_IRQ_RAW_SHIFT;
/* restore IRQs */
writel(irq_enable, port + PORT_IRQ_ENABLE_SET);
if (!(irq_stat & PORT_IRQ_COMPLETE)) {
- DPRINTK("EXIT, srst failed\n");
- return -EIO;
+ if (irq_stat & PORT_IRQ_ERROR)
+ reason = "SRST command error";
+ else
+ reason = "timeout";
+ goto err;
}
sil24_update_tf(ap);
out:
DPRINTK("EXIT, class=%u\n", *class);
return 0;
+
+ err:
+ printk(KERN_ERR "ata%u: softreset failed (%s)\n", ap->id, reason);
+ return -EIO;
}
-static int sil24_hardreset(struct ata_port *ap, int verbose,
- unsigned int *class)
+static int sil24_hardreset(struct ata_port *ap, unsigned int *class)
{
unsigned int dummy_class;
/* sil24 doesn't report device signature after hard reset */
- return sata_std_hardreset(ap, verbose, &dummy_class);
+ return sata_std_hardreset(ap, &dummy_class);
}
static int sil24_probe_reset(struct ata_port *ap, unsigned int *classes)
slot_stat = readl(port + PORT_SLOT_STAT);
if (!(slot_stat & HOST_SSTAT_ATTN)) {
struct sil24_port_priv *pp = ap->private_data;
+
+ if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC)
+ writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT);
+
/*
* !HOST_SSAT_ATTN guarantees successful completion,
* so reading back tf registers is unnecessary for
for (i = 0; i < host_set->n_ports; i++)
if (status & (1 << i)) {
struct ata_port *ap = host_set->ports[i];
- if (ap && !(ap->flags & ATA_FLAG_PORT_DISABLED)) {
+ if (ap && !(ap->flags & ATA_FLAG_DISABLED)) {
sil24_host_intr(host_set->ports[i]);
handled++;
} else
void __iomem *host_base = NULL;
void __iomem *port_base = NULL;
int i, rc;
+ u32 tmp;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
/* GPIO off */
writel(0, host_base + HOST_FLASH_CMD);
- /* Mask interrupts during initialization */
+ /* Apply workaround for completion IRQ loss on PCI-X errata */
+ if (probe_ent->host_flags & SIL24_FLAG_PCIX_IRQ_WOC) {
+ tmp = readl(host_base + HOST_CTRL);
+ if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL))
+ dev_printk(KERN_INFO, &pdev->dev,
+ "Applying completion IRQ loss on PCI-X "
+ "errata fix\n");
+ else
+ probe_ent->host_flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;
+ }
+
+ /* clear global reset & mask interrupts during initialization */
writel(0, host_base + HOST_CTRL);
for (i = 0; i < probe_ent->n_ports; i++) {
void __iomem *port = port_base + i * PORT_REGS_SIZE;
unsigned long portu = (unsigned long)port;
- u32 tmp;
- int cnt;
probe_ent->port[i].cmd_addr = portu + PORT_PRB;
probe_ent->port[i].scr_addr = portu + PORT_SCONTROL;
tmp = readl(port + PORT_CTRL_STAT);
if (tmp & PORT_CS_PORT_RST) {
writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
- readl(port + PORT_CTRL_STAT); /* sync */
- for (cnt = 0; cnt < 10; cnt++) {
- msleep(10);
- tmp = readl(port + PORT_CTRL_STAT);
- if (!(tmp & PORT_CS_PORT_RST))
- break;
- }
+ tmp = ata_wait_register(port + PORT_CTRL_STAT,
+ PORT_CS_PORT_RST,
+ PORT_CS_PORT_RST, 10, 100);
if (tmp & PORT_CS_PORT_RST)
dev_printk(KERN_ERR, &pdev->dev,
"failed to clear port RST\n");
}
+ /* Configure IRQ WoC */
+ if (probe_ent->host_flags & SIL24_FLAG_PCIX_IRQ_WOC)
+ writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT);
+ else
+ writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
+
/* Zero error counters. */
writel(0x8000, port + PORT_DECODE_ERR_THRESH);
writel(0x8000, port + PORT_CRC_ERR_THRESH);
/* Configure interrupts */
writel(0xffff, port + PORT_IRQ_ENABLE_CLR);
- writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR | PORT_IRQ_SDB_FIS,
- port + PORT_IRQ_ENABLE_SET);
+ writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR |
+ PORT_IRQ_SDB_NOTIFY, port + PORT_IRQ_ENABLE_SET);
/* Clear interrupts */
writel(0x0fff0fff, port + PORT_IRQ_STAT);