aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Hancock <hancockr@shaw.ca>2007-02-05 19:26:01 -0500
committerJeff Garzik <jeff@garzik.org>2007-02-09 17:39:39 -0500
commit5bd28a4b6efa73c5d033f3b86201c2c366b170cf (patch)
tree8e58be022a3185ac8d5bc8a73c8671903bd5be31
parent92ae78493f5f3de323652f3ea0ec8b7b2839c3d2 (diff)
sata_nv: cleanup ADMA error handling
This cleans up a few issues with the error handling in sata_nv in ADMA mode to make it more consistent with other NCQ-capable drivers like ahci and sata_sil24: - When a command failed, we would effectively set AC_ERR_DEV on the queued command always. In the case of NCQ commands this prevents libata from doing a log page query to determine the details of the failed command, since it thinks we've already analyzed. Just set flags in the port ehi->err_mask, then freeze or abort and let libata figure out what went wrong. - The code handled NV_ADMA_STAT_CPBERR as a "really bad error" which caused it to set error flags on every queued command. I don't know exactly what this flag means (no docs, grr!) but from what I can guess from the standard ADMA spec, it just means that one or more of the CPBs had an error, so we just need to go through and do our normal checks in this case. - In the error_handler function the code would always dump the state of all the CPBs. This output seems redundant at this point since libata already dumps the state of all active commands on errors (and it also triggers at times when it shouldn't, like when suspending). Take this out. [akpm@osdl.org: many coding-style fixes] Signed-off-by: Robert Hancock <hancockr@shaw.ca> Cc: Jeff Garzik <jeff@garzik.org> Cc: Tejun Heo <htejun@gmail.com> Cc: Allen Martin <AMartin@nvidia.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r--drivers/ata/sata_nv.c147
1 files changed, 72 insertions, 75 deletions
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 77e47b74ae96..3dd5ca16a2ba 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -651,53 +651,62 @@ static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, __le16 *cpb)
651 return idx; 651 return idx;
652} 652}
653 653
654static void nv_adma_check_cpb(struct ata_port *ap, int cpb_num, int force_err) 654static int nv_adma_check_cpb(struct ata_port *ap, int cpb_num, int force_err)
655{ 655{
656 struct nv_adma_port_priv *pp = ap->private_data; 656 struct nv_adma_port_priv *pp = ap->private_data;
657 int complete = 0, have_err = 0;
658 u8 flags = pp->cpb[cpb_num].resp_flags; 657 u8 flags = pp->cpb[cpb_num].resp_flags;
659 658
660 VPRINTK("CPB %d, flags=0x%x\n", cpb_num, flags); 659 VPRINTK("CPB %d, flags=0x%x\n", cpb_num, flags);
661 660
662 if (flags & NV_CPB_RESP_DONE) { 661 if (unlikely((force_err ||
663 VPRINTK("CPB flags done, flags=0x%x\n", flags); 662 flags & (NV_CPB_RESP_ATA_ERR |
664 complete = 1; 663 NV_CPB_RESP_CMD_ERR |
665 } 664 NV_CPB_RESP_CPB_ERR)))) {
666 if (flags & NV_CPB_RESP_ATA_ERR) { 665 struct ata_eh_info *ehi = &ap->eh_info;
667 ata_port_printk(ap, KERN_ERR, "CPB flags ATA err, flags=0x%x\n", flags); 666 int freeze = 0;
668 have_err = 1; 667
669 complete = 1; 668 ata_ehi_clear_desc(ehi);
670 } 669 ata_ehi_push_desc(ehi, "CPB resp_flags 0x%x", flags );
671 if (flags & NV_CPB_RESP_CMD_ERR) { 670 if (flags & NV_CPB_RESP_ATA_ERR) {
672 ata_port_printk(ap, KERN_ERR, "CPB flags CMD err, flags=0x%x\n", flags); 671 ata_ehi_push_desc(ehi, ": ATA error");
673 have_err = 1; 672 ehi->err_mask |= AC_ERR_DEV;
674 complete = 1; 673 } else if (flags & NV_CPB_RESP_CMD_ERR) {
675 } 674 ata_ehi_push_desc(ehi, ": CMD error");
676 if (flags & NV_CPB_RESP_CPB_ERR) { 675 ehi->err_mask |= AC_ERR_DEV;
677 ata_port_printk(ap, KERN_ERR, "CPB flags CPB err, flags=0x%x\n", flags); 676 } else if (flags & NV_CPB_RESP_CPB_ERR) {
678 have_err = 1; 677 ata_ehi_push_desc(ehi, ": CPB error");
679 complete = 1; 678 ehi->err_mask |= AC_ERR_SYSTEM;
679 freeze = 1;
680 } else {
681 /* notifier error, but no error in CPB flags? */
682 ehi->err_mask |= AC_ERR_OTHER;
683 freeze = 1;
684 }
685 /* Kill all commands. EH will determine what actually failed. */
686 if (freeze)
687 ata_port_freeze(ap);
688 else
689 ata_port_abort(ap);
690 return 1;
680 } 691 }
681 if(complete || force_err) 692
682 { 693 if (flags & NV_CPB_RESP_DONE) {
683 struct ata_queued_cmd *qc = ata_qc_from_tag(ap, cpb_num); 694 struct ata_queued_cmd *qc = ata_qc_from_tag(ap, cpb_num);
684 if(likely(qc)) { 695 VPRINTK("CPB flags done, flags=0x%x\n", flags);
685 u8 ata_status = 0; 696 if (likely(qc)) {
686 /* Only use the ATA port status for non-NCQ commands. 697 /* Grab the ATA port status for non-NCQ commands.
687 For NCQ commands the current status may have nothing to do with 698 For NCQ commands the current status may have nothing to do with
688 the command just completed. */ 699 the command just completed. */
689 if(qc->tf.protocol != ATA_PROT_NCQ) 700 if (qc->tf.protocol != ATA_PROT_NCQ) {
690 ata_status = readb(pp->ctl_block + (ATA_REG_STATUS * 4)); 701 u8 ata_status = readb(pp->ctl_block + (ATA_REG_STATUS * 4));
691 702 qc->err_mask |= ac_err_mask(ata_status);
692 if(have_err || force_err) 703 }
693 ata_status |= ATA_ERR;
694
695 qc->err_mask |= ac_err_mask(ata_status);
696 DPRINTK("Completing qc from tag %d with err_mask %u\n",cpb_num, 704 DPRINTK("Completing qc from tag %d with err_mask %u\n",cpb_num,
697 qc->err_mask); 705 qc->err_mask);
698 ata_qc_complete(qc); 706 ata_qc_complete(qc);
699 } 707 }
700 } 708 }
709 return 0;
701} 710}
702 711
703static int nv_host_intr(struct ata_port *ap, u8 irq_stat) 712static int nv_host_intr(struct ata_port *ap, u8 irq_stat)
@@ -741,7 +750,6 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance)
741 void __iomem *mmio = pp->ctl_block; 750 void __iomem *mmio = pp->ctl_block;
742 u16 status; 751 u16 status;
743 u32 gen_ctl; 752 u32 gen_ctl;
744 int have_global_err = 0;
745 u32 notifier, notifier_error; 753 u32 notifier, notifier_error;
746 754
747 /* if in ATA register mode, use standard ata interrupt handler */ 755 /* if in ATA register mode, use standard ata interrupt handler */
@@ -777,42 +785,50 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance)
777 readw(mmio + NV_ADMA_STAT); /* flush posted write */ 785 readw(mmio + NV_ADMA_STAT); /* flush posted write */
778 rmb(); 786 rmb();
779 787
780 /* freeze if hotplugged */ 788 handled++; /* irq handled if we got here */
781 if (unlikely(status & (NV_ADMA_STAT_HOTPLUG | NV_ADMA_STAT_HOTUNPLUG))) { 789
782 ata_port_printk(ap, KERN_NOTICE, "Hotplug event, freezing\n"); 790 /* freeze if hotplugged or controller error */
791 if (unlikely(status & (NV_ADMA_STAT_HOTPLUG |
792 NV_ADMA_STAT_HOTUNPLUG |
793 NV_ADMA_STAT_TIMEOUT))) {
794 struct ata_eh_info *ehi = &ap->eh_info;
795
796 ata_ehi_clear_desc(ehi);
797 ata_ehi_push_desc(ehi, "ADMA status 0x%08x", status );
798 if (status & NV_ADMA_STAT_TIMEOUT) {
799 ehi->err_mask |= AC_ERR_SYSTEM;
800 ata_ehi_push_desc(ehi, ": timeout");
801 } else if (status & NV_ADMA_STAT_HOTPLUG) {
802 ata_ehi_hotplugged(ehi);
803 ata_ehi_push_desc(ehi, ": hotplug");
804 } else if (status & NV_ADMA_STAT_HOTUNPLUG) {
805 ata_ehi_hotplugged(ehi);
806 ata_ehi_push_desc(ehi, ": hot unplug");
807 }
783 ata_port_freeze(ap); 808 ata_port_freeze(ap);
784 handled++;
785 continue; 809 continue;
786 } 810 }
787 811
788 if (status & NV_ADMA_STAT_TIMEOUT) { 812 if (status & (NV_ADMA_STAT_DONE |
789 ata_port_printk(ap, KERN_ERR, "timeout, stat=0x%x\n", status); 813 NV_ADMA_STAT_CPBERR)) {
790 have_global_err = 1;
791 }
792 if (status & NV_ADMA_STAT_CPBERR) {
793 ata_port_printk(ap, KERN_ERR, "CPB error, stat=0x%x\n", status);
794 have_global_err = 1;
795 }
796 if ((status & NV_ADMA_STAT_DONE) || have_global_err) {
797 /** Check CPBs for completed commands */ 814 /** Check CPBs for completed commands */
798 815
799 if(ata_tag_valid(ap->active_tag)) 816 if (ata_tag_valid(ap->active_tag)) {
800 /* Non-NCQ command */ 817 /* Non-NCQ command */
801 nv_adma_check_cpb(ap, ap->active_tag, have_global_err || 818 nv_adma_check_cpb(ap, ap->active_tag,
802 (notifier_error & (1 << ap->active_tag))); 819 notifier_error & (1 << ap->active_tag));
803 else { 820 } else {
804 int pos; 821 int pos, error = 0;
805 u32 active = ap->sactive; 822 u32 active = ap->sactive;
806 while( (pos = ffs(active)) ) { 823
824 while ((pos = ffs(active)) && !error) {
807 pos--; 825 pos--;
808 nv_adma_check_cpb(ap, pos, have_global_err || 826 error = nv_adma_check_cpb(ap, pos,
809 (notifier_error & (1 << pos)) ); 827 notifier_error & (1 << pos) );
810 active &= ~(1 << pos ); 828 active &= ~(1 << pos );
811 } 829 }
812 } 830 }
813 } 831 }
814
815 handled++; /* irq handled if we got here */
816 } 832 }
817 } 833 }
818 834
@@ -1372,28 +1388,9 @@ static void nv_adma_error_handler(struct ata_port *ap)
1372 int i; 1388 int i;
1373 u16 tmp; 1389 u16 tmp;
1374 1390
1375 u32 notifier = readl(mmio + NV_ADMA_NOTIFIER);
1376 u32 notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR);
1377 u32 gen_ctl = readl(pp->gen_block + NV_ADMA_GEN_CTL);
1378 u32 status = readw(mmio + NV_ADMA_STAT);
1379
1380 ata_port_printk(ap, KERN_ERR, "EH in ADMA mode, notifier 0x%X "
1381 "notifier_error 0x%X gen_ctl 0x%X status 0x%X\n",
1382 notifier, notifier_error, gen_ctl, status);
1383
1384 for( i=0;i<NV_ADMA_MAX_CPBS;i++) {
1385 struct nv_adma_cpb *cpb = &pp->cpb[i];
1386 if( cpb->ctl_flags || cpb->resp_flags )
1387 ata_port_printk(ap, KERN_ERR,
1388 "CPB %d: ctl_flags 0x%x, resp_flags 0x%x\n",
1389 i, cpb->ctl_flags, cpb->resp_flags);
1390 }
1391
1392 /* Push us back into port register mode for error handling. */ 1391 /* Push us back into port register mode for error handling. */
1393 nv_adma_register_mode(ap); 1392 nv_adma_register_mode(ap);
1394 1393
1395 ata_port_printk(ap, KERN_ERR, "Resetting port\n");
1396
1397 /* Mark all of the CPBs as invalid to prevent them from being executed */ 1394 /* Mark all of the CPBs as invalid to prevent them from being executed */
1398 for( i=0;i<NV_ADMA_MAX_CPBS;i++) 1395 for( i=0;i<NV_ADMA_MAX_CPBS;i++)
1399 pp->cpb[i].ctl_flags &= ~NV_CPB_CTL_CPB_VALID; 1396 pp->cpb[i].ctl_flags &= ~NV_CPB_CTL_CPB_VALID;