libata-pmp-prep: implement sata_async_notification()
[safe/jmp/linux-2.6] / drivers / ata / sata_sil24.c
index e65e8d5..b4f81eb 100644 (file)
@@ -30,7 +30,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME       "sata_sil24"
-#define DRV_VERSION    "0.3"
+#define DRV_VERSION    "1.0"
 
 /*
  * Port request block (PRB) 32 bytes
@@ -168,7 +168,7 @@ enum {
 
        DEF_PORT_IRQ            = PORT_IRQ_COMPLETE | PORT_IRQ_ERROR |
                                  PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG |
-                                 PORT_IRQ_UNK_FIS,
+                                 PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_NOTIFY,
 
        /* bits[27:16] are unmasked (raw) */
        PORT_IRQ_RAW_SHIFT      = 16,
@@ -237,7 +237,9 @@ enum {
        /* host flags */
        SIL24_COMMON_FLAGS      = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
                                  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
-                                 ATA_FLAG_NCQ | ATA_FLAG_SKIP_D2H_BSY,
+                                 ATA_FLAG_NCQ | ATA_FLAG_ACPI_SATA |
+                                 ATA_FLAG_AN,
+       SIL24_COMMON_LFLAGS     = ATA_LFLAG_SKIP_D2H_BSY,
        SIL24_FLAG_PCIX_IRQ_WOC = (1 << 24), /* IRQ loss errata on PCI-X */
 
        IRQ_STAT_4PORTS         = 0xf,
@@ -323,15 +325,14 @@ struct sil24_port_priv {
        struct ata_taskfile tf;                 /* Cached taskfile registers */
 };
 
-static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev);
+static void sil24_dev_config(struct ata_device *dev);
 static u8 sil24_check_status(struct ata_port *ap);
-static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg);
-static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val);
+static int sil24_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val);
+static int sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val);
 static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
 static void sil24_qc_prep(struct ata_queued_cmd *qc);
 static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc);
 static void sil24_irq_clear(struct ata_port *ap);
-static irqreturn_t sil24_interrupt(int irq, void *dev_instance);
 static void sil24_freeze(struct ata_port *ap);
 static void sil24_thaw(struct ata_port *ap);
 static void sil24_error_handler(struct ata_port *ap);
