diff options
author | Tejun Heo <htejun@gmail.com> | 2006-04-11 09:32:19 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-04-11 13:31:36 -0400 |
commit | 37024e8ee0d8dbcd0c2634192cb3836549db054e (patch) | |
tree | 681276204a0bc13833e1b48eec5b43aa3f3d865e /drivers/scsi/sata_sil24.c | |
parent | 9466d85bb2c80b1aa169dda638b535f2f19714e4 (diff) |
[PATCH] sata_sil24: implement loss of completion interrupt on PCI-X errta fix
SiI3124 might lose completion interrupt if completion interrupt occurs
shortly after SLOT_STAT register is read for the previous completion
interrupt if it is operating in PCI-X mode.
This currently doesn't trigger as libata never queues more than one
command, but it will with NCQ changes. This patch implements the
workaround - turning on WoC and explicitly clearing interrupt.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/scsi/sata_sil24.c')
-rw-r--r-- | drivers/scsi/sata_sil24.c | 27 |
1 files changed, 25 insertions, 2 deletions
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c index 50c2c6bcba6e..c535607e995a 100644 --- a/drivers/scsi/sata_sil24.c +++ b/drivers/scsi/sata_sil24.c | |||
@@ -221,6 +221,7 @@ enum { | |||
221 | /* host flags */ | 221 | /* host flags */ |
222 | SIL24_COMMON_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | | 222 | SIL24_COMMON_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | |
223 | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA, | 223 | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA, |
224 | SIL24_FLAG_PCIX_IRQ_WOC = (1 << 24), /* IRQ loss errata on PCI-X */ | ||
224 | 225 | ||
225 | IRQ_STAT_4PORTS = 0xf, | 226 | IRQ_STAT_4PORTS = 0xf, |
226 | }; | 227 | }; |
@@ -348,7 +349,8 @@ static struct ata_port_info sil24_port_info[] = { | |||
348 | /* sil_3124 */ | 349 | /* sil_3124 */ |
349 | { | 350 | { |
350 | .sht = &sil24_sht, | 351 | .sht = &sil24_sht, |
351 | .host_flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(4), | 352 | .host_flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(4) | |
353 | SIL24_FLAG_PCIX_IRQ_WOC, | ||
352 | .pio_mask = 0x1f, /* pio0-4 */ | 354 | .pio_mask = 0x1f, /* pio0-4 */ |
353 | .mwdma_mask = 0x07, /* mwdma0-2 */ | 355 | .mwdma_mask = 0x07, /* mwdma0-2 */ |
354 | .udma_mask = 0x3f, /* udma0-5 */ | 356 | .udma_mask = 0x3f, /* udma0-5 */ |
@@ -737,6 +739,10 @@ static inline void sil24_host_intr(struct ata_port *ap) | |||
737 | slot_stat = readl(port + PORT_SLOT_STAT); | 739 | slot_stat = readl(port + PORT_SLOT_STAT); |
738 | if (!(slot_stat & HOST_SSTAT_ATTN)) { | 740 | if (!(slot_stat & HOST_SSTAT_ATTN)) { |
739 | struct sil24_port_priv *pp = ap->private_data; | 741 | struct sil24_port_priv *pp = ap->private_data; |
742 | |||
743 | if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC) | ||
744 | writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT); | ||
745 | |||
740 | /* | 746 | /* |
741 | * !HOST_SSAT_ATTN guarantees successful completion, | 747 | * !HOST_SSAT_ATTN guarantees successful completion, |
742 | * so reading back tf registers is unnecessary for | 748 | * so reading back tf registers is unnecessary for |
@@ -868,6 +874,7 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
868 | void __iomem *host_base = NULL; | 874 | void __iomem *host_base = NULL; |
869 | void __iomem *port_base = NULL; | 875 | void __iomem *port_base = NULL; |
870 | int i, rc; | 876 | int i, rc; |
877 | u32 tmp; | ||
871 | 878 | ||
872 | if (!printed_version++) | 879 | if (!printed_version++) |
873 | dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); | 880 | dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); |
@@ -941,13 +948,23 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
941 | /* GPIO off */ | 948 | /* GPIO off */ |
942 | writel(0, host_base + HOST_FLASH_CMD); | 949 | writel(0, host_base + HOST_FLASH_CMD); |
943 | 950 | ||
951 | /* Apply workaround for completion IRQ loss on PCI-X errata */ | ||
952 | if (probe_ent->host_flags & SIL24_FLAG_PCIX_IRQ_WOC) { | ||
953 | tmp = readl(host_base + HOST_CTRL); | ||
954 | if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL)) | ||
955 | dev_printk(KERN_INFO, &pdev->dev, | ||
956 | "Applying completion IRQ loss on PCI-X " | ||
957 | "errata fix\n"); | ||
958 | else | ||
959 | probe_ent->host_flags &= ~SIL24_FLAG_PCIX_IRQ_WOC; | ||
960 | } | ||
961 | |||
944 | /* clear global reset & mask interrupts during initialization */ | 962 | /* clear global reset & mask interrupts during initialization */ |
945 | writel(0, host_base + HOST_CTRL); | 963 | writel(0, host_base + HOST_CTRL); |
946 | 964 | ||
947 | for (i = 0; i < probe_ent->n_ports; i++) { | 965 | for (i = 0; i < probe_ent->n_ports; i++) { |
948 | void __iomem *port = port_base + i * PORT_REGS_SIZE; | 966 | void __iomem *port = port_base + i * PORT_REGS_SIZE; |
949 | unsigned long portu = (unsigned long)port; | 967 | unsigned long portu = (unsigned long)port; |
950 | u32 tmp; | ||
951 | 968 | ||
952 | probe_ent->port[i].cmd_addr = portu + PORT_PRB; | 969 | probe_ent->port[i].cmd_addr = portu + PORT_PRB; |
953 | probe_ent->port[i].scr_addr = portu + PORT_SCONTROL; | 970 | probe_ent->port[i].scr_addr = portu + PORT_SCONTROL; |
@@ -969,6 +986,12 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
969 | "failed to clear port RST\n"); | 986 | "failed to clear port RST\n"); |
970 | } | 987 | } |
971 | 988 | ||
989 | /* Configure IRQ WoC */ | ||
990 | if (probe_ent->host_flags & SIL24_FLAG_PCIX_IRQ_WOC) | ||
991 | writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT); | ||
992 | else | ||
993 | writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR); | ||
994 | |||
972 | /* Zero error counters. */ | 995 | /* Zero error counters. */ |
973 | writel(0x8000, port + PORT_DECODE_ERR_THRESH); | 996 | writel(0x8000, port + PORT_DECODE_ERR_THRESH); |
974 | writel(0x8000, port + PORT_CRC_ERR_THRESH); | 997 | writel(0x8000, port + PORT_CRC_ERR_THRESH); |