]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/scsi/ahci.c
[PATCH] libata: implement and apply ata_eh_qc_complete/retry()
[linux-2.6-omap-h63xx.git] / drivers / scsi / ahci.c
index 83467a05dc8e3e31f150ca2e030746a1e854b5a7..5a6b23009897083cc1c5b690666f051d34f0db47 100644 (file)
@@ -184,7 +184,7 @@ struct ahci_port_priv {
 static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg);
 static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
 static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
-static int ahci_qc_issue(struct ata_queued_cmd *qc);
+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 void ahci_phy_reset(struct ata_port *ap);
 static void ahci_irq_clear(struct ata_port *ap);
@@ -214,7 +214,6 @@ static struct scsi_host_template ahci_sht = {
        .dma_boundary           = AHCI_DMA_BOUNDARY,
        .slave_configure        = ata_scsi_slave_config,
        .bios_param             = ata_std_bios_param,
-       .ordered_flush          = 1,
 };
 
 static const struct ata_port_operations ahci_ops = {
@@ -243,7 +242,7 @@ static const struct ata_port_operations ahci_ops = {
        .port_stop              = ahci_port_stop,
 };
 
-static struct ata_port_info ahci_port_info[] = {
+static const struct ata_port_info ahci_port_info[] = {
        /* board_ahci */
        {
                .sht            = &ahci_sht,
@@ -277,6 +276,16 @@ static const struct pci_device_id ahci_pci_tbl[] = {
          board_ahci }, /* ESB2 */
        { PCI_VENDOR_ID_INTEL, 0x27c6, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
          board_ahci }, /* ICH7-M DH */
+       { PCI_VENDOR_ID_INTEL, 0x2821, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+         board_ahci }, /* ICH8 */
+       { PCI_VENDOR_ID_INTEL, 0x2822, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+         board_ahci }, /* ICH8 */
+       { PCI_VENDOR_ID_INTEL, 0x2824, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+         board_ahci }, /* ICH8 */
+       { PCI_VENDOR_ID_INTEL, 0x2829, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+         board_ahci }, /* ICH8M */
+       { PCI_VENDOR_ID_INTEL, 0x282a, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+         board_ahci }, /* ICH8M */
        { }     /* terminate list */
 };
 
@@ -437,10 +446,61 @@ static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in,
        writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4));
 }
 
-static void ahci_phy_reset(struct ata_port *ap)
+static int ahci_stop_engine(struct ata_port *ap)
+{
+       void __iomem *mmio = ap->host_set->mmio_base;
+       void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+       int work;
+       u32 tmp;
+
+       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)
+                       return 0;
+               udelay(10);
+       }
+
+       return -EIO;
+}
+
+static void ahci_start_engine(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;
+
+       tmp = readl(port_mmio + PORT_CMD);
+       tmp |= PORT_CMD_START;
+       writel(tmp, port_mmio + PORT_CMD);
+       readl(port_mmio + PORT_CMD); /* flush */
+}
+
+static unsigned int ahci_dev_classify(struct ata_port *ap)
 {
        void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
        struct ata_taskfile tf;
+       u32 tmp;
+
+       tmp = readl(port_mmio + PORT_SIG);
+       tf.lbah         = (tmp >> 24)   & 0xff;
+       tf.lbam         = (tmp >> 16)   & 0xff;
+       tf.lbal         = (tmp >> 8)    & 0xff;
+       tf.nsect        = (tmp)         & 0xff;
+
+       return ata_dev_classify(&tf);
+}
+
+static void ahci_phy_reset(struct ata_port *ap)
+{
+       void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
        struct ata_device *dev = &ap->device[0];
        u32 new_tmp, tmp;
 
@@ -449,13 +509,7 @@ static void ahci_phy_reset(struct ata_port *ap)
        if (ap->flags & ATA_FLAG_PORT_DISABLED)
                return;
 
-       tmp = readl(port_mmio + PORT_SIG);
-       tf.lbah         = (tmp >> 24)   & 0xff;
-       tf.lbam         = (tmp >> 16)   & 0xff;
-       tf.lbal         = (tmp >> 8)    & 0xff;
-       tf.nsect        = (tmp)         & 0xff;
-
-       dev->class = ata_dev_classify(&tf);
+       dev->class = ahci_dev_classify(ap);
        if (!ata_dev_present(dev)) {
                ata_port_disable(ap);
                return;
@@ -563,7 +617,6 @@ static void ahci_restart_port(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;
-       int work;
 
        if ((ap->device[0].class != ATA_DEV_ATAPI) ||
            ((irq_stat & PORT_IRQ_TF_ERR) == 0))
@@ -579,20 +632,7 @@ static void ahci_restart_port(struct ata_port *ap, u32 irq_stat)
                        readl(port_mmio + PORT_SCR_ERR));
 
        /* 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);
-       }
+       ahci_stop_engine(ap);
 
        /* clear SATA phy error, if any */
        tmp = readl(port_mmio + PORT_SCR_ERR);
@@ -611,10 +651,7 @@ static void ahci_restart_port(struct ata_port *ap, u32 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 */
+       ahci_start_engine(ap);
 }
 
 static void ahci_eng_timeout(struct ata_port *ap)
@@ -635,18 +672,13 @@ static void ahci_eng_timeout(struct ata_port *ap)
                       ap->id);
        } else {
                ahci_restart_port(ap, readl(port_mmio + PORT_IRQ_STAT));
-
-               /* 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, AC_ERR_OTHER);
+               qc->err_mask |= AC_ERR_TIMEOUT;
        }
 
        spin_unlock_irqrestore(&host_set->lock, flags);
+
+       if (qc)
+               ata_eh_qc_complete(qc);
 }
 
 static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
@@ -664,7 +696,8 @@ static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
        ci = readl(port_mmio + PORT_CMD_ISSUE);
        if (likely((ci & 0x1) == 0)) {
                if (qc) {
-                       ata_qc_complete(qc, 0);
+                       assert(qc->err_mask == 0);
+                       ata_qc_complete(qc);
                        qc = NULL;
                }
        }
@@ -681,8 +714,10 @@ static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
                /* command processing has stopped due to error; restart */
                ahci_restart_port(ap, status);
 
-               if (qc)
-                       ata_qc_complete(qc, err_mask);
+               if (qc) {
+                       qc->err_mask |= err_mask;
+                       ata_qc_complete(qc);
+               }
        }
 
        return 1;
@@ -759,7 +794,7 @@ static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *
        return IRQ_RETVAL(handled);
 }
 
-static int ahci_qc_issue(struct ata_queued_cmd *qc)
+static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
 {
        struct ata_port *ap = qc->ap;
        void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;