board_ahci = 0,
board_ahci_vt8251 = 1,
+ board_ahci_ign_iferr = 2,
/* global controller registers */
HOST_CAP = 0x00, /* host capabilities */
AHCI_FLAG_MSI = (1 << 0),
/* ap->flags bits */
- AHCI_FLAG_RESET_NEEDS_CLO = (1 << 24),
- AHCI_FLAG_NO_NCQ = (1 << 25),
+ AHCI_FLAG_NO_NCQ = (1 << 24),
+ AHCI_FLAG_IGN_IRQ_IF_ERR = (1 << 25), /* ignore IRQ_IF_ERR */
};
struct ahci_cmd_hdr {
static void ahci_freeze(struct ata_port *ap);
static void ahci_thaw(struct ata_port *ap);
static void ahci_error_handler(struct ata_port *ap);
+static void ahci_vt8251_error_handler(struct ata_port *ap);
static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg);
static int ahci_port_resume(struct ata_port *ap);
.port_stop = ahci_port_stop,
};
+static const struct ata_port_operations ahci_vt8251_ops = {
+ .port_disable = ata_port_disable,
+
+ .check_status = ahci_check_status,
+ .check_altstatus = ahci_check_status,
+ .dev_select = ata_noop_dev_select,
+
+ .tf_read = ahci_tf_read,
+
+ .qc_prep = ahci_qc_prep,
+ .qc_issue = ahci_qc_issue,
+
+ .irq_handler = ahci_interrupt,
+ .irq_clear = ahci_irq_clear,
+
+ .scr_read = ahci_scr_read,
+ .scr_write = ahci_scr_write,
+
+ .freeze = ahci_freeze,
+ .thaw = ahci_thaw,
+
+ .error_handler = ahci_vt8251_error_handler,
+ .post_internal_cmd = ahci_post_internal_cmd,
+
+ .port_suspend = ahci_port_suspend,
+ .port_resume = ahci_port_resume,
+
+ .port_start = ahci_port_start,
+ .port_stop = ahci_port_stop,
+};
+
static const struct ata_port_info ahci_port_info[] = {
/* board_ahci */
{
.flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
ATA_FLAG_SKIP_D2H_BSY |
- AHCI_FLAG_RESET_NEEDS_CLO | AHCI_FLAG_NO_NCQ,
+ ATA_FLAG_HRST_TO_RESUME | AHCI_FLAG_NO_NCQ,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .udma_mask = 0x7f, /* udma0-6 ; FIXME */
+ .port_ops = &ahci_vt8251_ops,
+ },
+ /* board_ahci_ign_iferr */
+ {
+ .sht = &ahci_sht,
+ .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
+ ATA_FLAG_SKIP_D2H_BSY |
+ AHCI_FLAG_IGN_IRQ_IF_ERR,
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 ; FIXME */
.port_ops = &ahci_ops,
{ PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */
{ PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */
{ PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */
+ { PCI_VDEVICE(INTEL, 0x2922), board_ahci }, /* ICH9 */
+ { PCI_VDEVICE(INTEL, 0x2923), board_ahci }, /* ICH9 */
+ { PCI_VDEVICE(INTEL, 0x2924), board_ahci }, /* ICH9 */
+ { PCI_VDEVICE(INTEL, 0x2925), board_ahci }, /* ICH9 */
+ { PCI_VDEVICE(INTEL, 0x2927), board_ahci }, /* ICH9 */
+ { PCI_VDEVICE(INTEL, 0x2929), board_ahci }, /* ICH9M */
+ { PCI_VDEVICE(INTEL, 0x292a), board_ahci }, /* ICH9M */
+ { PCI_VDEVICE(INTEL, 0x292b), board_ahci }, /* ICH9M */
+ { PCI_VDEVICE(INTEL, 0x292f), board_ahci }, /* ICH9M */
+ { PCI_VDEVICE(INTEL, 0x294d), board_ahci }, /* ICH9 */
+ { PCI_VDEVICE(INTEL, 0x294e), board_ahci }, /* ICH9M */
/* JMicron */
- { PCI_VDEVICE(JMICRON, 0x2360), board_ahci }, /* JMicron JMB360 */
- { PCI_VDEVICE(JMICRON, 0x2361), board_ahci }, /* JMicron JMB361 */
- { PCI_VDEVICE(JMICRON, 0x2363), board_ahci }, /* JMicron JMB363 */
- { PCI_VDEVICE(JMICRON, 0x2365), board_ahci }, /* JMicron JMB365 */
- { PCI_VDEVICE(JMICRON, 0x2366), board_ahci }, /* JMicron JMB366 */
+ { PCI_VDEVICE(JMICRON, 0x2360), board_ahci_ign_iferr }, /* JMB360 */
+ { PCI_VDEVICE(JMICRON, 0x2361), board_ahci_ign_iferr }, /* JMB361 */
+ { PCI_VDEVICE(JMICRON, 0x2363), board_ahci_ign_iferr }, /* JMB363 */
+ { PCI_VDEVICE(JMICRON, 0x2365), board_ahci_ign_iferr }, /* JMB365 */
+ { PCI_VDEVICE(JMICRON, 0x2366), board_ahci_ign_iferr }, /* JMB366 */
/* ATI */
{ PCI_VDEVICE(ATI, 0x4380), board_ahci }, /* ATI SB600 non-raid */
{ PCI_VDEVICE(SI, 0x1185), board_ahci }, /* SiS 966 */
{ PCI_VDEVICE(SI, 0x0186), board_ahci }, /* SiS 968 */
+ /* Generic, PCI class code for AHCI */
+ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+ 0x010601, 0xffffff, board_ahci },
+
{ } /* terminate list */
};
};
+static inline int ahci_nr_ports(u32 cap)
+{
+ return (cap & 0x1f) + 1;
+}
+
static inline unsigned long ahci_port_base_ul (unsigned long base, unsigned int port)
{
return base + 0x100 + (port * 0x80);
static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev)
{
- u32 cap_save, tmp;
+ u32 cap_save, impl_save, tmp;
cap_save = readl(mmio + HOST_CAP);
cap_save &= ( (1<<28) | (1<<17) );
cap_save |= (1 << 27);
+ impl_save = readl(mmio + HOST_PORTS_IMPL);
/* global controller reset */
tmp = readl(mmio + HOST_CTL);
return -EIO;
}
+ /* turn on AHCI mode */
writel(HOST_AHCI_EN, mmio + HOST_CTL);
(void) readl(mmio + HOST_CTL); /* flush */
+
+ /* These write-once registers are normally cleared on reset.
+ * Restore BIOS values... which we HOPE were present before
+ * reset.
+ */
+ if (!impl_save) {
+ impl_save = (1 << ahci_nr_ports(cap_save)) - 1;
+ dev_printk(KERN_WARNING, &pdev->dev,
+ "PORTS_IMPL is zero, forcing 0x%x\n", impl_save);
+ }
writel(cap_save, mmio + HOST_CAP);
- writel(0xf, mmio + HOST_PORTS_IMPL);
+ writel(impl_save, mmio + HOST_PORTS_IMPL);
(void) readl(mmio + HOST_PORTS_IMPL); /* flush */
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
return 0;
}
-static int ahci_prereset(struct ata_port *ap)
-{
- if ((ap->flags & AHCI_FLAG_RESET_NEEDS_CLO) &&
- (ata_busy_wait(ap, ATA_BUSY, 1000) & ATA_BUSY)) {
- /* ATA_BUSY hasn't cleared, so send a CLO */
- ahci_clo(ap);
- }
-
- return ata_std_prereset(ap);
-}
-
static int ahci_softreset(struct ata_port *ap, unsigned int *class)
{
struct ahci_port_priv *pp = ap->private_data;
return rc;
}
+static int ahci_vt8251_hardreset(struct ata_port *ap, unsigned int *class)
+{
+ void __iomem *mmio = ap->host->mmio_base;
+ void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+ int rc;
+
+ DPRINTK("ENTER\n");
+
+ ahci_stop_engine(port_mmio);
+
+ rc = sata_port_hardreset(ap, sata_ehc_deb_timing(&ap->eh_context));
+
+ /* vt8251 needs SError cleared for the port to operate */
+ ahci_scr_write(ap, SCR_ERROR, ahci_scr_read(ap, SCR_ERROR));
+
+ ahci_start_engine(port_mmio);
+
+ DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
+
+ /* vt8251 doesn't clear BSY on signature FIS reception,
+ * request follow-up softreset.
+ */
+ return rc ?: -EAGAIN;
+}
+
static void ahci_postreset(struct ata_port *ap, unsigned int *class)
{
void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
/* analyze @irq_stat */
ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat);
+ /* some controllers set IRQ_IF_ERR on device errors, ignore it */
+ if (ap->flags & AHCI_FLAG_IGN_IRQ_IF_ERR)
+ irq_stat &= ~PORT_IRQ_IF_ERR;
+
if (irq_stat & PORT_IRQ_TF_ERR)
err_mask |= AC_ERR_DEV;
}
/* perform recovery */
- ata_do_eh(ap, ahci_prereset, ahci_softreset, ahci_hardreset,
+ ata_do_eh(ap, ata_std_prereset, ahci_softreset, ahci_hardreset,
+ ahci_postreset);
+}
+
+static void ahci_vt8251_error_handler(struct ata_port *ap)
+{
+ void __iomem *mmio = ap->host->mmio_base;
+ void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+
+ if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
+ /* restart engine */
+ ahci_stop_engine(port_mmio);
+ ahci_start_engine(port_mmio);
+ }
+
+ /* perform recovery */
+ ata_do_eh(ap, ata_std_prereset, ahci_softreset, ahci_vt8251_hardreset,
ahci_postreset);
}
hpriv->cap = readl(mmio + HOST_CAP);
hpriv->port_map = readl(mmio + HOST_PORTS_IMPL);
- probe_ent->n_ports = (hpriv->cap & 0x1f) + 1;
+ probe_ent->n_ports = ahci_nr_ports(hpriv->cap);
VPRINTK("cap 0x%x port_map 0x%x n_ports %d\n",
hpriv->cap, hpriv->port_map, probe_ent->n_ports);