@@ -346,6 +347,7 @@ static const struct pci_device_id sil24_pci_tbl[] = {
        { PCI_VDEVICE(CMD, 0x3124), BID_SIL3124 },
        { PCI_VDEVICE(INTEL, 0x3124), BID_SIL3124 },
        { PCI_VDEVICE(CMD, 0x3132), BID_SIL3132 },
+       { PCI_VDEVICE(CMD, 0x0242), BID_SIL3132 },
        { PCI_VDEVICE(CMD, 0x3131), BID_SIL3131 },
        { PCI_VDEVICE(CMD, 0x3531), BID_SIL3131 },
 
@@ -380,13 +382,9 @@ static struct scsi_host_template sil24_sht = {
        .slave_configure        = ata_scsi_slave_config,
        .slave_destroy          = ata_scsi_slave_destroy,
        .bios_param             = ata_std_bios_param,
-       .suspend                = ata_scsi_device_suspend,
-       .resume                 = ata_scsi_device_resume,
 };
 
 static const struct ata_port_operations sil24_ops = {
-       .port_disable           = ata_port_disable,
-
        .dev_config             = sil24_dev_config,
 
        .check_status           = sil24_check_status,
@@ -395,13 +393,11 @@ static const struct ata_port_operations sil24_ops = {
 
        .tf_read                = sil24_tf_read,
 
+       .qc_defer               = ata_std_qc_defer,
        .qc_prep                = sil24_qc_prep,
        .qc_issue               = sil24_qc_issue,
 
-       .irq_handler            = sil24_interrupt,
        .irq_clear              = sil24_irq_clear,
-       .irq_on                 = ata_dummy_irq_on,
-       .irq_ack                = ata_dummy_irq_ack,
 
        .scr_read               = sil24_scr_read,
        .scr_write              = sil24_scr_write,
@@ -421,33 +417,33 @@ static const struct ata_port_operations sil24_ops = {
 #define SIL24_NPORTS2FLAG(nports)      ((((unsigned)(nports) - 1) & 0x3) << 30)
 #define SIL24_FLAG2NPORTS(flag)                ((((flag) >> 30) & 0x3) + 1)
 
-static struct ata_port_info sil24_port_info[] = {
+static const struct ata_port_info sil24_port_info[] = {
        /* sil_3124 */
        {
-               .sht            = &sil24_sht,
                .flags          = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(4) |
                                  SIL24_FLAG_PCIX_IRQ_WOC,
+               .link_flags     = SIL24_COMMON_LFLAGS,
                .pio_mask       = 0x1f,                 /* pio0-4 */
                .mwdma_mask     = 0x07,                 /* mwdma0-2 */
-               .udma_mask      = 0x3f,                 /* udma0-5 */
+               .udma_mask      = ATA_UDMA5,            /* udma0-5 */
                .port_ops       = &sil24_ops,
        },
        /* sil_3132 */
        {
-               .sht            = &sil24_sht,
                .flags          = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(2),
+               .link_flags     = SIL24_COMMON_LFLAGS,
                .pio_mask       = 0x1f,                 /* pio0-4 */
                .mwdma_mask     = 0x07,                 /* mwdma0-2 */
-               .udma_mask      = 0x3f,                 /* udma0-5 */
+               .udma_mask      = ATA_UDMA5,            /* udma0-5 */
                .port_ops       = &sil24_ops,
        },
        /* sil_3131/sil_3531 */
        {
-               .sht            = &sil24_sht,
                .flags          = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(1),
+               .link_flags     = SIL24_COMMON_LFLAGS,
                .pio_mask       = 0x1f,                 /* pio0-4 */
                .mwdma_mask     = 0x07,                 /* mwdma0-2 */
-               .udma_mask      = 0x3f,                 /* udma0-5 */
+               .udma_mask      = ATA_UDMA5,            /* udma0-5 */
                .port_ops       = &sil24_ops,
        },
 };
@@ -459,9 +455,9 @@ static int sil24_tag(int tag)
        return tag;
 }
 
-static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev)
+static void sil24_dev_config(struct ata_device *dev)
 {
-       void __iomem *port = ap->ioaddr.cmd_addr;
+       void __iomem *port = dev->link->ap->ioaddr.cmd_addr;
 
        if (dev->cdb_len == 16)
                writel(PORT_CS_CDB16, port + PORT_CTRL_STAT);
@@ -469,15 +465,15 @@ static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev)
                writel(PORT_CS_CDB16, port + PORT_CTRL_CLR);
 }
 
-static inline void sil24_update_tf(struct ata_port *ap)
+static void sil24_read_tf(struct ata_port *ap, int tag, struct ata_taskfile *tf)
 {
-       struct sil24_port_priv *pp = ap->private_data;
        void __iomem *port = ap->ioaddr.cmd_addr;
-       struct sil24_prb __iomem *prb = port;
+       struct sil24_prb __iomem *prb;
        u8 fis[6 * 4];
 
-       memcpy_fromio(fis, prb->fis, 6 * 4);
-       ata_tf_from_fis(fis, &pp->tf);
+       prb = port + PORT_LRAM + sil24_tag(tag) * PORT_LRAM_SLOT_SZ;
+       memcpy_fromio(fis, prb->fis, sizeof(fis));
+       ata_tf_from_fis(fis, tf);
 }
 
 static u8 sil24_check_status(struct ata_port *ap)
@@ -493,25 +489,30 @@ static int sil24_scr_map[] = {
        [SCR_ACTIVE]    = 3,
 };
 
-static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg)
+static int sil24_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val)
 {
        void __iomem *scr_addr = ap->ioaddr.scr_addr;
+
        if (sc_reg < ARRAY_SIZE(sil24_scr_map)) {
                void __iomem *addr;
                addr = scr_addr + sil24_scr_map[sc_reg] * 4;
-               return readl(scr_addr + sil24_scr_map[sc_reg] * 4);
+               *val = readl(scr_addr + sil24_scr_map[sc_reg] * 4);
+               return 0;
        }
-       return 0xffffffffU;
+       return -EINVAL;
 }
 
