diff options
author | Tejun Heo <htejun@gmail.com> | 2007-09-23 00:19:54 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-10-12 14:55:44 -0400 |
commit | 238180343eff95697ed71eea137cf61ba3cea6ad (patch) | |
tree | e7beaf26a9181e26e3b7bbf57dc8f321ef2949c7 | |
parent | 3454dc6922dc550c0d3ccf292c4e227403b10b6e (diff) |
sata_sil24: implement PORT_RST
As DEV_RST (hardreset) sometimes fail to recover the controller
(especially after PMP DMA CS errata). In such cases, perform PORT_RST
prior to DEV_RST.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r-- | drivers/ata/sata_sil24.c | 93 |
1 files changed, 70 insertions, 23 deletions
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index 03bfbb65c533..15b9a80a1782 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c | |||
@@ -323,6 +323,7 @@ struct sil24_port_priv { | |||
323 | union sil24_cmd_block *cmd_block; /* 32 cmd blocks */ | 323 | union sil24_cmd_block *cmd_block; /* 32 cmd blocks */ |
324 | dma_addr_t cmd_block_dma; /* DMA base addr for them */ | 324 | dma_addr_t cmd_block_dma; /* DMA base addr for them */ |
325 | struct ata_taskfile tf; /* Cached taskfile registers */ | 325 | struct ata_taskfile tf; /* Cached taskfile registers */ |
326 | int do_port_rst; | ||
326 | }; | 327 | }; |
327 | 328 | ||
328 | static void sil24_dev_config(struct ata_device *dev); | 329 | static void sil24_dev_config(struct ata_device *dev); |
@@ -536,6 +537,31 @@ static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf) | |||
536 | *tf = pp->tf; | 537 | *tf = pp->tf; |
537 | } | 538 | } |
538 | 539 | ||
540 | static void sil24_config_port(struct ata_port *ap) | ||
541 | { | ||
542 | void __iomem *port = ap->ioaddr.cmd_addr; | ||
543 | |||
544 | /* configure IRQ WoC */ | ||
545 | if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC) | ||
546 | writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT); | ||
547 | else | ||
548 | writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR); | ||
549 | |||
550 | /* zero error counters. */ | ||
551 | writel(0x8000, port + PORT_DECODE_ERR_THRESH); | ||
552 | writel(0x8000, port + PORT_CRC_ERR_THRESH); | ||
553 | writel(0x8000, port + PORT_HSHK_ERR_THRESH); | ||
554 | writel(0x0000, port + PORT_DECODE_ERR_CNT); | ||
555 | writel(0x0000, port + PORT_CRC_ERR_CNT); | ||
556 | writel(0x0000, port + PORT_HSHK_ERR_CNT); | ||
557 | |||
558 | /* always use 64bit activation */ | ||
559 | writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR); | ||
560 | |||
561 | /* clear port multiplier enable and resume bits */ | ||
562 | writel(PORT_CS_PMP_EN | PORT_CS_PMP_RESUME, port + PORT_CTRL_CLR); | ||
563 | } | ||
564 | |||
539 | static void sil24_config_pmp(struct ata_port *ap, int attached) | 565 | static void sil24_config_pmp(struct ata_port *ap, int attached) |
540 | { | 566 | { |
541 | void __iomem *port = ap->ioaddr.cmd_addr; | 567 | void __iomem *port = ap->ioaddr.cmd_addr; |
@@ -564,6 +590,7 @@ static void sil24_clear_pmp(struct ata_port *ap) | |||
564 | static int sil24_init_port(struct ata_port *ap) | 590 | static int sil24_init_port(struct ata_port *ap) |
565 | { | 591 | { |
566 | void __iomem *port = ap->ioaddr.cmd_addr; | 592 | void __iomem *port = ap->ioaddr.cmd_addr; |
593 | struct sil24_port_priv *pp = ap->private_data; | ||
567 | u32 tmp; | 594 | u32 tmp; |
568 | 595 | ||
569 | /* clear PMP error status */ | 596 | /* clear PMP error status */ |
@@ -576,8 +603,12 @@ static int sil24_init_port(struct ata_port *ap) | |||
576 | tmp = ata_wait_register(port + PORT_CTRL_STAT, | 603 | tmp = ata_wait_register(port + PORT_CTRL_STAT, |
577 | PORT_CS_RDY, 0, 10, 100); | 604 | PORT_CS_RDY, 0, 10, 100); |
578 | 605 | ||
579 | if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY) | 606 | if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY) { |
607 | pp->do_port_rst = 1; | ||
608 | ap->link.eh_context.i.action |= ATA_EH_HARDRESET; | ||
580 | return -EIO; | 609 | return -EIO; |
610 | } | ||
611 | |||
581 | return 0; | 612 | return 0; |
582 | } | 613 | } |
583 | 614 | ||
@@ -692,10 +723,34 @@ static int sil24_hardreset(struct ata_link *link, unsigned int *class, | |||
692 | { | 723 | { |
693 | struct ata_port *ap = link->ap; | 724 | struct ata_port *ap = link->ap; |
694 | void __iomem *port = ap->ioaddr.cmd_addr; | 725 | void __iomem *port = ap->ioaddr.cmd_addr; |
726 | struct sil24_port_priv *pp = ap->private_data; | ||
727 | int did_port_rst = 0; | ||
695 | const char *reason; | 728 | const char *reason; |
696 | int tout_msec, rc; | 729 | int tout_msec, rc; |
697 | u32 tmp; | 730 | u32 tmp; |
698 | 731 | ||
732 | retry: | ||
733 | /* Sometimes, DEV_RST is not enough to recover the controller. | ||
734 | * This happens often after PM DMA CS errata. | ||
735 | */ | ||
736 | if (pp->do_port_rst) { | ||
737 | ata_port_printk(ap, KERN_WARNING, "controller in dubious " | ||
738 | "state, performing PORT_RST\n"); | ||
739 | |||
740 | writel(PORT_CS_PORT_RST, port + PORT_CTRL_STAT); | ||
741 | msleep(10); | ||
742 | writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR); | ||
743 | ata_wait_register(port + PORT_CTRL_STAT, PORT_CS_RDY, 0, | ||
744 | 10, 5000); | ||
745 | |||
746 | /* restore port configuration */ | ||
747 | sil24_config_port(ap); | ||
748 | sil24_config_pmp(ap, ap->nr_pmp_links); | ||
749 | |||
750 | pp->do_port_rst = 0; | ||
751 | did_port_rst = 1; | ||
752 | } | ||
753 | |||
699 | /* sil24 does the right thing(tm) without any protection */ | 754 | /* sil24 does the right thing(tm) without any protection */ |
700 | sata_set_spd(link); | 755 | sata_set_spd(link); |
701 | 756 | ||
@@ -732,6 +787,11 @@ static int sil24_hardreset(struct ata_link *link, unsigned int *class, | |||
732 | return -EAGAIN; | 787 | return -EAGAIN; |
733 | 788 | ||
734 | err: | 789 | err: |
790 | if (!did_port_rst) { | ||
791 | pp->do_port_rst = 1; | ||
792 | goto retry; | ||
793 | } | ||
794 | |||
735 | ata_link_printk(link, KERN_ERR, "hardreset failed (%s)\n", reason); | 795 | ata_link_printk(link, KERN_ERR, "hardreset failed (%s)\n", reason); |
736 | return -EIO; | 796 | return -EIO; |
737 | } | 797 | } |
@@ -997,6 +1057,7 @@ static void sil24_error_intr(struct ata_port *ap) | |||
997 | ehi->err_mask |= AC_ERR_OTHER; | 1057 | ehi->err_mask |= AC_ERR_OTHER; |
998 | ehi->action |= ATA_EH_HARDRESET; | 1058 | ehi->action |= ATA_EH_HARDRESET; |
999 | ata_ehi_push_desc(ehi, "PMP DMA CS errata"); | 1059 | ata_ehi_push_desc(ehi, "PMP DMA CS errata"); |
1060 | pp->do_port_rst = 1; | ||
1000 | freeze = 1; | 1061 | freeze = 1; |
1001 | } | 1062 | } |
1002 | 1063 | ||
@@ -1152,6 +1213,8 @@ static irqreturn_t sil24_interrupt(int irq, void *dev_instance) | |||
1152 | 1213 | ||
1153 | static void sil24_error_handler(struct ata_port *ap) | 1214 | static void sil24_error_handler(struct ata_port *ap) |
1154 | { | 1215 | { |
1216 | struct sil24_port_priv *pp = ap->private_data; | ||
1217 | |||
1155 | if (sil24_init_port(ap)) | 1218 | if (sil24_init_port(ap)) |
1156 | ata_eh_freeze_port(ap); | 1219 | ata_eh_freeze_port(ap); |
1157 | 1220 | ||
@@ -1160,6 +1223,8 @@ static void sil24_error_handler(struct ata_port *ap) | |||
1160 | ata_std_postreset, sata_pmp_std_prereset, | 1223 | ata_std_postreset, sata_pmp_std_prereset, |
1161 | sil24_pmp_softreset, sil24_pmp_hardreset, | 1224 | sil24_pmp_softreset, sil24_pmp_hardreset, |
1162 | sata_pmp_std_postreset); | 1225 | sata_pmp_std_postreset); |
1226 | |||
1227 | pp->do_port_rst = 0; | ||
1163 | } | 1228 | } |
1164 | 1229 | ||
1165 | static void sil24_post_internal_cmd(struct ata_queued_cmd *qc) | 1230 | static void sil24_post_internal_cmd(struct ata_queued_cmd *qc) |
@@ -1206,7 +1271,6 @@ static int sil24_port_start(struct ata_port *ap) | |||
1206 | static void sil24_init_controller(struct ata_host *host) | 1271 | static void sil24_init_controller(struct ata_host *host) |
1207 | { | 1272 | { |
1208 | void __iomem *host_base = host->iomap[SIL24_HOST_BAR]; | 1273 | void __iomem *host_base = host->iomap[SIL24_HOST_BAR]; |
1209 | void __iomem *port_base = host->iomap[SIL24_PORT_BAR]; | ||
1210 | u32 tmp; | 1274 | u32 tmp; |
1211 | int i; | 1275 | int i; |
1212 | 1276 | ||
@@ -1218,7 +1282,8 @@ static void sil24_init_controller(struct ata_host *host) | |||
1218 | 1282 | ||
1219 | /* init ports */ | 1283 | /* init ports */ |
1220 | for (i = 0; i < host->n_ports; i++) { | 1284 | for (i = 0; i < host->n_ports; i++) { |
1221 | void __iomem *port = port_base + i * PORT_REGS_SIZE; | 1285 | struct ata_port *ap = host->ports[i]; |
1286 | void __iomem *port = ap->ioaddr.cmd_addr; | ||
1222 | 1287 | ||
1223 | /* Initial PHY setting */ | 1288 | /* Initial PHY setting */ |
1224 | writel(0x20c, port + PORT_PHY_CFG); | 1289 | writel(0x20c, port + PORT_PHY_CFG); |
@@ -1235,26 +1300,8 @@ static void sil24_init_controller(struct ata_host *host) | |||
1235 | "failed to clear port RST\n"); | 1300 | "failed to clear port RST\n"); |
1236 | } | 1301 | } |
1237 | 1302 | ||
1238 | /* Configure IRQ WoC */ | 1303 | /* configure port */ |
1239 | if (host->ports[0]->flags & SIL24_FLAG_PCIX_IRQ_WOC) | 1304 | sil24_config_port(ap); |
1240 | writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT); | ||
1241 | else | ||
1242 | writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR); | ||
1243 | |||
1244 | /* Zero error counters. */ | ||
1245 | writel(0x8000, port + PORT_DECODE_ERR_THRESH); | ||
1246 | writel(0x8000, port + PORT_CRC_ERR_THRESH); | ||
1247 | writel(0x8000, port + PORT_HSHK_ERR_THRESH); | ||
1248 | writel(0x0000, port + PORT_DECODE_ERR_CNT); | ||
1249 | writel(0x0000, port + PORT_CRC_ERR_CNT); | ||
1250 | writel(0x0000, port + PORT_HSHK_ERR_CNT); | ||
1251 | |||
1252 | /* Always use 64bit activation */ | ||
1253 | writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR); | ||
1254 | |||
1255 | /* Clear port multiplier enable and resume bits */ | ||
1256 | writel(PORT_CS_PMP_EN | PORT_CS_PMP_RESUME, | ||
1257 | port + PORT_CTRL_CLR); | ||
1258 | } | 1305 | } |
1259 | 1306 | ||
1260 | /* Turn on interrupts */ | 1307 | /* Turn on interrupts */ |