]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/scsi/sata_sil24.c
[PATCH] Revert "ACPI: execute Notify() handlers on new thread"
[linux-2.6-omap-h63xx.git] / drivers / scsi / sata_sil24.c
index 6e7728cfaf6bd21e53d8dd4db8b273526a19e29c..2e0f4a4076af6cd48a118c68b3640917116c158f 100644 (file)
 #include <asm/io.h>
 
 #define DRV_NAME       "sata_sil24"
-#define DRV_VERSION    "0.24"
+#define DRV_VERSION    "0.3"
 
 /*
  * Port request block (PRB) 32 bytes
  */
 struct sil24_prb {
-       u16     ctrl;
-       u16     prot;
-       u32     rx_cnt;
+       __le16  ctrl;
+       __le16  prot;
+       __le32  rx_cnt;
        u8      fis[6 * 4];
 };
 
@@ -47,17 +47,17 @@ struct sil24_prb {
  * Scatter gather entry (SGE) 16 bytes
  */
 struct sil24_sge {
-       u64     addr;
-       u32     cnt;
-       u32     flags;
+       __le64  addr;
+       __le32  cnt;
+       __le32  flags;
 };
 
 /*
  * Port multiplier
  */
 struct sil24_port_multiplier {
-       u32     diag;
-       u32     sactive;
+       __le32  diag;
+       __le32  sactive;
 };
 
 enum {
@@ -92,13 +92,16 @@ enum {
        HOST_CTRL_STOP          = (1 << 18), /* latched PCI STOP */
        HOST_CTRL_DEVSEL        = (1 << 19), /* latched PCI DEVSEL */
        HOST_CTRL_REQ64         = (1 << 20), /* latched PCI REQ64 */
+       HOST_CTRL_GLOBAL_RST    = (1 << 31), /* global reset */
 
        /*
         * Port registers
         * (8192 bytes @ +0x0000, +0x2000, +0x4000 and +0x6000 @ BAR2)
         */
        PORT_REGS_SIZE          = 0x2000,
-       PORT_PRB                = 0x0000, /* (32 bytes PRB + 16 bytes SGEs * 6) * 31 (3968 bytes) */
+
+       PORT_LRAM               = 0x0000, /* 31 LRAM slots and PM regs */
+       PORT_LRAM_SLOT_SZ       = 0x0080, /* 32 bytes PRB + 2 SGE, ACT... */
 
        PORT_PM                 = 0x0f80, /* 8 bytes PM * 16 (128 bytes) */
                /* 32 bit regs */
@@ -157,7 +160,8 @@ enum {
        PORT_IRQ_SDB_NOTIFY     = (1 << 11), /* SDB notify received */
 
        DEF_PORT_IRQ            = PORT_IRQ_COMPLETE | PORT_IRQ_ERROR |
-                                 PORT_IRQ_DEV_XCHG | PORT_IRQ_UNK_FIS,
+                                 PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG |
+                                 PORT_IRQ_UNK_FIS,
 
        /* bits[27:16] are unmasked (raw) */
        PORT_IRQ_RAW_SHIFT      = 16,
@@ -216,6 +220,8 @@ enum {
        SGE_DRD                 = (1 << 29), /* discard data read (/dev/null)
                                                data address ignored */
 
+       SIL24_MAX_CMDS          = 31,
+
        /* board id */
        BID_SIL3124             = 0,
        BID_SIL3132             = 1,
@@ -223,7 +229,8 @@ enum {
 
        /* host flags */
        SIL24_COMMON_FLAGS      = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
-                                 ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA,
+                                 ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
+                                 ATA_FLAG_NCQ | ATA_FLAG_SKIP_D2H_BSY,
        SIL24_FLAG_PCIX_IRQ_WOC = (1 << 24), /* IRQ loss errata on PCI-X */
 
        IRQ_STAT_4PORTS         = 0xf,
@@ -320,7 +327,6 @@ 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 void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
-static int sil24_probe_reset(struct ata_port *ap, unsigned int *classes);
 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);
@@ -333,6 +339,7 @@ static int sil24_port_start(struct ata_port *ap);
 static void sil24_port_stop(struct ata_port *ap);
 static void sil24_host_stop(struct ata_host_set *host_set);
 static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
+static int sil24_pci_device_resume(struct pci_dev *pdev);
 
 static const struct pci_device_id sil24_pci_tbl[] = {
        { 0x1095, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 },
@@ -348,6 +355,8 @@ static struct pci_driver sil24_pci_driver = {
        .id_table               = sil24_pci_tbl,
        .probe                  = sil24_init_one,
        .remove                 = ata_pci_remove_one, /* safe? */
+       .suspend                = ata_pci_device_suspend,
+       .resume                 = sil24_pci_device_resume,
 };
 
 static struct scsi_host_template sil24_sht = {
@@ -355,7 +364,8 @@ static struct scsi_host_template sil24_sht = {
        .name                   = DRV_NAME,
        .ioctl                  = ata_scsi_ioctl,
        .queuecommand           = ata_scsi_queuecmd,
-       .can_queue              = ATA_DEF_QUEUE,
+       .change_queue_depth     = ata_scsi_change_queue_depth,
+       .can_queue              = SIL24_MAX_CMDS,
        .this_id                = ATA_SHT_THIS_ID,
        .sg_tablesize           = LIBATA_MAX_PRD,
        .cmd_per_lun            = ATA_SHT_CMD_PER_LUN,
@@ -364,7 +374,10 @@ static struct scsi_host_template sil24_sht = {
        .proc_name              = DRV_NAME,
        .dma_boundary           = ATA_DMA_BOUNDARY,
        .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 = {
@@ -378,8 +391,6 @@ static const struct ata_port_operations sil24_ops = {
 
        .tf_read                = sil24_tf_read,
 
-       .probe_reset            = sil24_probe_reset,
-
        .qc_prep                = sil24_qc_prep,
        .qc_issue               = sil24_qc_issue,
 
@@ -437,6 +448,13 @@ static struct ata_port_info sil24_port_info[] = {
        },
 };
 
+static int sil24_tag(int tag)
+{
+       if (unlikely(ata_tag_internal(tag)))
+               return 0;
+       return tag;
+}
+
 static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev)
 {
        void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
@@ -578,7 +596,7 @@ static int sil24_hardreset(struct ata_port *ap, unsigned int *class)
 {
        void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
        const char *reason;
-       int tout_msec;
+       int tout_msec, rc;
        u32 tmp;
 
        /* sil24 does the right thing(tm) without any protection */
@@ -592,10 +610,14 @@ static int sil24_hardreset(struct ata_port *ap, unsigned int *class)
        tmp = ata_wait_register(port + PORT_CTRL_STAT,
                                PORT_CS_DEV_RST, PORT_CS_DEV_RST, 10, tout_msec);
 
-       /* SStatus oscillates between zero and valid status for short
-        * duration after DEV_RST, give it time to settle.
+       /* SStatus oscillates between zero and valid status after
+        * DEV_RST, debounce it.
         */
-       msleep(100);
+       rc = sata_phy_debounce(ap, sata_deb_timing_long);
+       if (rc) {
+               reason = "PHY debouncing failed";
+               goto err;
+       }
 
        if (tmp & PORT_CS_DEV_RST) {
                if (ata_port_offline(ap))
@@ -604,28 +626,19 @@ static int sil24_hardreset(struct ata_port *ap, unsigned int *class)
                goto err;
        }
 
-       if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) {
-               reason = "device not ready";
-               goto err;
-       }
-
-       /* sil24 doesn't report device class code after hardreset,
-        * leave *class alone.
+       /* Sil24 doesn't store signature FIS after hardreset, so we
+        * can't wait for BSY to clear.  Some devices take a long time
+        * to get ready and those devices will choke if we don't wait
+        * for BSY clearance here.  Tell libata to perform follow-up
+        * softreset.
         */
-       return 0;
+       return -EAGAIN;
 
  err:
        ata_port_printk(ap, KERN_ERR, "hardreset failed (%s)\n", reason);
        return -EIO;
 }
 
-static int sil24_probe_reset(struct ata_port *ap, unsigned int *classes)
-{
-       return ata_drive_probe_reset(ap, ata_std_probeinit,
-                                    sil24_softreset, sil24_hardreset,
-                                    ata_std_postreset, classes);
-}
-
 static inline void sil24_fill_sg(struct ata_queued_cmd *qc,
                                 struct sil24_sge *sge)
 {
@@ -649,14 +662,17 @@ static void sil24_qc_prep(struct ata_queued_cmd *qc)
 {
        struct ata_port *ap = qc->ap;
        struct sil24_port_priv *pp = ap->private_data;
-       union sil24_cmd_block *cb = pp->cmd_block + qc->tag;
+       union sil24_cmd_block *cb;
        struct sil24_prb *prb;
        struct sil24_sge *sge;
        u16 ctrl = 0;
 
+       cb = &pp->cmd_block[sil24_tag(qc->tag)];
+
        switch (qc->tf.protocol) {
        case ATA_PROT_PIO:
        case ATA_PROT_DMA:
+       case ATA_PROT_NCQ:
        case ATA_PROT_NODATA:
                prb = &cb->ata.prb;
                sge = cb->ata.sge;
@@ -694,12 +710,17 @@ static void sil24_qc_prep(struct ata_queued_cmd *qc)
 static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc)
 {
        struct ata_port *ap = qc->ap;
-       void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
        struct sil24_port_priv *pp = ap->private_data;
-       dma_addr_t paddr = pp->cmd_block_dma + qc->tag * sizeof(*pp->cmd_block);
+       void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
+       unsigned int tag = sil24_tag(qc->tag);
+       dma_addr_t paddr;
+       void __iomem *activate;
 
-       writel((u32)paddr, port + PORT_CMD_ACTIVATE);
-       writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + 4);
+       paddr = pp->cmd_block_dma + tag * sizeof(*pp->cmd_block);
+       activate = port + PORT_CMD_ACTIVATE + tag * 8;
+
+       writel((u32)paddr, activate);
+       writel((u64)paddr >> 32, activate + 4);
 
        return 0;
 }
@@ -748,13 +769,11 @@ 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_DEV_XCHG) {
-               ehi->err_mask |= AC_ERR_ATA_BUS;
-               /* sil24 doesn't recover very well from phy
-                * disconnection with a softreset.  Force hardreset.
-                */
-               ehi->action |= ATA_EH_HARDRESET;
-               ata_ehi_push_desc(ehi, ", device_exchanged");
+       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");
                freeze = 1;
        }
 
@@ -791,9 +810,6 @@ static void sil24_error_intr(struct ata_port *ap)
                /* record error info */
                qc = ata_qc_from_tag(ap, ap->active_tag);
                if (qc) {
-                       int tag = qc->tag;
-                       if (unlikely(ata_tag_internal(tag)))
-                               tag = 0;
                        sil24_update_tf(ap);
                        qc->err_mask |= err_mask;
                } else
@@ -809,11 +825,17 @@ static void sil24_error_intr(struct ata_port *ap)
                ata_port_abort(ap);
 }
 
+static void sil24_finish_qc(struct ata_queued_cmd *qc)
+{
+       if (qc->flags & ATA_QCFLAG_RESULT_TF)
+               sil24_update_tf(qc->ap);
+}
+
 static inline void sil24_host_intr(struct ata_port *ap)
 {
        void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
-       struct ata_queued_cmd *qc;
-       u32 slot_stat;
+       u32 slot_stat, qc_active;
+       int rc;
 
        slot_stat = readl(port + PORT_SLOT_STAT);
 
@@ -825,18 +847,22 @@ static inline void sil24_host_intr(struct ata_port *ap)
        if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC)
                writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT);
 
-       qc = ata_qc_from_tag(ap, ap->active_tag);
-       if (qc) {
-               if (qc->flags & ATA_QCFLAG_RESULT_TF)
-                       sil24_update_tf(ap);
-               ata_qc_complete(qc);
+       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;
+               ehi->err_mask |= AC_ERR_HSM;
+               ehi->action |= ATA_EH_SOFTRESET;
+               ata_port_freeze(ap);
                return;
        }
 
        if (ata_ratelimit())
                ata_port_printk(ap, KERN_INFO, "spurious interrupt "
-                       "(slot_stat 0x%x active_tag %d)\n",
-                       slot_stat, ap->active_tag);
+                       "(slot_stat 0x%x active_tag %d sactive 0x%x)\n",
+                       slot_stat, ap->active_tag, ap->sactive);
 }
 
 static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
@@ -886,7 +912,8 @@ static void sil24_error_handler(struct ata_port *ap)
        }
 
        /* perform recovery */
-       ata_do_eh(ap, sil24_softreset, sil24_hardreset, ata_std_postreset);
+       ata_do_eh(ap, ata_std_prereset, sil24_softreset, sil24_hardreset,
+                 ata_std_postreset);
 }
 
 static void sil24_post_internal_cmd(struct ata_queued_cmd *qc)
@@ -903,7 +930,7 @@ static void sil24_post_internal_cmd(struct ata_queued_cmd *qc)
 
 static inline void sil24_cblk_free(struct sil24_port_priv *pp, struct device *dev)
 {
-       const size_t cb_size = sizeof(*pp->cmd_block);
+       const size_t cb_size = sizeof(*pp->cmd_block) * SIL24_MAX_CMDS;
 
        dma_free_coherent(dev, cb_size, pp->cmd_block, pp->cmd_block_dma);
 }
@@ -913,7 +940,7 @@ static int sil24_port_start(struct ata_port *ap)
        struct device *dev = ap->host_set->dev;
        struct sil24_port_priv *pp;
        union sil24_cmd_block *cb;
-       size_t cb_size = sizeof(*cb);
+       size_t cb_size = sizeof(*cb) * SIL24_MAX_CMDS;
        dma_addr_t cb_dma;
        int rc = -ENOMEM;
 
@@ -967,6 +994,64 @@ static void sil24_host_stop(struct ata_host_set *host_set)
        kfree(hpriv);
 }
 
+static void sil24_init_controller(struct pci_dev *pdev, int n_ports,
+                                 unsigned long host_flags,
+                                 void __iomem *host_base,
+                                 void __iomem *port_base)
+{
+       u32 tmp;
+       int i;
+
+       /* GPIO off */
+       writel(0, host_base + HOST_FLASH_CMD);
+
+       /* clear global reset & mask interrupts during initialization */
+       writel(0, host_base + HOST_CTRL);
+
+       /* init ports */
+       for (i = 0; i < n_ports; i++) {
+               void __iomem *port = port_base + i * PORT_REGS_SIZE;
+
+               /* Initial PHY setting */
+               writel(0x20c, port + PORT_PHY_CFG);
+
+               /* Clear port RST */
+               tmp = readl(port + PORT_CTRL_STAT);
+               if (tmp & PORT_CS_PORT_RST) {
+                       writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
+                       tmp = ata_wait_register(port + PORT_CTRL_STAT,
+                                               PORT_CS_PORT_RST,
+                                               PORT_CS_PORT_RST, 10, 100);
+                       if (tmp & PORT_CS_PORT_RST)
+                               dev_printk(KERN_ERR, &pdev->dev,
+                                          "failed to clear port RST\n");
+               }
+
+               /* Configure IRQ WoC */
+               if (host_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);
+
+               /* Zero error counters. */
+               writel(0x8000, port + PORT_DECODE_ERR_THRESH);
+               writel(0x8000, port + PORT_CRC_ERR_THRESH);
+               writel(0x8000, port + PORT_HSHK_ERR_THRESH);
+               writel(0x0000, port + PORT_DECODE_ERR_CNT);
+               writel(0x0000, port + PORT_CRC_ERR_CNT);
+               writel(0x0000, port + PORT_HSHK_ERR_CNT);
+
+               /* Always use 64bit activation */
+               writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
+
+               /* Clear port multiplier enable and resume bits */
+               writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR);
+       }
+
+       /* Turn on interrupts */
+       writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL);
+}
+
 static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        static int printed_version = 0;
