aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRobert Hancock <hancockr@shaw.ca>2007-05-05 17:36:36 -0400
committerJeff Garzik <jeff@garzik.org>2007-05-09 20:15:46 -0400
commit53014e2526dff68628adb11c44bd1e8f2a2a9ae9 (patch)
tree50908a1ef9967d221bc80ae32cb7c27b90ff3235 /drivers
parent7871e74acbf5013970daaa1d032854210282340c (diff)
sata_nv: fix ADMA freeze/thaw/irq_clear issues
This patch fixes some problems with ADMA-capable controllers with regard to freeze, thaw and irq_clear libata callbacks. Freeze and thaw didn't switch the ADMA-specific interrupts on or off, and more critically the irq_clear function didn't respect the restriction that the notifier clear registers for both ports have to be written at the same time even when only one port is being cleared. This could result in timeouts on one port when error handling (i.e. as a result of hotplug) occurred on the other port. As well, this fixes some issues in the interrupt handler: we shouldn't check any ADMA status if the port has ADMA switched off because of an ATAPI device, and it also checks to see if any ADMA interrupt has been raised even when we are in port-register mode. Signed-off-by: Robert Hancock <hancockr@shaw.ca> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ata/sata_nv.c92
1 files changed, 78 insertions, 14 deletions
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index e2e795e58236..a097595d4dc7 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -257,6 +257,8 @@ static void nv_adma_port_stop(struct ata_port *ap);
257static int nv_adma_port_suspend(struct ata_port *ap, pm_message_t mesg); 257static int nv_adma_port_suspend(struct ata_port *ap, pm_message_t mesg);
258static int nv_adma_port_resume(struct ata_port *ap); 258static int nv_adma_port_resume(struct ata_port *ap);
259#endif 259#endif
260static void nv_adma_freeze(struct ata_port *ap);
261static void nv_adma_thaw(struct ata_port *ap);
260static void nv_adma_error_handler(struct ata_port *ap); 262static void nv_adma_error_handler(struct ata_port *ap);
261static void nv_adma_host_stop(struct ata_host *host); 263static void nv_adma_host_stop(struct ata_host *host);
262static void nv_adma_post_internal_cmd(struct ata_queued_cmd *qc); 264static void nv_adma_post_internal_cmd(struct ata_queued_cmd *qc);
@@ -444,8 +446,8 @@ static const struct ata_port_operations nv_adma_ops = {
444 .bmdma_status = ata_bmdma_status, 446 .bmdma_status = ata_bmdma_status,
445 .qc_prep = nv_adma_qc_prep, 447 .qc_prep = nv_adma_qc_prep,
446 .qc_issue = nv_adma_qc_issue, 448 .qc_issue = nv_adma_qc_issue,
447 .freeze = nv_ck804_freeze, 449 .freeze = nv_adma_freeze,
448 .thaw = nv_ck804_thaw, 450 .thaw = nv_adma_thaw,
449 .error_handler = nv_adma_error_handler, 451 .error_handler = nv_adma_error_handler,
450 .post_internal_cmd = nv_adma_post_internal_cmd, 452 .post_internal_cmd = nv_adma_post_internal_cmd,
451 .data_xfer = ata_data_xfer, 453 .data_xfer = ata_data_xfer,
@@ -815,8 +817,16 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance)
815 u16 status; 817 u16 status;
816 u32 gen_ctl; 818 u32 gen_ctl;
817 u32 notifier, notifier_error; 819 u32 notifier, notifier_error;
820
821 /* if ADMA is disabled, use standard ata interrupt handler */
822 if (pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) {
823 u8 irq_stat = readb(host->iomap[NV_MMIO_BAR] + NV_INT_STATUS_CK804)
824 >> (NV_INT_PORT_SHIFT * i);
825 handled += nv_host_intr(ap, irq_stat);
826 continue;
827 }
818 828
819 /* if in ATA register mode, use standard ata interrupt handler */ 829 /* if in ATA register mode, check for standard interrupts */
820 if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) { 830 if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) {
821 u8 irq_stat = readb(host->iomap[NV_MMIO_BAR] + NV_INT_STATUS_CK804) 831 u8 irq_stat = readb(host->iomap[NV_MMIO_BAR] + NV_INT_STATUS_CK804)
822 >> (NV_INT_PORT_SHIFT * i); 832 >> (NV_INT_PORT_SHIFT * i);
@@ -826,7 +836,6 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance)
826 command is active, to prevent losing interrupts. */ 836 command is active, to prevent losing interrupts. */
827 irq_stat |= NV_INT_DEV; 837 irq_stat |= NV_INT_DEV;
828 handled += nv_host_intr(ap, irq_stat); 838 handled += nv_host_intr(ap, irq_stat);
829 continue;
830 } 839 }
831 840
832 notifier = readl(mmio + NV_ADMA_NOTIFIER); 841 notifier = readl(mmio + NV_ADMA_NOTIFIER);
@@ -912,22 +921,77 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance)
912 return IRQ_RETVAL(handled); 921 return IRQ_RETVAL(handled);
913} 922}
914 923
924static void nv_adma_freeze(struct ata_port *ap)
925{
926 struct nv_adma_port_priv *pp = ap->private_data;
927 void __iomem *mmio = pp->ctl_block;
928 u16 tmp;
929
930 nv_ck804_freeze(ap);
931
932 if (pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE)
933 return;
934
935 /* clear any outstanding CK804 notifications */
936 writeb( NV_INT_ALL << (ap->port_no * NV_INT_PORT_SHIFT),
937 ap->host->iomap[NV_MMIO_BAR] + NV_INT_STATUS_CK804);
938
939 /* Disable interrupt */
940 tmp = readw(mmio + NV_ADMA_CTL);
941 writew( tmp & ~(NV_ADMA_CTL_AIEN | NV_ADMA_CTL_HOTPLUG_IEN),
942 mmio + NV_ADMA_CTL);
943 readw( mmio + NV_ADMA_CTL ); /* flush posted write */
944}
945
946static void nv_adma_thaw(struct ata_port *ap)
947{
948 struct nv_adma_port_priv *pp = ap->private_data;
949 void __iomem *mmio = pp->ctl_block;
950 u16 tmp;
951
952 nv_ck804_thaw(ap);
953
954 if (pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE)
955 return;
956
957 /* Enable interrupt */
958 tmp = readw(mmio + NV_ADMA_CTL);
959 writew( tmp | (NV_ADMA_CTL_AIEN | NV_ADMA_CTL_HOTPLUG_IEN),
960 mmio + NV_ADMA_CTL);
961 readw( mmio + NV_ADMA_CTL ); /* flush posted write */
962}
963
915static void nv_adma_irq_clear(struct ata_port *ap) 964static void nv_adma_irq_clear(struct ata_port *ap)
916{ 965{
917 struct nv_adma_port_priv *pp = ap->private_data; 966 struct nv_adma_port_priv *pp = ap->private_data;
918 void __iomem *mmio = pp->ctl_block; 967 void __iomem *mmio = pp->ctl_block;
919 u16 status = readw(mmio + NV_ADMA_STAT); 968 u32 notifier_clears[2];
920 u32 notifier = readl(mmio + NV_ADMA_NOTIFIER);
921 u32 notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR);
922 void __iomem *dma_stat_addr = ap->ioaddr.bmdma_addr + ATA_DMA_STATUS;
923 969
924 /* clear ADMA status */ 970 if (pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) {
925 writew(status, mmio + NV_ADMA_STAT); 971 ata_bmdma_irq_clear(ap);
926 writel(notifier | notifier_error, 972 return;
927 pp->notifier_clear_block); 973 }
974
975 /* clear any outstanding CK804 notifications */
976 writeb( NV_INT_ALL << (ap->port_no * NV_INT_PORT_SHIFT),
977 ap->host->iomap[NV_MMIO_BAR] + NV_INT_STATUS_CK804);
928 978
929 /** clear legacy status */ 979 /* clear ADMA status */
930 iowrite8(ioread8(dma_stat_addr), dma_stat_addr); 980 writew(0xffff, mmio + NV_ADMA_STAT);
981
982 /* clear notifiers - note both ports need to be written with
983 something even though we are only clearing on one */
984 if (ap->port_no == 0) {
985 notifier_clears[0] = 0xFFFFFFFF;
986 notifier_clears[1] = 0;
987 } else {
988 notifier_clears[0] = 0;
989 notifier_clears[1] = 0xFFFFFFFF;
990 }
991 pp = ap->host->ports[0]->private_data;
992 writel(notifier_clears[0], pp->notifier_clear_block);
993 pp = ap->host->ports[1]->private_data;
994 writel(notifier_clears[1], pp->notifier_clear_block);
931} 995}
932 996
933static void nv_adma_post_internal_cmd(struct ata_queued_cmd *qc) 997static void nv_adma_post_internal_cmd(struct ata_queued_cmd *qc)