[libata] sata_sx4: convert to new exception handling methods
authorJeff Garzik <jeff@garzik.org>
Wed, 8 Apr 2009 20:02:18 +0000 (16:02 -0400)
committerJeff Garzik <jgarzik@redhat.com>
Fri, 15 May 2009 18:05:22 +0000 (14:05 -0400)
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
drivers/ata/sata_sx4.c

index dce3dccced3f0c2a2243ea7216be9e2a4f762737..1ce98afa69b65d682e8417feacba311af7970ac0 100644 (file)
@@ -213,8 +213,9 @@ struct pdc_host_priv {
 
 
 static int pdc_sata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
-static void pdc_eng_timeout(struct ata_port *ap);
-static void pdc_20621_phy_reset(struct ata_port *ap);
+static void pdc_error_handler(struct ata_port *ap);
+static void pdc_freeze(struct ata_port *ap);
+static void pdc_thaw(struct ata_port *ap);
 static int pdc_port_start(struct ata_port *ap);
 static void pdc20621_qc_prep(struct ata_queued_cmd *qc);
 static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
@@ -233,6 +234,10 @@ static void pdc20621_put_to_dimm(struct ata_host *host,
                                 void *psource, u32 offset, u32 size);
 static void pdc20621_irq_clear(struct ata_port *ap);
 static unsigned int pdc20621_qc_issue(struct ata_queued_cmd *qc);
+static int pdc_softreset(struct ata_link *link, unsigned int *class,
+                        unsigned long deadline);
+static void pdc_post_internal_cmd(struct ata_queued_cmd *qc);
+static int pdc_check_atapi_dma(struct ata_queued_cmd *qc);
 
 
 static struct scsi_host_template pdc_sata_sht = {
@@ -243,20 +248,24 @@ static struct scsi_host_template pdc_sata_sht = {
 
 /* TODO: inherit from base port_ops after converting to new EH */
 static struct ata_port_operations pdc_20621_ops = {
-       .sff_tf_load            = pdc_tf_load_mmio,
-       .sff_tf_read            = ata_sff_tf_read,
-       .sff_check_status       = ata_sff_check_status,
-       .sff_exec_command       = pdc_exec_command_mmio,
-       .sff_dev_select         = ata_sff_dev_select,
-       .phy_reset              = pdc_20621_phy_reset,
+       .inherits               = &ata_sff_port_ops,
+
+       .check_atapi_dma        = pdc_check_atapi_dma,
        .qc_prep                = pdc20621_qc_prep,
        .qc_issue               = pdc20621_qc_issue,
-       .qc_fill_rtf            = ata_sff_qc_fill_rtf,
-       .sff_data_xfer          = ata_sff_data_xfer,
-       .eng_timeout            = pdc_eng_timeout,
-       .sff_irq_clear          = pdc20621_irq_clear,
-       .sff_irq_on             = ata_sff_irq_on,
+
+       .freeze                 = pdc_freeze,
+       .thaw                   = pdc_thaw,
+       .softreset              = pdc_softreset,
+       .error_handler          = pdc_error_handler,
+       .lost_interrupt         = ATA_OP_NULL,
+       .post_internal_cmd      = pdc_post_internal_cmd,
+
        .port_start             = pdc_port_start,
+
+       .sff_tf_load            = pdc_tf_load_mmio,
+       .sff_exec_command       = pdc_exec_command_mmio,
+       .sff_irq_clear          = pdc20621_irq_clear,
 };
 
 static const struct ata_port_info pdc_port_info[] = {
@@ -310,14 +319,6 @@ static int pdc_port_start(struct ata_port *ap)
        return 0;
 }
 
-static void pdc_20621_phy_reset(struct ata_port *ap)
-{
-       VPRINTK("ENTER\n");
-       ap->cbl = ATA_CBL_SATA;
-       ata_port_probe(ap);
-       ata_bus_reset(ap);
-}
-
 static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf,
                                   unsigned int portno,
                                           unsigned int total_len)
@@ -859,40 +860,115 @@ static irqreturn_t pdc20621_interrupt(int irq, void *dev_instance)
        return IRQ_RETVAL(handled);
 }
 
-static void pdc_eng_timeout(struct ata_port *ap)
+static void pdc_freeze(struct ata_port *ap)
 {
-       u8 drv_stat;
-       struct ata_host *host = ap->host;
-       struct ata_queued_cmd *qc;
-       unsigned long flags;
+       void __iomem *mmio = ap->ioaddr.cmd_addr;
+       u32 tmp;
 
-       DPRINTK("ENTER\n");
+       /* FIXME: if all 4 ATA engines are stopped, also stop HDMA engine */
 
-       spin_lock_irqsave(&host->lock, flags);
+       tmp = readl(mmio + PDC_CTLSTAT);
+       tmp |= PDC_MASK_INT;
+       tmp &= ~PDC_DMA_ENABLE;
+       writel(tmp, mmio + PDC_CTLSTAT);
+       readl(mmio + PDC_CTLSTAT); /* flush */
+}
 
-       qc = ata_qc_from_tag(ap, ap->link.active_tag);
+static void pdc_thaw(struct ata_port *ap)
+{
+       void __iomem *mmio = ap->ioaddr.cmd_addr;
+       void __iomem *mmio_base;
+       u32 tmp;
 
-       switch (qc->tf.protocol) {
-       case ATA_PROT_DMA:
-       case ATA_PROT_NODATA:
-               ata_port_printk(ap, KERN_ERR, "command timeout\n");
-               qc->err_mask |= __ac_err_mask(ata_wait_idle(ap));
-               break;
+       /* FIXME: start HDMA engine, if zero ATA engines running */
 
-       default:
-               drv_stat = ata_sff_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000);
+       /* reading SEQ mask register clears IRQ */
+       mmio_base = ap->host->iomap[PDC_MMIO_BAR] + PDC_CHIP0_OFS;
+       readl(mmio_base + PDC_20621_SEQMASK);
 
-               ata_port_printk(ap, KERN_ERR,
-                               "unknown timeout, cmd 0x%x stat 0x%x\n",
-                               qc->tf.command, drv_stat);
+       /* turn IRQ back on */
+       tmp = readl(mmio + PDC_CTLSTAT);
+       tmp &= ~PDC_MASK_INT;
+       writel(tmp, mmio + PDC_CTLSTAT);
+       readl(mmio + PDC_CTLSTAT); /* flush */
+}
 
-               qc->err_mask |= ac_err_mask(drv_stat);
-               break;
+static void pdc_reset_port(struct ata_port *ap)
+{
+       void __iomem *mmio = ap->ioaddr.cmd_addr + PDC_CTLSTAT;
+       unsigned int i;
+       u32 tmp;
+
+       /* FIXME: handle HDMA copy engine */
+
+       for (i = 11; i > 0; i--) {
+               tmp = readl(mmio);
+               if (tmp & PDC_RESET)
+                       break;
+
+               udelay(100);
+
+               tmp |= PDC_RESET;
+               writel(tmp, mmio);
        }
 
-       spin_unlock_irqrestore(&host->lock, flags);
-       ata_eh_qc_complete(qc);
-       DPRINTK("EXIT\n");
+       tmp &= ~PDC_RESET;
+       writel(tmp, mmio);
+       readl(mmio);    /* flush */
+}
+
+static int pdc_softreset(struct ata_link *link, unsigned int *class,
+                        unsigned long deadline)
+{
+       pdc_reset_port(link->ap);
+       return ata_sff_softreset(link, class, deadline);
+}
+
+static void pdc_error_handler(struct ata_port *ap)
+{
+       if (!(ap->pflags & ATA_PFLAG_FROZEN))
+               pdc_reset_port(ap);
+
+       ata_std_error_handler(ap);
+}
+
+static void pdc_post_internal_cmd(struct ata_queued_cmd *qc)
+{
+       struct ata_port *ap = qc->ap;
+
+       /* make DMA engine forget about the failed command */
+       if (qc->flags & ATA_QCFLAG_FAILED)
+               pdc_reset_port(ap);
+}
+
+static int pdc_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+       u8 *scsicmd = qc->scsicmd->cmnd;
+       int pio = 1; /* atapi dma off by default */
+
+       /* Whitelist commands that may use DMA. */
+       switch (scsicmd[0]) {
+       case WRITE_12:
+       case WRITE_10:
+       case WRITE_6:
+       case READ_12:
+       case READ_10:
+       case READ_6:
+       case 0xad: /* READ_DVD_STRUCTURE */
+       case 0xbe: /* READ_CD */
+               pio = 0;
+       }
+       /* -45150 (FFFF4FA2) to -1 (FFFFFFFF) shall use PIO mode */
+       if (scsicmd[0] == WRITE_10) {
+               unsigned int lba =
+                       (scsicmd[2] << 24) |
+                       (scsicmd[3] << 16) |
+                       (scsicmd[4] << 8) |
+                       scsicmd[5];
+               if (lba >= 0xFFFF4FA2)
+                       pio = 1;
+       }
+       return pio;
 }
 
 static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf)