|
@@ -71,6 +71,7 @@ enum {
|
|
|
AHCI_CMD_CLR_BUSY = (1 << 10),
|
|
|
|
|
|
RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */
|
|
|
+ RX_FIS_UNK = 0x60, /* offset of Unknown FIS data */
|
|
|
|
|
|
board_ahci = 0,
|
|
|
board_ahci_vt8251 = 1,
|
|
@@ -128,15 +129,16 @@ enum {
|
|
|
PORT_IRQ_PIOS_FIS = (1 << 1), /* PIO Setup FIS rx'd */
|
|
|
PORT_IRQ_D2H_REG_FIS = (1 << 0), /* D2H Register FIS rx'd */
|
|
|
|
|
|
- PORT_IRQ_FATAL = PORT_IRQ_TF_ERR |
|
|
|
- PORT_IRQ_HBUS_ERR |
|
|
|
- PORT_IRQ_HBUS_DATA_ERR |
|
|
|
- PORT_IRQ_IF_ERR,
|
|
|
- DEF_PORT_IRQ = PORT_IRQ_FATAL | PORT_IRQ_PHYRDY |
|
|
|
- PORT_IRQ_CONNECT | PORT_IRQ_SG_DONE |
|
|
|
- PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_FIS |
|
|
|
- PORT_IRQ_DMAS_FIS | PORT_IRQ_PIOS_FIS |
|
|
|
- PORT_IRQ_D2H_REG_FIS,
|
|
|
+ PORT_IRQ_FREEZE = PORT_IRQ_HBUS_ERR |
|
|
|
+ PORT_IRQ_IF_ERR |
|
|
|
+ PORT_IRQ_CONNECT |
|
|
|
+ PORT_IRQ_UNK_FIS,
|
|
|
+ PORT_IRQ_ERROR = PORT_IRQ_FREEZE |
|
|
|
+ PORT_IRQ_TF_ERR |
|
|
|
+ PORT_IRQ_HBUS_DATA_ERR,
|
|
|
+ DEF_PORT_IRQ = PORT_IRQ_ERROR | PORT_IRQ_SG_DONE |
|
|
|
+ PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS |
|
|
|
+ PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS,
|
|
|
|
|
|
/* PORT_CMD bits */
|
|
|
PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */
|
|
@@ -197,13 +199,15 @@ static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc);
|
|
|
static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs);
|
|
|
static int ahci_probe_reset(struct ata_port *ap, unsigned int *classes);
|
|
|
static void ahci_irq_clear(struct ata_port *ap);
|
|
|
-static void ahci_eng_timeout(struct ata_port *ap);
|
|
|
static int ahci_port_start(struct ata_port *ap);
|
|
|
static void ahci_port_stop(struct ata_port *ap);
|
|
|
static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
|
|
|
static void ahci_qc_prep(struct ata_queued_cmd *qc);
|
|
|
static u8 ahci_check_status(struct ata_port *ap);
|
|
|
-static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
|
|
|
+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_post_internal_cmd(struct ata_queued_cmd *qc);
|
|
|
static void ahci_remove_one (struct pci_dev *pdev);
|
|
|
|
|
|
static struct scsi_host_template ahci_sht = {
|
|
@@ -237,14 +241,18 @@ static const struct ata_port_operations ahci_ops = {
|
|
|
.qc_prep = ahci_qc_prep,
|
|
|
.qc_issue = ahci_qc_issue,
|
|
|
|
|
|
- .eng_timeout = ahci_eng_timeout,
|
|
|
-
|
|
|
.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_error_handler,
|
|
|
+ .post_internal_cmd = ahci_post_internal_cmd,
|
|
|
+
|
|
|
.port_start = ahci_port_start,
|
|
|
.port_stop = ahci_port_stop,
|
|
|
};
|
|
@@ -789,108 +797,97 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc)
|
|
|
ahci_fill_cmd_slot(pp, opts);
|
|
|
}
|
|
|
|
|
|
-static void ahci_restart_port(struct ata_port *ap, u32 irq_stat)
|
|
|
+static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
|
|
|
{
|
|
|
- void __iomem *mmio = ap->host_set->mmio_base;
|
|
|
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
|
|
|
- u32 tmp;
|
|
|
+ struct ahci_port_priv *pp = ap->private_data;
|
|
|
+ struct ata_eh_info *ehi = &ap->eh_info;
|
|
|
+ unsigned int err_mask = 0, action = 0;
|
|
|
+ struct ata_queued_cmd *qc;
|
|
|
+ u32 serror;
|
|
|
|
|
|
- if ((ap->device[0].class != ATA_DEV_ATAPI) ||
|
|
|
- ((irq_stat & PORT_IRQ_TF_ERR) == 0))
|
|
|
- ata_port_printk(ap, KERN_WARNING, "port reset, "
|
|
|
- "p_is %x is %x pis %x cmd %x tf %x ss %x se %x\n",
|
|
|
- irq_stat,
|
|
|
- readl(mmio + HOST_IRQ_STAT),
|
|
|
- readl(port_mmio + PORT_IRQ_STAT),
|
|
|
- readl(port_mmio + PORT_CMD),
|
|
|
- readl(port_mmio + PORT_TFDATA),
|
|
|
- readl(port_mmio + PORT_SCR_STAT),
|
|
|
- readl(port_mmio + PORT_SCR_ERR));
|
|
|
-
|
|
|
- /* stop DMA */
|
|
|
- ahci_stop_engine(ap);
|
|
|
+ ata_ehi_clear_desc(ehi);
|
|
|
|
|
|
- /* clear SATA phy error, if any */
|
|
|
- tmp = readl(port_mmio + PORT_SCR_ERR);
|
|
|
- writel(tmp, port_mmio + PORT_SCR_ERR);
|
|
|
+ /* AHCI needs SError cleared; otherwise, it might lock up */
|
|
|
+ serror = ahci_scr_read(ap, SCR_ERROR);
|
|
|
+ ahci_scr_write(ap, SCR_ERROR, serror);
|
|
|
|
|
|
- /* if DRQ/BSY is set, device needs to be reset.
|
|
|
- * if so, issue COMRESET
|
|
|
- */
|
|
|
- tmp = readl(port_mmio + PORT_TFDATA);
|
|
|
- if (tmp & (ATA_BUSY | ATA_DRQ)) {
|
|
|
- writel(0x301, port_mmio + PORT_SCR_CTL);
|
|
|
- readl(port_mmio + PORT_SCR_CTL); /* flush */
|
|
|
- udelay(10);
|
|
|
- writel(0x300, port_mmio + PORT_SCR_CTL);
|
|
|
- readl(port_mmio + PORT_SCR_CTL); /* flush */
|
|
|
+ /* analyze @irq_stat */
|
|
|
+ ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat);
|
|
|
+
|
|
|
+ if (irq_stat & PORT_IRQ_TF_ERR)
|
|
|
+ err_mask |= AC_ERR_DEV;
|
|
|
+
|
|
|
+ if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) {
|
|
|
+ err_mask |= AC_ERR_HOST_BUS;
|
|
|
+ action |= ATA_EH_SOFTRESET;
|
|
|
}
|
|
|
|
|
|
- /* re-start DMA */
|
|
|
- ahci_start_engine(ap);
|
|
|
-}
|
|
|
+ if (irq_stat & PORT_IRQ_IF_ERR) {
|
|
|
+ err_mask |= AC_ERR_ATA_BUS;
|
|
|
+ action |= ATA_EH_SOFTRESET;
|
|
|
+ ata_ehi_push_desc(ehi, ", interface fatal error");
|
|
|
+ }
|
|
|
|
|
|
-static void ahci_eng_timeout(struct ata_port *ap)
|
|
|
-{
|
|
|
- struct ata_host_set *host_set = ap->host_set;
|
|
|
- void __iomem *mmio = host_set->mmio_base;
|
|
|
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
|
|
|
- struct ata_queued_cmd *qc;
|
|
|
- unsigned long flags;
|
|
|
+ if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) {
|
|
|
+ err_mask |= AC_ERR_ATA_BUS;
|
|
|
+ action |= ATA_EH_SOFTRESET;
|
|
|
+ ata_ehi_push_desc(ehi, ", %s", irq_stat & PORT_IRQ_CONNECT ?
|
|
|
+ "connection status changed" : "PHY RDY changed");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (irq_stat & PORT_IRQ_UNK_FIS) {
|
|
|
+ u32 *unk = (u32 *)(pp->rx_fis + RX_FIS_UNK);
|
|
|
|
|
|
- ata_port_printk(ap, KERN_WARNING, "handling error/timeout\n");
|
|
|
+ err_mask |= AC_ERR_HSM;
|
|
|
+ action |= ATA_EH_SOFTRESET;
|
|
|
+ ata_ehi_push_desc(ehi, ", unknown FIS %08x %08x %08x %08x",
|
|
|
+ unk[0], unk[1], unk[2], unk[3]);
|
|
|
+ }
|
|
|
|
|
|
- spin_lock_irqsave(&host_set->lock, flags);
|
|
|
+ /* okay, let's hand over to EH */
|
|
|
+ ehi->serror |= serror;
|
|
|
+ ehi->action |= action;
|
|
|
|
|
|
- ahci_restart_port(ap, readl(port_mmio + PORT_IRQ_STAT));
|
|
|
qc = ata_qc_from_tag(ap, ap->active_tag);
|
|
|
- qc->err_mask |= AC_ERR_TIMEOUT;
|
|
|
-
|
|
|
- spin_unlock_irqrestore(&host_set->lock, flags);
|
|
|
+ if (qc)
|
|
|
+ qc->err_mask |= err_mask;
|
|
|
+ else
|
|
|
+ ehi->err_mask |= err_mask;
|
|
|
|
|
|
- ata_eh_qc_complete(qc);
|
|
|
+ if (irq_stat & PORT_IRQ_FREEZE)
|
|
|
+ ata_port_freeze(ap);
|
|
|
+ else
|
|
|
+ ata_port_abort(ap);
|
|
|
}
|
|
|
|
|
|
-static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
|
|
|
+static void ahci_host_intr(struct ata_port *ap)
|
|
|
{
|
|
|
void __iomem *mmio = ap->host_set->mmio_base;
|
|
|
void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
|
|
|
- u32 status, serr, ci;
|
|
|
-
|
|
|
- serr = readl(port_mmio + PORT_SCR_ERR);
|
|
|
- writel(serr, port_mmio + PORT_SCR_ERR);
|
|
|
+ struct ata_queued_cmd *qc;
|
|
|
+ u32 status, ci;
|
|
|
|
|
|
status = readl(port_mmio + PORT_IRQ_STAT);
|
|
|
writel(status, port_mmio + PORT_IRQ_STAT);
|
|
|
|
|
|
- ci = readl(port_mmio + PORT_CMD_ISSUE);
|
|
|
- if (likely((ci & 0x1) == 0)) {
|
|
|
- if (qc) {
|
|
|
- WARN_ON(qc->err_mask);
|
|
|
- ata_qc_complete(qc);
|
|
|
- qc = NULL;
|
|
|
- }
|
|
|
+ if (unlikely(status & PORT_IRQ_ERROR)) {
|
|
|
+ ahci_error_intr(ap, status);
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- if (status & PORT_IRQ_FATAL) {
|
|
|
- unsigned int err_mask;
|
|
|
- if (status & PORT_IRQ_TF_ERR)
|
|
|
- err_mask = AC_ERR_DEV;
|
|
|
- else if (status & PORT_IRQ_IF_ERR)
|
|
|
- err_mask = AC_ERR_ATA_BUS;
|
|
|
- else
|
|
|
- err_mask = AC_ERR_HOST_BUS;
|
|
|
-
|
|
|
- /* command processing has stopped due to error; restart */
|
|
|
- ahci_restart_port(ap, status);
|
|
|
-
|
|
|
- if (qc) {
|
|
|
- qc->err_mask |= err_mask;
|
|
|
+ if ((qc = ata_qc_from_tag(ap, ap->active_tag))) {
|
|
|
+ ci = readl(port_mmio + PORT_CMD_ISSUE);
|
|
|
+ if ((ci & 0x1) == 0) {
|
|
|
ata_qc_complete(qc);
|
|
|
+ return;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return 1;
|
|
|
+ /* spurious interrupt */
|
|
|
+ if (ata_ratelimit())
|
|
|
+ ata_port_printk(ap, KERN_INFO, "spurious interrupt "
|
|
|
+ "(irq_stat 0x%x active_tag %d)\n",
|
|
|
+ status, ap->active_tag);
|
|
|
}
|
|
|
|
|
|
static void ahci_irq_clear(struct ata_port *ap)
|
|
@@ -927,14 +924,7 @@ static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *
|
|
|
|
|
|
ap = host_set->ports[i];
|
|
|
if (ap) {
|
|
|
- struct ata_queued_cmd *qc;
|
|
|
- qc = ata_qc_from_tag(ap, ap->active_tag);
|
|
|
- if (!ahci_host_intr(ap, qc))
|
|
|
- if (ata_ratelimit())
|
|
|
- dev_printk(KERN_WARNING, host_set->dev,
|
|
|
- "unhandled interrupt on port %u\n",
|
|
|
- i);
|
|
|
-
|
|
|
+ ahci_host_intr(ap);
|
|
|
VPRINTK("port %u\n", i);
|
|
|
} else {
|
|
|
VPRINTK("port %u (no irq)\n", i);
|
|
@@ -951,7 +941,7 @@ static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *
|
|
|
handled = 1;
|
|
|
}
|
|
|
|
|
|
- spin_unlock(&host_set->lock);
|
|
|
+ spin_unlock(&host_set->lock);
|
|
|
|
|
|
VPRINTK("EXIT\n");
|
|
|
|
|
@@ -969,6 +959,56 @@ static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void ahci_freeze(struct ata_port *ap)
|
|
|
+{
|
|
|
+ void __iomem *mmio = ap->host_set->mmio_base;
|
|
|
+ void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
|
|
|
+
|
|
|
+ /* turn IRQ off */
|
|
|
+ writel(0, port_mmio + PORT_IRQ_MASK);
|
|
|
+}
|
|
|
+
|
|
|
+static void ahci_thaw(struct ata_port *ap)
|
|
|
+{
|
|
|
+ void __iomem *mmio = ap->host_set->mmio_base;
|
|
|
+ void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
|
|
|
+ u32 tmp;
|
|
|
+
|
|
|
+ /* clear IRQ */
|
|
|
+ tmp = readl(port_mmio + PORT_IRQ_STAT);
|
|
|
+ writel(tmp, port_mmio + PORT_IRQ_STAT);
|
|
|
+ writel(1 << ap->id, mmio + HOST_IRQ_STAT);
|
|
|
+
|
|
|
+ /* turn IRQ back on */
|
|
|
+ writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);
|
|
|
+}
|
|
|
+
|
|
|
+static void ahci_error_handler(struct ata_port *ap)
|
|
|
+{
|
|
|
+ if (!(ap->flags & ATA_FLAG_FROZEN)) {
|
|
|
+ /* restart engine */
|
|
|
+ ahci_stop_engine(ap);
|
|
|
+ ahci_start_engine(ap);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* perform recovery */
|
|
|
+ ata_do_eh(ap, ahci_softreset, ahci_hardreset, ahci_postreset);
|
|
|
+}
|
|
|
+
|
|
|
+static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
|
|
|
+{
|
|
|
+ struct ata_port *ap = qc->ap;
|
|
|
+
|
|
|
+ if (qc->flags & ATA_QCFLAG_FAILED)
|
|
|
+ qc->err_mask |= AC_ERR_OTHER;
|
|
|
+
|
|
|
+ if (qc->err_mask) {
|
|
|
+ /* make DMA engine forget about the failed command */
|
|
|
+ ahci_stop_engine(ap);
|
|
|
+ ahci_start_engine(ap);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void ahci_setup_port(struct ata_ioports *port, unsigned long base,
|
|
|
unsigned int port_idx)
|
|
|
{
|
|
@@ -1113,9 +1153,6 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
|
|
|
writel(tmp, port_mmio + PORT_IRQ_STAT);
|
|
|
|
|
|
writel(1 << i, mmio + HOST_IRQ_STAT);
|
|
|
-
|
|
|
- /* set irq mask (enables interrupts) */
|
|
|
- writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);
|
|
|
}
|
|
|
|
|
|
tmp = readl(mmio + HOST_CTL);
|