Pull bugzilla-7200 into release branch
[powerpc.git] / drivers / ata / sata_nv.c
index b9ef6f5..ab92f20 100644 (file)
@@ -362,6 +362,8 @@ static const struct ata_port_operations nv_generic_ops = {
        .data_xfer              = ata_data_xfer,
        .irq_handler            = nv_generic_interrupt,
        .irq_clear              = ata_bmdma_irq_clear,
+       .irq_on                 = ata_irq_on,
+       .irq_ack                = ata_irq_ack,
        .scr_read               = nv_scr_read,
        .scr_write              = nv_scr_write,
        .port_start             = ata_port_start,
@@ -387,6 +389,8 @@ static const struct ata_port_operations nv_nf2_ops = {
        .data_xfer              = ata_data_xfer,
        .irq_handler            = nv_nf2_interrupt,
        .irq_clear              = ata_bmdma_irq_clear,
+       .irq_on                 = ata_irq_on,
+       .irq_ack                = ata_irq_ack,
        .scr_read               = nv_scr_read,
        .scr_write              = nv_scr_write,
        .port_start             = ata_port_start,
@@ -412,6 +416,8 @@ static const struct ata_port_operations nv_ck804_ops = {
        .data_xfer              = ata_data_xfer,
        .irq_handler            = nv_ck804_interrupt,
        .irq_clear              = ata_bmdma_irq_clear,
+       .irq_on                 = ata_irq_on,
+       .irq_ack                = ata_irq_ack,
        .scr_read               = nv_scr_read,
        .scr_write              = nv_scr_write,
        .port_start             = ata_port_start,
@@ -439,6 +445,8 @@ static const struct ata_port_operations nv_adma_ops = {
        .data_xfer              = ata_data_xfer,
        .irq_handler            = nv_adma_interrupt,
        .irq_clear              = nv_adma_irq_clear,
+       .irq_on                 = ata_irq_on,
+       .irq_ack                = ata_irq_ack,
        .scr_read               = nv_scr_read,
        .scr_write              = nv_scr_write,
        .port_start             = nv_adma_port_start,
@@ -504,14 +512,38 @@ static void nv_adma_register_mode(struct ata_port *ap)
 {
        struct nv_adma_port_priv *pp = ap->private_data;
        void __iomem *mmio = pp->ctl_block;
-       u16 tmp;
+       u16 tmp, status;
+       int count = 0;
 
        if (pp->flags & NV_ADMA_PORT_REGISTER_MODE)
                return;
 
+       status = readw(mmio + NV_ADMA_STAT);
+       while(!(status & NV_ADMA_STAT_IDLE) && count < 20) {
+               ndelay(50);
+               status = readw(mmio + NV_ADMA_STAT);
+               count++;
+       }
+       if(count == 20)
+               ata_port_printk(ap, KERN_WARNING,
+                       "timeout waiting for ADMA IDLE, stat=0x%hx\n",
+                       status);
+
        tmp = readw(mmio + NV_ADMA_CTL);
        writew(tmp & ~NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
 
+       count = 0;
+       status = readw(mmio + NV_ADMA_STAT);
+       while(!(status & NV_ADMA_STAT_LEGACY) && count < 20) {
+               ndelay(50);
+               status = readw(mmio + NV_ADMA_STAT);
+               count++;
+       }
+       if(count == 20)
+               ata_port_printk(ap, KERN_WARNING,
+                        "timeout waiting for ADMA LEGACY, stat=0x%hx\n",
+                        status);
+
        pp->flags |= NV_ADMA_PORT_REGISTER_MODE;
 }
 
@@ -519,7 +551,8 @@ static void nv_adma_mode(struct ata_port *ap)
 {
        struct nv_adma_port_priv *pp = ap->private_data;
        void __iomem *mmio = pp->ctl_block;
-       u16 tmp;
+       u16 tmp, status;
+       int count = 0;
 
        if (!(pp->flags & NV_ADMA_PORT_REGISTER_MODE))
                return;
@@ -529,6 +562,18 @@ static void nv_adma_mode(struct ata_port *ap)
        tmp = readw(mmio + NV_ADMA_CTL);
        writew(tmp | NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
 
+       status = readw(mmio + NV_ADMA_STAT);
+       while(((status & NV_ADMA_STAT_LEGACY) ||
+             !(status & NV_ADMA_STAT_IDLE)) && count < 20) {
+               ndelay(50);
+               status = readw(mmio + NV_ADMA_STAT);
+               count++;
+       }
+       if(count == 20)
+               ata_port_printk(ap, KERN_WARNING,
+                       "timeout waiting for ADMA LEGACY clear and IDLE, stat=0x%hx\n",
+                       status);
+
        pp->flags &= ~NV_ADMA_PORT_REGISTER_MODE;
 }
 
@@ -643,53 +688,62 @@ static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, __le16 *cpb)
        return idx;
 }
 
-static void nv_adma_check_cpb(struct ata_port *ap, int cpb_num, int force_err)
+static int nv_adma_check_cpb(struct ata_port *ap, int cpb_num, int force_err)
 {
        struct nv_adma_port_priv *pp = ap->private_data;
-       int complete = 0, have_err = 0;
        u8 flags = pp->cpb[cpb_num].resp_flags;
 
        VPRINTK("CPB %d, flags=0x%x\n", cpb_num, flags);
 
-       if (flags & NV_CPB_RESP_DONE) {
-               VPRINTK("CPB flags done, flags=0x%x\n", flags);
-               complete = 1;
-       }
-       if (flags & NV_CPB_RESP_ATA_ERR) {
-               ata_port_printk(ap, KERN_ERR, "CPB flags ATA err, flags=0x%x\n", flags);
-               have_err = 1;
-               complete = 1;
-       }
-       if (flags & NV_CPB_RESP_CMD_ERR) {
-               ata_port_printk(ap, KERN_ERR, "CPB flags CMD err, flags=0x%x\n", flags);
-               have_err = 1;
-               complete = 1;
-       }
-       if (flags & NV_CPB_RESP_CPB_ERR) {
-               ata_port_printk(ap, KERN_ERR, "CPB flags CPB err, flags=0x%x\n", flags);
-               have_err = 1;
-               complete = 1;
+       if (unlikely((force_err ||
+                    flags & (NV_CPB_RESP_ATA_ERR |
+                             NV_CPB_RESP_CMD_ERR |
+                             NV_CPB_RESP_CPB_ERR)))) {
+               struct ata_eh_info *ehi = &ap->eh_info;
+               int freeze = 0;
+
+               ata_ehi_clear_desc(ehi);
+               ata_ehi_push_desc(ehi, "CPB resp_flags 0x%x", flags );
+               if (flags & NV_CPB_RESP_ATA_ERR) {
+                       ata_ehi_push_desc(ehi, ": ATA error");
+                       ehi->err_mask |= AC_ERR_DEV;
+               } else if (flags & NV_CPB_RESP_CMD_ERR) {
+                       ata_ehi_push_desc(ehi, ": CMD error");
+                       ehi->err_mask |= AC_ERR_DEV;
+               } else if (flags & NV_CPB_RESP_CPB_ERR) {
+                       ata_ehi_push_desc(ehi, ": CPB error");
+                       ehi->err_mask |= AC_ERR_SYSTEM;
+                       freeze = 1;
+               } else {
+                       /* notifier error, but no error in CPB flags? */
+                       ehi->err_mask |= AC_ERR_OTHER;
+                       freeze = 1;
+               }
+               /* Kill all commands. EH will determine what actually failed. */
+               if (freeze)
+                       ata_port_freeze(ap);
+               else
+                       ata_port_abort(ap);
+               return 1;
        }
-       if(complete || force_err)
-       {
+
+       if (flags & NV_CPB_RESP_DONE) {
                struct ata_queued_cmd *qc = ata_qc_from_tag(ap, cpb_num);
-               if(likely(qc)) {
-                       u8 ata_status = 0;
-                       /* Only use the ATA port status for non-NCQ commands.
+               VPRINTK("CPB flags done, flags=0x%x\n", flags);
+               if (likely(qc)) {
+                       /* Grab the ATA port status for non-NCQ commands.
                           For NCQ commands the current status may have nothing to do with
                           the command just completed. */
-                       if(qc->tf.protocol != ATA_PROT_NCQ)
-                               ata_status = readb(pp->ctl_block + (ATA_REG_STATUS * 4));
-
-                       if(have_err || force_err)
-                               ata_status |= ATA_ERR;
-
-                       qc->err_mask |= ac_err_mask(ata_status);
+                       if (qc->tf.protocol != ATA_PROT_NCQ) {
+                               u8 ata_status = readb(pp->ctl_block + (ATA_REG_STATUS * 4));
+                               qc->err_mask |= ac_err_mask(ata_status);
+                       }
                        DPRINTK("Completing qc from tag %d with err_mask %u\n",cpb_num,
                                qc->err_mask);
                        ata_qc_complete(qc);
                }
        }
+       return 0;
 }
 
 static int nv_host_intr(struct ata_port *ap, u8 irq_stat)
@@ -733,7 +787,6 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance)
                        void __iomem *mmio = pp->ctl_block;
                        u16 status;
                        u32 gen_ctl;
-                       int have_global_err = 0;
                        u32 notifier, notifier_error;
 
                        /* if in ATA register mode, use standard ata interrupt handler */
@@ -769,42 +822,54 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance)
                        readw(mmio + NV_ADMA_STAT); /* flush posted write */
                        rmb();
 
-                       /* freeze if hotplugged */
-                       if (unlikely(status & (NV_ADMA_STAT_HOTPLUG | NV_ADMA_STAT_HOTUNPLUG))) {
-                               ata_port_printk(ap, KERN_NOTICE, "Hotplug event, freezing\n");
+                       handled++; /* irq handled if we got here */
+
+                       /* freeze if hotplugged or controller error */
+                       if (unlikely(status & (NV_ADMA_STAT_HOTPLUG |
+                                              NV_ADMA_STAT_HOTUNPLUG |
+                                              NV_ADMA_STAT_TIMEOUT |
+                                              NV_ADMA_STAT_SERROR))) {
+                               struct ata_eh_info *ehi = &ap->eh_info;
+
+                               ata_ehi_clear_desc(ehi);
+                               ata_ehi_push_desc(ehi, "ADMA status 0x%08x", status );
+                               if (status & NV_ADMA_STAT_TIMEOUT) {
+                                       ehi->err_mask |= AC_ERR_SYSTEM;
+                                       ata_ehi_push_desc(ehi, ": timeout");
+                               } else if (status & NV_ADMA_STAT_HOTPLUG) {
+                                       ata_ehi_hotplugged(ehi);
+                                       ata_ehi_push_desc(ehi, ": hotplug");
+                               } else if (status & NV_ADMA_STAT_HOTUNPLUG) {
+                                       ata_ehi_hotplugged(ehi);
+                                       ata_ehi_push_desc(ehi, ": hot unplug");
+                               } else if (status & NV_ADMA_STAT_SERROR) {
+                                       /* let libata analyze SError and figure out the cause */
+                                       ata_ehi_push_desc(ehi, ": SError");
+                               }
                                ata_port_freeze(ap);
-                               handled++;
                                continue;
                        }
 
-                       if (status & NV_ADMA_STAT_TIMEOUT) {
-                               ata_port_printk(ap, KERN_ERR, "timeout, stat=0x%x\n", status);
-                               have_global_err = 1;
-                       }
-                       if (status & NV_ADMA_STAT_CPBERR) {
-                               ata_port_printk(ap, KERN_ERR, "CPB error, stat=0x%x\n", status);
-                               have_global_err = 1;
-                       }
-                       if ((status & NV_ADMA_STAT_DONE) || have_global_err) {
+                       if (status & (NV_ADMA_STAT_DONE |
+                                     NV_ADMA_STAT_CPBERR)) {
                                /** Check CPBs for completed commands */
 
-                               if(ata_tag_valid(ap->active_tag))
+                               if (ata_tag_valid(ap->active_tag)) {
                                        /* Non-NCQ command */
-                                       nv_adma_check_cpb(ap, ap->active_tag, have_global_err ||
-                                               (notifier_error & (1 << ap->active_tag)));
-                               else {
-                                       int pos;
+                                       nv_adma_check_cpb(ap, ap->active_tag,
+                                               notifier_error & (1 << ap->active_tag));
+                               else {
+                                       int pos, error = 0;
                                        u32 active = ap->sactive;
-                                       while( (pos = ffs(active)) ) {
+
+                                       while ((pos = ffs(active)) && !error) {
                                                pos--;
-                                               nv_adma_check_cpb(ap, pos, have_global_err ||
-                                                       (notifier_error & (1 << pos)) );
+                                               error = nv_adma_check_cpb(ap, pos,
+                                                       notifier_error & (1 << pos) );
                                                active &= ~(1 << pos );
                                        }
                                }
                        }
-
-                       handled++; /* irq handled if we got here */
                }
        }
 
@@ -1135,16 +1200,31 @@ static void nv_adma_fill_sg(struct ata_queued_cmd *qc, struct nv_adma_cpb *cpb)
                cpb->next_aprd = cpu_to_le64(((u64)(pp->aprd_dma + NV_ADMA_SGTBL_SZ * qc->tag)));
 }
 
+static int nv_adma_use_reg_mode(struct ata_queued_cmd *qc)
+{
+       struct nv_adma_port_priv *pp = qc->ap->private_data;
+
+       /* ADMA engine can only be used for non-ATAPI DMA commands,
+          or interrupt-driven no-data commands. */
+       if((pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) ||
+          (qc->tf.flags & ATA_TFLAG_POLLING))
+               return 1;
+
+       if((qc->flags & ATA_QCFLAG_DMAMAP) ||
+          (qc->tf.protocol == ATA_PROT_NODATA))
+               return 0;
+
+       return 1;
+}
+
 static void nv_adma_qc_prep(struct ata_queued_cmd *qc)
 {
        struct nv_adma_port_priv *pp = qc->ap->private_data;
        struct nv_adma_cpb *cpb = &pp->cpb[qc->tag];
        u8 ctl_flags = NV_CPB_CTL_CPB_VALID |
-                      NV_CPB_CTL_APRD_VALID |
                       NV_CPB_CTL_IEN;
 
-       if (!(qc->flags & ATA_QCFLAG_DMAMAP) ||
-            (pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE)) {
+       if (nv_adma_use_reg_mode(qc)) {
                nv_adma_register_mode(qc->ap);
                ata_qc_prep(qc);
                return;
@@ -1164,7 +1244,11 @@ static void nv_adma_qc_prep(struct ata_queued_cmd *qc)
 
        nv_adma_tf_to_cpb(&qc->tf, cpb->tf);
 
-       nv_adma_fill_sg(qc, cpb);
+       if(qc->flags & ATA_QCFLAG_DMAMAP) {
+               nv_adma_fill_sg(qc, cpb);
+               ctl_flags |= NV_CPB_CTL_APRD_VALID;
+       } else
+               memset(&cpb->aprd[0], 0, sizeof(struct nv_adma_prd) * 5);
 
        /* Be paranoid and don't let the device see NV_CPB_CTL_CPB_VALID until we are
           finished filling in all of the contents */
@@ -1179,10 +1263,9 @@ static unsigned int nv_adma_qc_issue(struct ata_queued_cmd *qc)
 
        VPRINTK("ENTER\n");
 
-       if (!(qc->flags & ATA_QCFLAG_DMAMAP) ||
-            (pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE)) {
+       if (nv_adma_use_reg_mode(qc)) {
                /* use ATA register mode */
-               VPRINTK("no dmamap or ATAPI, using ATA register mode: 0x%lx\n", qc->flags);
+               VPRINTK("using ATA register mode: 0x%lx\n", qc->flags);
                nv_adma_register_mode(qc->ap);
                return ata_qc_issue_prot(qc);
        } else
@@ -1364,28 +1447,9 @@ static void nv_adma_error_handler(struct ata_port *ap)
                int i;
                u16 tmp;
 
-               u32 notifier = readl(mmio + NV_ADMA_NOTIFIER);
-               u32 notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR);
-               u32 gen_ctl = readl(pp->gen_block + NV_ADMA_GEN_CTL);
-               u32 status = readw(mmio + NV_ADMA_STAT);
-
-               ata_port_printk(ap, KERN_ERR, "EH in ADMA mode, notifier 0x%X "
-                       "notifier_error 0x%X gen_ctl 0x%X status 0x%X\n",
-                       notifier, notifier_error, gen_ctl, status);
-
-               for( i=0;i<NV_ADMA_MAX_CPBS;i++) {
-                       struct nv_adma_cpb *cpb = &pp->cpb[i];
-                       if( cpb->ctl_flags || cpb->resp_flags )
-                               ata_port_printk(ap, KERN_ERR,
-                                       "CPB %d: ctl_flags 0x%x, resp_flags 0x%x\n",
-                                       i, cpb->ctl_flags, cpb->resp_flags);
-               }
-
                /* Push us back into port register mode for error handling. */
                nv_adma_register_mode(ap);
 
-               ata_port_printk(ap, KERN_ERR, "Resetting port\n");
-
                /* Mark all of the CPBs as invalid to prevent them from being executed */
                for( i=0;i<NV_ADMA_MAX_CPBS;i++)
                        pp->cpb[i].ctl_flags &= ~NV_CPB_CTL_CPB_VALID;
@@ -1515,8 +1579,11 @@ static int nv_pci_device_resume(struct pci_dev *pdev)
 {
        struct ata_host *host = dev_get_drvdata(&pdev->dev);
        struct nv_host_priv *hpriv = host->private_data;
+       int rc;
 
-       ata_pci_device_do_resume(pdev);
+       rc = ata_pci_device_do_resume(pdev);
+       if(rc)
+               return rc;
 
        if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
                if(hpriv->type >= CK804) {