-static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val)
+static int sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val)
 {
        void __iomem *scr_addr = ap->ioaddr.scr_addr;
+
        if (sc_reg < ARRAY_SIZE(sil24_scr_map)) {
                void __iomem *addr;
                addr = scr_addr + sil24_scr_map[sc_reg] * 4;
                writel(val, scr_addr + sil24_scr_map[sc_reg] * 4);
+               return 0;
        }
+       return -EINVAL;
 }
 
 static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
@@ -536,18 +537,65 @@ static int sil24_init_port(struct ata_port *ap)
        return 0;
 }
 
-static int sil24_softreset(struct ata_port *ap, unsigned int *class)
+static int sil24_exec_polled_cmd(struct ata_port *ap, int pmp,
+                                const struct ata_taskfile *tf,
+                                int is_cmd, u32 ctrl,
+                                unsigned long timeout_msec)
 {
        void __iomem *port = 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;
-       u32 mask, irq_stat;
+       u32 irq_enabled, irq_mask, irq_stat;
+       int rc;
+
+       prb->ctrl = cpu_to_le16(ctrl);
+       ata_tf_to_fis(tf, pmp, is_cmd, prb->fis);
+
+       /* temporarily plug completion and error interrupts */
+       irq_enabled = readl(port + PORT_IRQ_ENABLE_SET);
+       writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR, port + PORT_IRQ_ENABLE_CLR);
+
+       writel((u32)paddr, port + PORT_CMD_ACTIVATE);
+       writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + 4);
+
+       irq_mask = (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR) << PORT_IRQ_RAW_SHIFT;
+       irq_stat = ata_wait_register(port + PORT_IRQ_STAT, irq_mask, 0x0,
+                                    10, timeout_msec);
+
+       writel(irq_mask, port + PORT_IRQ_STAT); /* clear IRQs */
+       irq_stat >>= PORT_IRQ_RAW_SHIFT;
+
+       if (irq_stat & PORT_IRQ_COMPLETE)
+               rc = 0;
+       else {
+               /* force port into known state */
+               sil24_init_port(ap);
+
+               if (irq_stat & PORT_IRQ_ERROR)
+                       rc = -EIO;
+               else
+                       rc = -EBUSY;
+       }
+
+       /* restore IRQ enabled */
+       writel(irq_enabled, port + PORT_IRQ_ENABLE_SET);
+
+       return rc;
+}
+
+static int sil24_do_softreset(struct ata_link *link, unsigned int *class,
+                             int pmp, unsigned long deadline)
+{
+       struct ata_port *ap = link->ap;
+       unsigned long timeout_msec = 0;
+       struct ata_taskfile tf;
        const char *reason;
+       int rc;
 
        DPRINTK("ENTER\n");
 
-       if (ata_port_offline(ap)) {
+       if (ata_link_offline(link)) {
                DPRINTK("PHY reports no device\n");
                *class = ATA_DEV_NONE;
                goto out;
@@ -560,29 +608,22 @@ static int sil24_softreset(struct ata_port *ap, unsigned int *class)
        }
 
        /* do SRST */
-       prb->ctrl = cpu_to_le16(PRB_CTRL_SRST);
-       prb->fis[1] = 0; /* no PMP yet */
-
-       writel((u32)paddr, port + PORT_CMD_ACTIVATE);
-       writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + 4);
-
-       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);
-
-       writel(irq_stat, port + PORT_IRQ_STAT); /* clear IRQs */
-       irq_stat >>= PORT_IRQ_RAW_SHIFT;
-
-       if (!(irq_stat & PORT_IRQ_COMPLETE)) {
-               if (irq_stat & PORT_IRQ_ERROR)
-                       reason = "SRST command error";
-               else
-                       reason = "timeout";
+       if (time_after(deadline, jiffies))
+               timeout_msec = jiffies_to_msecs(deadline - jiffies);
+
+       ata_tf_init(link->device, &tf); /* doesn't really matter */
+       rc = sil24_exec_polled_cmd(ap, pmp, &tf, 0, PRB_CTRL_SRST,
+                                  timeout_msec);
+       if (rc == -EBUSY) {
+               reason = "timeout";
+               goto err;
+       } else if (rc) {
+               reason = "SRST command error";
                goto err;
        }
 
-       sil24_update_tf(ap);
-       *class = ata_dev_classify(&pp->tf);
+       sil24_read_tf(ap, 0, &tf);
+       *class = ata_dev_classify(&tf);
 
        if (*class == ATA_DEV_UNKNOWN)
                *class = ATA_DEV_NONE;
@@ -592,22 +633,30 @@ static int sil24_softreset(struct ata_port *ap, unsigned int *class)
        return 0;
 
  err:
-       ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason);
+       ata_link_printk(link, KERN_ERR, "softreset failed (%s)\n", reason);
        return -EIO;
 }
 
