libata-pmp-prep: implement sata_async_notification()
[safe/jmp/linux-2.6] / drivers / ata / sata_sil24.c
index a69d78c..b4f81eb 100644 (file)
@@ -30,7 +30,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME       "sata_sil24"
-#define DRV_VERSION    "0.8"
+#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,
@@ -325,8 +327,8 @@ struct sil24_port_priv {
 
 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);
@@ -383,8 +385,6 @@ static struct scsi_host_template sil24_sht = {
 };
 
 static const struct ata_port_operations sil24_ops = {
-       .port_disable           = ata_port_disable,
-
        .dev_config             = sil24_dev_config,
 
        .check_status           = sil24_check_status,
@@ -393,12 +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_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,
@@ -423,25 +422,28 @@ static const struct ata_port_info sil24_port_info[] = {
        {
                .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 */
        {
                .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 */
        {
                .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,
        },
 };
@@ -455,7 +457,7 @@ static int sil24_tag(int tag)
 
 static void sil24_dev_config(struct ata_device *dev)
 {
-       void __iomem *port = dev->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);
@@ -463,15 +465,15 @@ static void sil24_dev_config(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)
@@ -487,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)
@@ -530,19 +537,65 @@ static int sil24_init_port(struct ata_port *ap)
        return 0;
 }
 
-static int sil24_softreset(struct ata_port *ap, unsigned int *class,
-                          unsigned long deadline)
+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;
@@ -555,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, jiffies_to_msecs(deadline - jiffies));
-
-       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;
@@ -587,23 +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);
@@ -613,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, deadline);
+       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;
@@ -635,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;
 }
 
@@ -698,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);
@@ -753,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;
 
@@ -766,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;
        }
 
@@ -796,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;
@@ -824,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)
@@ -834,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)) {
@@ -841,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)
@@ -887,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
@@ -901,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);
@@ -1056,12 +1126,15 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        host->iomap = iomap;
 
        for (i = 0; i < host->n_ports; i++) {
-               void __iomem *port = iomap[SIL24_PORT_BAR] + i * PORT_REGS_SIZE;
+               struct ata_port *ap = host->ports[i];
+               size_t offset = ap->port_no * PORT_REGS_SIZE;
+               void __iomem *port = iomap[SIL24_PORT_BAR] + offset;
 
                host->ports[i]->ioaddr.cmd_addr = port;
                host->ports[i]->ioaddr.scr_addr = port + PORT_SCONTROL;
 
-               ata_std_ports(&host->ports[i]->ioaddr);
+               ata_port_pbar_desc(ap, SIL24_HOST_BAR, -1, "host");
+               ata_port_pbar_desc(ap, SIL24_PORT_BAR, offset, "port");
        }
 
        /* configure and activate the device */