07_libata_convert-ahci-to-new-eh.patch
This patch converts ahci driver to use new NCQ helpers.
Signed-off-by: Tejun Heo <[EMAIL PROTECTED]>
ahci.c | 363 ++++++++++-------------------------------------------------------
1 files changed, 58 insertions(+), 305 deletions(-)
Index: work/drivers/scsi/ahci.c
===================================================================
--- work.orig/drivers/scsi/ahci.c 2005-07-07 22:08:34.000000000 +0900
+++ work/drivers/scsi/ahci.c 2005-07-07 22:08:36.000000000 +0900
@@ -171,7 +171,6 @@ struct ahci_port_priv {
dma_addr_t cmd_tbl_dma;
void *rx_fis;
dma_addr_t rx_fis_dma;
- u32 sactive;
};
static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg);
@@ -181,7 +180,7 @@ static int ahci_qc_issue(struct ata_queu
static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs
*regs);
static void ahci_phy_reset(struct ata_port *ap);
static void ahci_irq_clear(struct ata_port *ap);
-static void ahci_eng_timeout(struct ata_port *ap);
+static void ahci_error_handler(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_host_stop(struct ata_host_set *host_set);
@@ -227,7 +226,7 @@ static struct ata_port_operations ahci_o
.qc_prep = ahci_qc_prep,
.qc_issue = ahci_qc_issue,
- .eng_timeout = ahci_eng_timeout,
+ .error_handler = ahci_error_handler,
.irq_handler = ahci_interrupt,
.irq_clear = ahci_irq_clear,
@@ -427,6 +426,47 @@ static void ahci_scr_write (struct ata_p
writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4));
}
+static void ahci_stop_dma(struct ata_port *ap)
+{
+ void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+ u32 tmp;
+ int work;
+
+ /* stop DMA */
+ tmp = readl(port_mmio + PORT_CMD);
+ tmp &= ~PORT_CMD_START;
+ writel(tmp, port_mmio + PORT_CMD);
+
+ /* wait for engine to stop. */
+ work = 500;
+ while (work-- > 0) {
+ tmp = readl(port_mmio + PORT_CMD);
+ if ((tmp & PORT_CMD_LIST_ON) == 0)
+ break;
+ msleep(1);
+ }
+}
+
+static void ahci_start_dma(struct ata_port *ap)
+{
+ void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+ u32 tmp;
+
+ /* clear SATA phy error, if any */
+ tmp = readl(port_mmio + PORT_SCR_ERR);
+ writel(tmp, port_mmio + PORT_SCR_ERR);
+
+ /* clear status */
+ tmp = readl(port_mmio + PORT_IRQ_STAT);
+ writel(tmp, port_mmio + PORT_IRQ_STAT);
+
+ /* re-start DMA */
+ tmp = readl(port_mmio + PORT_CMD);
+ tmp |= PORT_CMD_START;
+ writel(tmp, port_mmio + PORT_CMD);
+ readl(port_mmio + PORT_CMD); /* flush */
+}
+
static void ahci_phy_reset(struct ata_port *ap)
{
void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
@@ -549,290 +589,22 @@ static void ahci_qc_prep(struct ata_queu
ahci_fill_sg(qc, offset);
}
-/*
- * Return 1 if COMRESET was done
- */
-static int ahci_intr_error(struct ata_port *ap, u32 irq_stat)
-{
- void *mmio = ap->host_set->mmio_base;
- void *port_mmio = ahci_port_base(mmio, ap->port_no);
- u32 tmp;
- int work, reset = 0;
-
- /* stop DMA */
- tmp = readl(port_mmio + PORT_CMD);
- tmp &= ~PORT_CMD_START;
- writel(tmp, port_mmio + PORT_CMD);
-
- /* wait for engine to stop. TODO: this could be
- * as long as 500 msec
- */
- work = 1000;
- while (work-- > 0) {
- tmp = readl(port_mmio + PORT_CMD);
- if ((tmp & PORT_CMD_LIST_ON) == 0)
- break;
- udelay(10);
- }
-
- /* clear SATA phy error, if any */
- tmp = readl(port_mmio + PORT_SCR_ERR);
- writel(tmp, port_mmio + PORT_SCR_ERR);
-
- /* clear status */
- tmp = readl(port_mmio + PORT_IRQ_STAT);
- writel(tmp, port_mmio + PORT_IRQ_STAT);
-
- /* 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)) {
- printk(KERN_WARNING "ata%u: stat=%x, issuing COMRESET\n",
ap->id, tmp);
- 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 */
- reset = 1;
- }
-
- /* re-start DMA */
- tmp = readl(port_mmio + PORT_CMD);
- tmp |= PORT_CMD_START;
- writel(tmp, port_mmio + PORT_CMD);
- readl(port_mmio + PORT_CMD); /* flush */
-
- printk(KERN_WARNING "ata%u: error occurred, port reset\n", ap->id);
- return reset;
-}
-
-static void ahci_complete_requests(struct ata_port *ap, u32 tag_mask, int err)
-{
- while (tag_mask) {
- struct ata_queued_cmd *qc;
- int tag = ffs(tag_mask) - 1;
-
- tag_mask &= ~(1 << tag);
- qc = ata_qc_from_tag(ap, tag);
- if (qc)
- ata_qc_complete(qc, err);
- else
- printk(KERN_ERR "ahci: missing tag %d\n", tag);
- }
-}
-
-static void dump_log_page(unsigned char *p)
-{
- int i;
-
- printk("LOG 0x10: nq=%d, tag=%d\n", p[0] >> 7, p[0] & 0x1f);
-
- for (i = 2; i < 14; i++)
- printk("%d:%d ", i, p[i]);
-
- printk("\n");
-}
-
-/*
- * TODO: needs to use READ_LOG_EXT/page=10h to retrieve error information
- */
-extern void ata_qc_free(struct ata_queued_cmd *qc);
-static void ahci_ncq_timeout(struct ata_port *ap)
+static void ahci_error_handler(struct ata_port *ap)
{
- struct ahci_port_priv *pp = ap->private_data;
- void *mmio = ap->host_set->mmio_base;
- void *port_mmio = ahci_port_base(mmio, ap->port_no);
- struct ata_queued_cmd *qc;
- unsigned long flags;
- char *buffer;
- u32 sactive;
- int reset;
-
- printk(KERN_WARNING "ata%u: ncq interrupt error (Q=%d)\n", ap->id,
ap->queue_depth);
-
- spin_lock_irqsave(&ap->host_set->lock, flags);
-
- sactive = readl(port_mmio + PORT_SCR_ACT);
-
- printk(KERN_WARNING "ata%u: SActive 0x%x (0x%x)\n", ap->id, sactive,
pp->sactive);
- reset = ahci_intr_error(ap, readl(port_mmio + PORT_IRQ_STAT));
-
- spin_unlock_irqrestore(&ap->host_set->lock, flags);
-
- /*
- * if COMRESET was done, we don't have to issue a log page read
- */
- if (reset)
- goto done;
-
- buffer = kmalloc(512, GFP_KERNEL);
- if (!buffer) {
- printk(KERN_ERR "ata%u: unable to allocate memory for error\n",
ap->id);
- goto done;
- }
-
- if (ata_read_log_page(ap, 0, READ_LOG_SATA_NCQ_PAGE, buffer, 1)) {
- printk(KERN_ERR "ata%u: unable to read log page\n", ap->id);
- goto out;
- }
-
- dump_log_page(buffer);
-
- /*
- * if NQ is cleared, bottom 5 bits contain the tag of the errored
- * command
- */
- if ((buffer[0] & (1 << 7)) == 0) {
- int tag = buffer[0] & 0x1f;
-
- qc = ata_qc_from_tag(ap, tag);
- if (qc)
- ata_qc_complete(qc, ATA_ERR);
- }
-
/*
- * requeue the remaining commands
+ * Bring controller to known state. libata layer will take
+ * care of the drive.
*/
- while (pp->sactive) {
- int tag = ffs(pp->sactive) - 1;
-
- pp->sactive &= ~(1 << tag);
- qc = ata_qc_from_tag(ap, tag);
- if (qc) {
- if (qc->scsicmd)
- ata_qc_free(qc);
- else
- ata_qc_complete(qc, ATA_ERR);
- } else
- printk(KERN_ERR "ata%u: missing tag %d\n", ap->id, tag);
- }
-
-out:
- kfree(buffer);
-done:
- ata_scsi_unblock_requests(ap);
-}
-
-static void ahci_nonncq_timeout(struct ata_port *ap)
-{
- void *mmio = ap->host_set->mmio_base;
- void *port_mmio = ahci_port_base(mmio, ap->port_no);
- struct ata_queued_cmd *qc;
-
- DPRINTK("ENTER\n");
-
- ahci_intr_error(ap, readl(port_mmio + PORT_IRQ_STAT));
-
- qc = ata_qc_from_tag(ap, ap->active_tag);
- if (!qc) {
- printk(KERN_ERR "ata%u: BUG: timeout without command\n",
- ap->id);
- } else {
- /* hack alert! We cannot use the supplied completion
- * function from inside the ->eh_strategy_handler() thread.
- * libata is the only user of ->eh_strategy_handler() in
- * any kernel, so the default scsi_done() assumes it is
- * not being called from the SCSI EH.
- */
- qc->scsidone = scsi_finish_command;
- ata_qc_complete(qc, ATA_ERR);
- }
-}
-
-static void ahci_eng_timeout(struct ata_port *ap)
-{
- struct ahci_port_priv *pp = ap->private_data;
-
- if (pp->sactive)
- ahci_ncq_timeout(ap);
- else
- ahci_nonncq_timeout(ap);
-}
-
-static int ahci_ncq_intr(struct ata_port *ap, u32 status)
-{
- struct ahci_port_priv *pp = ap->private_data;
- void *mmio = ap->host_set->mmio_base;
- void *port_mmio = ahci_port_base(mmio, ap->port_no);
-
- if (!pp->sactive)
- return 0;
-
- if (status & PORT_IRQ_SDB_FIS) {
- u8 *sdb = pp->rx_fis + RX_FIS_SDB_REG;
- u32 sactive, mask;
-
- if (unlikely(sdb[2] & ATA_ERR)) {
- printk("SDB fis, stat %x, err %x\n", sdb[2], sdb[3]);
- return 1;
- }
-
- /*
- * SActive will have the bits cleared for completed commands
- */
- sactive = readl(port_mmio + PORT_SCR_ACT);
- mask = pp->sactive & ~sactive;
- if (mask) {
- ahci_complete_requests(ap, mask, 0);
- pp->sactive = sactive;
- return 1;
- } else
- printk(KERN_INFO "ata%u: SDB with no bits cleared\n",
ap->id);
- } else if (status & PORT_IRQ_D2H_REG_FIS) {
- u8 *d2h = pp->rx_fis + RX_FIS_D2H_REG;
-
- /*
- * pre-BSY clear error, let timeout error handling take care
- * of it when it kicks in
- */
- if (d2h[2] & ATA_ERR) {
- VPRINTK("D2H fis, err %x\n", d2h[2]);
- return 1;
- }
-
- printk("D2H fis\n");
- } else
- printk(KERN_WARNING "ata%u: unhandled FIS, stat %x\n", ap->id,
status);
-
- return 0;
-}
-
-static void ahci_ncq_intr_error(struct ata_port *ap, u32 status)
-{
- struct ahci_port_priv *pp = ap->private_data;
- struct ata_queued_cmd *qc;
- struct ata_taskfile tf;
- int tag;
-
- printk(KERN_ERR "ata%u: NCQ err status 0x%x\n", ap->id, status);
+ ahci_stop_dma(ap);
+ ahci_start_dma(ap);
- if (status & PORT_IRQ_D2H_REG_FIS) {
- ahci_tf_read(ap, &tf);
- tag = tf.nsect >> 3;
-
- qc = ata_qc_from_tag(ap, tag);
- if (qc) {
- printk(KERN_ERR "ata%u: ending bad tag %d\n", ap->id,
tag);
- pp->sactive &= ~(1 << tag);
- ata_qc_complete(qc, ATA_ERR);
- } else
- printk(KERN_ERR "ata%u: error on tag %d, but not
present\n", ap->id, tag);
- }
-
- /*
- * let command timeout deal with error handling
- */
- ata_scsi_block_requests(ap);
+ ata_ncq_recover(ap, 0);
}
static inline int ahci_host_intr(struct ata_port *ap)
{
- struct ahci_port_priv *pp = ap->private_data;
- void *mmio = ap->host_set->mmio_base;
- void *port_mmio = ahci_port_base(mmio, ap->port_no);
- struct ata_queued_cmd *qc;
- u32 status, serr, ci;
+ void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+ u32 status, serr;
serr = readl(port_mmio + PORT_SCR_ERR);
writel(serr, port_mmio + PORT_SCR_ERR);
@@ -840,29 +612,13 @@ static inline int ahci_host_intr(struct
status = readl(port_mmio + PORT_IRQ_STAT);
writel(status, port_mmio + PORT_IRQ_STAT);
- if (status & PORT_IRQ_FATAL) {
- printk("ata%u: irq error %x %x, tag %d\n", ap->id, serr,
status, ap->active_tag);
- if (pp->sactive)
- ahci_ncq_intr_error(ap, status);
- else
- ahci_intr_error(ap, status);
-
- return 1;
- }
-
- if (ahci_ncq_intr(ap, status))
- return 1;
-
- ci = readl(port_mmio + PORT_CMD_ISSUE);
-
- if ((ci & (1 << ap->active_tag)) == 0) {
- VPRINTK("NON-NCQ interrupt\n");
-
- qc = ata_qc_from_tag(ap, ap->active_tag);
- if (qc && (qc->flags & ATA_QCFLAG_ACTIVE) &&
- !(qc->flags & ATA_QCFLAG_NCQ))
- ata_qc_complete(qc, 0);
- }
+ if (!(status & PORT_IRQ_FATAL)) {
+ void *cmd_issue = port_mmio + PORT_CMD_ISSUE;
+ if (ap->sactive ||
+ (readl(cmd_issue) & (1 << ap->active_tag)) == 0)
+ ata_ncq_complete(ap);
+ } else
+ ata_ncq_error(ap);
return 1;
}
@@ -919,12 +675,9 @@ static irqreturn_t ahci_interrupt (int i
static int ahci_qc_issue(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
- struct ahci_port_priv *pp = ap->private_data;
void *port_mmio = (void *) ap->ioaddr.cmd_addr;
if (qc->flags & ATA_QCFLAG_NCQ) {
- pp->sactive |= (1 << qc->tag);
-
writel(1 << qc->tag, port_mmio + PORT_SCR_ACT);
readl(port_mmio + PORT_SCR_ACT);
}
-
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html