-static int sil24_hardreset(struct ata_port *ap, unsigned int *class)
+static int sil24_softreset(struct ata_link *link, unsigned int *class,
+                          unsigned long deadline)
 {
+       return sil24_do_softreset(link, class, 0, deadline);
+}
+
+static int sil24_hardreset(struct ata_link *link, unsigned int *class,
+                          unsigned long deadline)
+{
+       struct ata_port *ap = link->ap;
        void __iomem *port = ap->ioaddr.cmd_addr;
        const char *reason;
        int tout_msec, rc;
        u32 tmp;
 
        /* sil24 does the right thing(tm) without any protection */
-       sata_set_spd(ap);
+       sata_set_spd(link);
 
        tout_msec = 100;
-       if (ata_port_online(ap))
+       if (ata_link_online(link))
                tout_msec = 5000;
 
        writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT);
@@ -617,14 +666,14 @@ static int sil24_hardreset(struct ata_port *ap, unsigned int *class)
        /* SStatus oscillates between zero and valid status after
         * DEV_RST, debounce it.
         */
-       rc = sata_phy_debounce(ap, sata_deb_timing_long);
+       rc = sata_link_debounce(link, sata_deb_timing_long, deadline);
        if (rc) {
                reason = "PHY debouncing failed";
                goto err;
        }
 
        if (tmp & PORT_CS_DEV_RST) {
-               if (ata_port_offline(ap))
+               if (ata_link_offline(link))
                        return 0;
                reason = "link not ready";
                goto err;
@@ -639,7 +688,7 @@ static int sil24_hardreset(struct ata_port *ap, unsigned int *class)
        return -EAGAIN;
 
  err:
-       ata_port_printk(ap, KERN_ERR, "hardreset failed (%s)\n", reason);
+       ata_link_printk(link, KERN_ERR, "hardreset failed (%s)\n", reason);
        return -EIO;
 }
 
@@ -647,7 +696,6 @@ static inline void sil24_fill_sg(struct ata_queued_cmd *qc,
                                 struct sil24_sge *sge)
 {
        struct scatterlist *sg;
-       unsigned int idx = 0;
 
        ata_for_each_sg(sg, qc) {
                sge->addr = cpu_to_le64(sg_dma_address(sg));
@@ -656,9 +704,7 @@ static inline void sil24_fill_sg(struct ata_queued_cmd *qc,
                        sge->flags = cpu_to_le32(SGE_TRM);
                else
                        sge->flags = 0;
-
                sge++;
-               idx++;
        }
 }
 
@@ -705,7 +751,7 @@ static void sil24_qc_prep(struct ata_queued_cmd *qc)
        }
 
        prb->ctrl = cpu_to_le16(ctrl);
-       ata_tf_to_fis(&qc->tf, prb->fis, 0);
+       ata_tf_to_fis(&qc->tf, 0, 1, prb->fis);
 
        if (qc->flags & ATA_QCFLAG_DMAMAP)
                sil24_fill_sg(qc, sge);
@@ -760,7 +806,8 @@ static void sil24_thaw(struct ata_port *ap)
 static void sil24_error_intr(struct ata_port *ap)
 {
        void __iomem *port = ap->ioaddr.cmd_addr;
-       struct ata_eh_info *ehi = &ap->eh_info;
+       struct sil24_port_priv *pp = ap->private_data;
+       struct ata_eh_info *ehi = &ap->link.eh_info;
        int freeze = 0;
        u32 irq_stat;
 
@@ -773,18 +820,23 @@ static void sil24_error_intr(struct ata_port *ap)
 
        ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat);
 
