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 /drivers/ata | |
| 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>
Diffstat (limited to 'drivers/ata')
| -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 */ |