@@ -1020,7 +1105,7 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        probe_ent->n_ports      = SIL24_FLAG2NPORTS(pinfo->host_flags);
 
        probe_ent->irq = pdev->irq;
-       probe_ent->irq_flags = SA_SHIRQ;
+       probe_ent->irq_flags = IRQF_SHARED;
        probe_ent->mmio_base = port_base;
        probe_ent->private_data = hpriv;
 
@@ -1055,9 +1140,6 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                }
        }
 
-       /* GPIO off */
-       writel(0, host_base + HOST_FLASH_CMD);
-
        /* Apply workaround for completion IRQ loss on PCI-X errata */
        if (probe_ent->host_flags & SIL24_FLAG_PCIX_IRQ_WOC) {
                tmp = readl(host_base + HOST_CTRL);
@@ -1069,56 +1151,18 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                        probe_ent->host_flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;
        }
 
-       /* clear global reset & mask interrupts during initialization */
-       writel(0, host_base + HOST_CTRL);
-
        for (i = 0; i < probe_ent->n_ports; i++) {
-               void __iomem *port = port_base + i * PORT_REGS_SIZE;
-               unsigned long portu = (unsigned long)port;
+               unsigned long portu =
+                       (unsigned long)port_base + i * PORT_REGS_SIZE;
 
-               probe_ent->port[i].cmd_addr = portu + PORT_PRB;
+               probe_ent->port[i].cmd_addr = portu;
                probe_ent->port[i].scr_addr = portu + PORT_SCONTROL;
 
                ata_std_ports(&probe_ent->port[i]);
-
-               /* Initial PHY setting */
-               writel(0x20c, port + PORT_PHY_CFG);
-
-               /* Clear port RST */
-               tmp = readl(port + PORT_CTRL_STAT);
-               if (tmp & PORT_CS_PORT_RST) {
-                       writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
-                       tmp = ata_wait_register(port + PORT_CTRL_STAT,
-                                               PORT_CS_PORT_RST,
-                                               PORT_CS_PORT_RST, 10, 100);
-                       if (tmp & PORT_CS_PORT_RST)
-                               dev_printk(KERN_ERR, &pdev->dev,
-                                          "failed to clear port RST\n");
-               }
-
-               /* Configure IRQ WoC */
-               if (probe_ent->host_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);
-
-               /* Zero error counters. */
-               writel(0x8000, port + PORT_DECODE_ERR_THRESH);
-               writel(0x8000, port + PORT_CRC_ERR_THRESH);
-               writel(0x8000, port + PORT_HSHK_ERR_THRESH);
-               writel(0x0000, port + PORT_DECODE_ERR_CNT);
-               writel(0x0000, port + PORT_CRC_ERR_CNT);
-               writel(0x0000, port + PORT_HSHK_ERR_CNT);
-
-               /* Always use 64bit activation */
-               writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
-
-               /* Clear port multiplier enable and resume bits */
-               writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR);
        }
 
-       /* Turn on interrupts */
-       writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL);
+       sil24_init_controller(pdev, probe_ent->n_ports, probe_ent->host_flags,
+                             host_base, port_base);
 
        pci_set_master(pdev);
 
@@ -1141,6 +1185,25 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        return rc;
 }
 
+static int sil24_pci_device_resume(struct pci_dev *pdev)
+{
+       struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+       struct sil24_host_priv *hpriv = host_set->private_data;
+
+       ata_pci_device_do_resume(pdev);
+
+       if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND)
+               writel(HOST_CTRL_GLOBAL_RST, hpriv->host_base + HOST_CTRL);
+
+       sil24_init_controller(pdev, host_set->n_ports,
+                             host_set->ports[0]->flags,
+                             hpriv->host_base, hpriv->port_base);
+
+       ata_host_set_resume(host_set);
+
+       return 0;
+}
+
 static int __init sil24_init(void)
 {
        return pci_module_init(&sil24_pci_driver);