+       if (irq_stat & PORT_IRQ_SDB_NOTIFY) {
+               ata_ehi_push_desc(ehi, "SDB notify");
+               sata_async_notification(ap);
+       }
+
        if (irq_stat & (PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG)) {
                ata_ehi_hotplugged(ehi);
-               ata_ehi_push_desc(ehi, "%s",
-                              irq_stat & PORT_IRQ_PHYRDY_CHG ?
-                              "PHY RDY changed" : "device exchanged");
+               ata_ehi_push_desc(ehi, "%s",
+                                 irq_stat & PORT_IRQ_PHYRDY_CHG ?
+                                 "PHY RDY changed" : "device exchanged");
                freeze = 1;
        }
 
        if (irq_stat & PORT_IRQ_UNK_FIS) {
                ehi->err_mask |= AC_ERR_HSM;
                ehi->action |= ATA_EH_SOFTRESET;
-               ata_ehi_push_desc(ehi , ", unknown FIS");
+               ata_ehi_push_desc(ehi, "unknown FIS");
                freeze = 1;
        }
 
@@ -803,18 +855,18 @@ static void sil24_error_intr(struct ata_port *ap)
                if (ci && ci->desc) {
                        err_mask |= ci->err_mask;
                        action |= ci->action;
-                       ata_ehi_push_desc(ehi, "%s", ci->desc);
+                       ata_ehi_push_desc(ehi, "%s", ci->desc);
                } else {
                        err_mask |= AC_ERR_OTHER;
                        action |= ATA_EH_SOFTRESET;
-                       ata_ehi_push_desc(ehi, "unknown command error %d",
+                       ata_ehi_push_desc(ehi, "unknown command error %d",
                                          cerr);
                }
 
                /* record error info */
-               qc = ata_qc_from_tag(ap, ap->active_tag);
+               qc = ata_qc_from_tag(ap, ap->link.active_tag);
                if (qc) {
-                       sil24_update_tf(ap);
+                       sil24_read_tf(ap, qc->tag, &pp->tf);
                        qc->err_mask |= err_mask;
                } else
                        ehi->err_mask |= err_mask;
@@ -831,8 +883,11 @@ static void sil24_error_intr(struct ata_port *ap)
 
 static void sil24_finish_qc(struct ata_queued_cmd *qc)
 {
+       struct ata_port *ap = qc->ap;
+       struct sil24_port_priv *pp = ap->private_data;
+
        if (qc->flags & ATA_QCFLAG_RESULT_TF)
-               sil24_update_tf(qc->ap);
+               sil24_read_tf(ap, qc->tag, &pp->tf);
 }
 
 static inline void sil24_host_intr(struct ata_port *ap)
@@ -841,6 +896,16 @@ static inline void sil24_host_intr(struct ata_port *ap)
        u32 slot_stat, qc_active;
        int rc;
 
+       /* If PCIX_IRQ_WOC, there's an inherent race window between
+        * clearing IRQ pending status and reading PORT_SLOT_STAT
+        * which may cause spurious interrupts afterwards.  This is
+        * unavoidable and much better than losing interrupts which
+        * happens if IRQ pending is cleared after reading
+        * PORT_SLOT_STAT.
+        */
+       if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC)
+               writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT);
+
        slot_stat = readl(port + PORT_SLOT_STAT);
 
        if (unlikely(slot_stat & HOST_SSTAT_ATTN)) {
@@ -848,25 +913,23 @@ static inline void sil24_host_intr(struct ata_port *ap)
                return;
        }
 
-       if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC)
-               writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT);
-
        qc_active = slot_stat & ~HOST_SSTAT_ATTN;
        rc = ata_qc_complete_multiple(ap, qc_active, sil24_finish_qc);
        if (rc > 0)
                return;
        if (rc < 0) {
-               struct ata_eh_info *ehi = &ap->eh_info;
+               struct ata_eh_info *ehi = &ap->link.eh_info;
                ehi->err_mask |= AC_ERR_HSM;
                ehi->action |= ATA_EH_SOFTRESET;
                ata_port_freeze(ap);
                return;
        }
 
-       if (ata_ratelimit())
+       /* spurious interrupts are expected if PCIX_IRQ_WOC */
+       if (!(ap->flags & SIL24_FLAG_PCIX_IRQ_WOC) && ata_ratelimit())
                ata_port_printk(ap, KERN_INFO, "spurious interrupt "
                        "(slot_stat 0x%x active_tag %d sactive 0x%x)\n",
-                       slot_stat, ap->active_tag, ap->sactive);
+                       slot_stat, ap->link.active_tag, ap->link.sactive);
 }
 
 static irqreturn_t sil24_interrupt(int irq, void *dev_instance)
@@ -894,7 +957,7 @@ static irqreturn_t sil24_interrupt(int irq, void *dev_instance)
                if (status & (1 << i)) {
                        struct ata_port *ap = host->ports[i];
                        if (ap && !(ap->flags & ATA_FLAG_DISABLED)) {
-                               sil24_host_intr(host->ports[i]);
+                               sil24_host_intr(ap);
                                handled++;
                        } else
                                printk(KERN_ERR DRV_NAME
@@ -908,7 +971,7 @@ static irqreturn_t sil24_interrupt(int irq, void *dev_instance)
 
 static void sil24_error_handler(struct ata_port *ap)
 {
-       struct ata_eh_context *ehc = &ap->eh_context;
+       struct ata_eh_context *ehc = &ap->link.eh_context;
 
        if (sil24_init_port(ap)) {
                ata_eh_freeze_port(ap);
@@ -924,11 +987,8 @@ static void sil24_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;
-
        /* make DMA engine forget about the failed command */
-       if (qc->err_mask)
+       if (qc->flags & ATA_QCFLAG_FAILED)
                sil24_init_port(ap);
 }
 
@@ -964,11 +1024,10 @@ static int sil24_port_start(struct ata_port *ap)
        return 0;
 }
 
-static void sil24_init_controller(struct pci_dev *pdev, int n_ports,
-                                 unsigned long port_flags,
-                                 void __iomem *host_base,
-                                 void __iomem *port_base)
+static void sil24_init_controller(struct ata_host *host)
 {
+       void __iomem *host_base = host->iomap[SIL24_HOST_BAR];
+       void __iomem *port_base = host->iomap[SIL24_PORT_BAR];
        u32 tmp;
        int i;
 
@@ -979,7 +1038,7 @@ static void sil24_init_controller(struct pci_dev *pdev, int n_ports,
        writel(0, host_base + HOST_CTRL);
 
        /* init ports */
-       for (i = 0; i < n_ports; i++) {
+       for (i = 0; i < host->n_ports; i++) {
                void __iomem *port = port_base + i * PORT_REGS_SIZE;
 
                /* Initial PHY setting */
@@ -993,12 +1052,12 @@ static void sil24_init_controller(struct pci_dev *pdev, int n_ports,
                                                PORT_CS_PORT_RST,
                                                PORT_CS_PORT_RST, 10, 100);
                        if (tmp & PORT_CS_PORT_RST)
-                               dev_printk(KERN_ERR, &pdev->dev,
+                               dev_printk(KERN_ERR, host->dev,
                                           "failed to clear port RST\n");
                }
 
                /* Configure IRQ WoC */
-               if (port_flags & SIL24_FLAG_PCIX_IRQ_WOC)
+               if (host->ports[0]->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);
@@ -1026,18 +1085,17 @@ static void sil24_init_controller(struct pci_dev *pdev, int n_ports,
 static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        static int printed_version = 0;
-       struct device *dev = &pdev->dev;
-       unsigned int board_id = (unsigned int)ent->driver_data;
-       struct ata_port_info *pinfo = &sil24_port_info[board_id];
-       struct ata_probe_ent *probe_ent;
-       void __iomem *host_base;
-       void __iomem *port_base;
+       struct ata_port_info pi = sil24_port_info[ent->driver_data];
+       const struct ata_port_info *ppi[] = { &pi, NULL };
+       void __iomem * const *iomap;
+       struct ata_host *host;
        int i, rc;
        u32 tmp;
 
        if (!printed_version++)
                dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
+       /* acquire resources */
        rc = pcim_enable_device(pdev);
        if (rc)
                return rc;
@@ -1047,33 +1105,39 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                                DRV_NAME);
        if (rc)
                return rc;
+       iomap = pcim_iomap_table(pdev);
 
-       /* allocate & init probe_ent */
-       probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
-       if (!probe_ent)
-               return -ENOMEM;
+       /* apply workaround for completion IRQ loss on PCI-X errata */
+       if (pi.flags & SIL24_FLAG_PCIX_IRQ_WOC) {
+               tmp = readl(iomap[SIL24_HOST_BAR] + 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
+                       pi.flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;
+       }
 
-       probe_ent->dev = pci_dev_to_dev(pdev);
-       INIT_LIST_HEAD(&probe_ent->node);
+       /* allocate and fill host */
+       host = ata_host_alloc_pinfo(&pdev->dev, ppi,
+                                   SIL24_FLAG2NPORTS(ppi[0]->flags));
+       if (!host)
+               return -ENOMEM;
+       host->iomap = iomap;
 
-       probe_ent->sht          = pinfo->sht;
-       probe_ent->port_flags   = pinfo->flags;
-       probe_ent->pio_mask     = pinfo->pio_mask;
-       probe_ent->mwdma_mask   = pinfo->mwdma_mask;
-       probe_ent->udma_mask    = pinfo->udma_mask;
-       probe_ent->port_ops     = pinfo->port_ops;
-       probe_ent->n_ports      = SIL24_FLAG2NPORTS(pinfo->flags);
+       for (i = 0; i < host->n_ports; i++) {
+               struct ata_port *ap = host->ports[i];
+               size_t offset = ap->port_no * PORT_REGS_SIZE;
+               void __iomem *port = iomap[SIL24_PORT_BAR] + offset;
 
-       probe_ent->irq = pdev->irq;
-       probe_ent->irq_flags = IRQF_SHARED;
-       probe_ent->iomap = pcim_iomap_table(pdev);
+               host->ports[i]->ioaddr.cmd_addr = port;
+               host->ports[i]->ioaddr.scr_addr = port + PORT_SCONTROL;
 
-       host_base = probe_ent->iomap[SIL24_HOST_BAR];
-       port_base = probe_ent->iomap[SIL24_PORT_BAR];
+               ata_port_pbar_desc(ap, SIL24_HOST_BAR, -1, "host");
+               ata_port_pbar_desc(ap, SIL24_PORT_BAR, offset, "port");
+       }
 
-       /*
-        * Configure the device
-        */
+       /* configure and activate the device */
        if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
                rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
                if (rc) {
@@ -1099,36 +1163,11 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                }
        }
 
-       /* Apply workaround for completion IRQ loss on PCI-X errata */
-       if (probe_ent->port_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->port_flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;
-       }
-
-       for (i = 0; i < probe_ent->n_ports; i++) {
-               void __iomem *port = port_base + i * PORT_REGS_SIZE;
-
-               probe_ent->port[i].cmd_addr = port;
-               probe_ent->port[i].scr_addr = port + PORT_SCONTROL;
-
-               ata_std_ports(&probe_ent->port[i]);
-       }
-
-       sil24_init_controller(pdev, probe_ent->n_ports, probe_ent->port_flags,
-                             host_base, port_base);
+       sil24_init_controller(host);
 
        pci_set_master(pdev);
-
-       if (!ata_device_add(probe_ent))
-               return -ENODEV;
-
-       devm_kfree(dev, probe_ent);
-       return 0;
+       return ata_host_activate(host, pdev->irq, sil24_interrupt, IRQF_SHARED,
+                                &sil24_sht);
 }
 
 #ifdef CONFIG_PM
@@ -1136,7 +1175,6 @@ static int sil24_pci_device_resume(struct pci_dev *pdev)
 {
        struct ata_host *host = dev_get_drvdata(&pdev->dev);
        void __iomem *host_base = host->iomap[SIL24_HOST_BAR];
-       void __iomem *port_base = host->iomap[SIL24_PORT_BAR];
        int rc;
 
        rc = ata_pci_device_do_resume(pdev);
@@ -1146,8 +1184,7 @@ static int sil24_pci_device_resume(struct pci_dev *pdev)
        if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND)
                writel(HOST_CTRL_GLOBAL_RST, host_base + HOST_CTRL);
 
-       sil24_init_controller(pdev, host->n_ports, host->ports[0]->flags,
-                             host_base, port_base);
+       sil24_init_controller(host);
 
        ata_host_resume(host);