diff options
| author | Tejun Heo <htejun@gmail.com> | 2006-05-15 07:58:32 -0400 |
|---|---|---|
| committer | Tejun Heo <htejun@gmail.com> | 2006-05-15 07:58:32 -0400 |
| commit | 88ce7550c38f46c8697f53727a571bf838bee398 (patch) | |
| tree | 58df8b1898eb2077a8fdb8c8a8ab8d00294ccbe8 | |
| parent | 2a3917a8bb40a2cb75b458da9c356e8557e8fbed (diff) | |
[PATCH] sata_sil24: convert to new EH
Convert sata_sil24 to new EH.
* When port is frozen, IRQ for the port is masked.
* sil24_softreset() doesn't need to mangle with IRQ mask anymore.
libata ensures that the port is frozen during reset.
* Only turn on interrupts which are handled by interrupt handler and
EH. As we don't handle SDB notify yet, turn it off. DEV_XCHG and
UNK_FIS are handled by EH and thus turned on.
* sil24_softreset() usually fails to recover the port after DEV_XCHG.
ATA_PORT_HARDRESET is used as recovery action for DEV_XCHG.
* sil24 may be invoked without any active command. e.g. DEV_XCHG irq
occuring while no qc in progress still triggers EH and will reset
the port and revalidate attached device.
Signed-off-by: Tejun Heo <htejun@gmail.com>
| -rw-r--r-- | drivers/scsi/sata_sil24.c | 328 |
1 files changed, 187 insertions, 141 deletions
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c index ff96724b1e38..6e7728cfaf6b 100644 --- a/drivers/scsi/sata_sil24.c +++ b/drivers/scsi/sata_sil24.c | |||
| @@ -156,6 +156,9 @@ enum { | |||
| 156 | PORT_IRQ_HANDSHAKE = (1 << 10), /* handshake error threshold */ | 156 | PORT_IRQ_HANDSHAKE = (1 << 10), /* handshake error threshold */ |
| 157 | PORT_IRQ_SDB_NOTIFY = (1 << 11), /* SDB notify received */ | 157 | PORT_IRQ_SDB_NOTIFY = (1 << 11), /* SDB notify received */ |
| 158 | 158 | ||
| 159 | DEF_PORT_IRQ = PORT_IRQ_COMPLETE | PORT_IRQ_ERROR | | ||
| 160 | PORT_IRQ_DEV_XCHG | PORT_IRQ_UNK_FIS, | ||
| 161 | |||
| 159 | /* bits[27:16] are unmasked (raw) */ | 162 | /* bits[27:16] are unmasked (raw) */ |
| 160 | PORT_IRQ_RAW_SHIFT = 16, | 163 | PORT_IRQ_RAW_SHIFT = 16, |
| 161 | PORT_IRQ_MASKED_MASK = 0x7ff, | 164 | PORT_IRQ_MASKED_MASK = 0x7ff, |
| @@ -242,6 +245,58 @@ union sil24_cmd_block { | |||
| 242 | struct sil24_atapi_block atapi; | 245 | struct sil24_atapi_block atapi; |
| 243 | }; | 246 | }; |
| 244 | 247 | ||
| 248 | static struct sil24_cerr_info { | ||
| 249 | unsigned int err_mask, action; | ||
| 250 | const char *desc; | ||
| 251 | } sil24_cerr_db[] = { | ||
| 252 | [0] = { AC_ERR_DEV, ATA_EH_REVALIDATE, | ||
| 253 | "device error" }, | ||
| 254 | [PORT_CERR_DEV] = { AC_ERR_DEV, ATA_EH_REVALIDATE, | ||
| 255 | "device error via D2H FIS" }, | ||
| 256 | [PORT_CERR_SDB] = { AC_ERR_DEV, ATA_EH_REVALIDATE, | ||
| 257 | "device error via SDB FIS" }, | ||
| 258 | [PORT_CERR_DATA] = { AC_ERR_ATA_BUS, ATA_EH_SOFTRESET, | ||
| 259 | "error in data FIS" }, | ||
| 260 | [PORT_CERR_SEND] = { AC_ERR_ATA_BUS, ATA_EH_SOFTRESET, | ||
| 261 | "failed to transmit command FIS" }, | ||
| 262 | [PORT_CERR_INCONSISTENT] = { AC_ERR_HSM, ATA_EH_SOFTRESET, | ||
| 263 | "protocol mismatch" }, | ||
| 264 | [PORT_CERR_DIRECTION] = { AC_ERR_HSM, ATA_EH_SOFTRESET, | ||
| 265 | "data directon mismatch" }, | ||
| 266 | [PORT_CERR_UNDERRUN] = { AC_ERR_HSM, ATA_EH_SOFTRESET, | ||
| 267 | "ran out of SGEs while writing" }, | ||
| 268 | [PORT_CERR_OVERRUN] = { AC_ERR_HSM, ATA_EH_SOFTRESET, | ||
| 269 | "ran out of SGEs while reading" }, | ||
| 270 | [PORT_CERR_PKT_PROT] = { AC_ERR_HSM, ATA_EH_SOFTRESET, | ||
| 271 | "invalid data directon for ATAPI CDB" }, | ||
| 272 | [PORT_CERR_SGT_BOUNDARY] = { AC_ERR_SYSTEM, ATA_EH_SOFTRESET, | ||
| 273 | "SGT no on qword boundary" }, | ||
| 274 | [PORT_CERR_SGT_TGTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, | ||
| 275 | "PCI target abort while fetching SGT" }, | ||
| 276 | [PORT_CERR_SGT_MSTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, | ||
| 277 | "PCI master abort while fetching SGT" }, | ||
| 278 | [PORT_CERR_SGT_PCIPERR] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, | ||
| 279 | "PCI parity error while fetching SGT" }, | ||
| 280 | [PORT_CERR_CMD_BOUNDARY] = { AC_ERR_SYSTEM, ATA_EH_SOFTRESET, | ||
| 281 | "PRB not on qword boundary" }, | ||
| 282 | [PORT_CERR_CMD_TGTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, | ||
| 283 | "PCI target abort while fetching PRB" }, | ||
| 284 | [PORT_CERR_CMD_MSTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, | ||
| 285 | "PCI master abort while fetching PRB" }, | ||
| 286 | [PORT_CERR_CMD_PCIPERR] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, | ||
| 287 | "PCI parity error while fetching PRB" }, | ||
| 288 | [PORT_CERR_XFR_UNDEF] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, | ||
| 289 | "undefined error while transferring data" }, | ||
| 290 | [PORT_CERR_XFR_TGTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, | ||
| 291 | "PCI target abort while transferring data" }, | ||
| 292 | [PORT_CERR_XFR_MSTABRT] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, | ||
| 293 | "PCI master abort while transferring data" }, | ||
| 294 | [PORT_CERR_XFR_PCIPERR] = { AC_ERR_HOST_BUS, ATA_EH_SOFTRESET, | ||
| 295 | "PCI parity error while transferring data" }, | ||
| 296 | [PORT_CERR_SENDSERVICE] = { AC_ERR_HSM, ATA_EH_SOFTRESET, | ||
| 297 | "FIS received while sending service FIS" }, | ||
| 298 | }; | ||
| 299 | |||
| 245 | /* | 300 | /* |
| 246 | * ap->private_data | 301 | * ap->private_data |
| 247 | * | 302 | * |
| @@ -269,8 +324,11 @@ static int sil24_probe_reset(struct ata_port *ap, unsigned int *classes); | |||
| 269 | static void sil24_qc_prep(struct ata_queued_cmd *qc); | 324 | static void sil24_qc_prep(struct ata_queued_cmd *qc); |
| 270 | static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc); | 325 | static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc); |
| 271 | static void sil24_irq_clear(struct ata_port *ap); | 326 | static void sil24_irq_clear(struct ata_port *ap); |
| 272 | static void sil24_eng_timeout(struct ata_port *ap); | ||
| 273 | static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs); | 327 | static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs); |
| 328 | static void sil24_freeze(struct ata_port *ap); | ||
| 329 | static void sil24_thaw(struct ata_port *ap); | ||
| 330 | static void sil24_error_handler(struct ata_port *ap); | ||
| 331 | static void sil24_post_internal_cmd(struct ata_queued_cmd *qc); | ||
| 274 | static int sil24_port_start(struct ata_port *ap); | 332 | static int sil24_port_start(struct ata_port *ap); |
| 275 | static void sil24_port_stop(struct ata_port *ap); | 333 | static void sil24_port_stop(struct ata_port *ap); |
| 276 | static void sil24_host_stop(struct ata_host_set *host_set); | 334 | static void sil24_host_stop(struct ata_host_set *host_set); |
| @@ -325,14 +383,17 @@ static const struct ata_port_operations sil24_ops = { | |||
| 325 | .qc_prep = sil24_qc_prep, | 383 | .qc_prep = sil24_qc_prep, |
| 326 | .qc_issue = sil24_qc_issue, | 384 | .qc_issue = sil24_qc_issue, |
| 327 | 385 | ||
| 328 | .eng_timeout = sil24_eng_timeout, | ||
| 329 | |||
| 330 | .irq_handler = sil24_interrupt, | 386 | .irq_handler = sil24_interrupt, |
| 331 | .irq_clear = sil24_irq_clear, | 387 | .irq_clear = sil24_irq_clear, |
| 332 | 388 | ||
| 333 | .scr_read = sil24_scr_read, | 389 | .scr_read = sil24_scr_read, |
| 334 | .scr_write = sil24_scr_write, | 390 | .scr_write = sil24_scr_write, |
| 335 | 391 | ||
| 392 | .freeze = sil24_freeze, | ||
| 393 | .thaw = sil24_thaw, | ||
| 394 | .error_handler = sil24_error_handler, | ||
| 395 | .post_internal_cmd = sil24_post_internal_cmd, | ||
| 396 | |||
| 336 | .port_start = sil24_port_start, | 397 | .port_start = sil24_port_start, |
| 337 | .port_stop = sil24_port_stop, | 398 | .port_stop = sil24_port_stop, |
| 338 | .host_stop = sil24_host_stop, | 399 | .host_stop = sil24_host_stop, |
| @@ -459,7 +520,7 @@ static int sil24_softreset(struct ata_port *ap, unsigned int *class) | |||
| 459 | struct sil24_port_priv *pp = ap->private_data; | 520 | struct sil24_port_priv *pp = ap->private_data; |
| 460 | struct sil24_prb *prb = &pp->cmd_block[0].ata.prb; | 521 | struct sil24_prb *prb = &pp->cmd_block[0].ata.prb; |
| 461 | dma_addr_t paddr = pp->cmd_block_dma; | 522 | dma_addr_t paddr = pp->cmd_block_dma; |
| 462 | u32 mask, irq_enable, irq_stat; | 523 | u32 mask, irq_stat; |
| 463 | const char *reason; | 524 | const char *reason; |
| 464 | 525 | ||
| 465 | DPRINTK("ENTER\n"); | 526 | DPRINTK("ENTER\n"); |
| @@ -470,10 +531,6 @@ static int sil24_softreset(struct ata_port *ap, unsigned int *class) | |||
| 470 | goto out; | 531 | goto out; |
| 471 | } | 532 | } |
| 472 | 533 | ||
| 473 | /* temporarily turn off IRQs during SRST */ | ||
| 474 | irq_enable = readl(port + PORT_IRQ_ENABLE_SET); | ||
| 475 | writel(irq_enable, port + PORT_IRQ_ENABLE_CLR); | ||
| 476 | |||
| 477 | /* put the port into known state */ | 534 | /* put the port into known state */ |
| 478 | if (sil24_init_port(ap)) { | 535 | if (sil24_init_port(ap)) { |
| 479 | reason ="port not ready"; | 536 | reason ="port not ready"; |
| @@ -494,9 +551,6 @@ static int sil24_softreset(struct ata_port *ap, unsigned int *class) | |||
| 494 | writel(irq_stat, port + PORT_IRQ_STAT); /* clear IRQs */ | 551 | writel(irq_stat, port + PORT_IRQ_STAT); /* clear IRQs */ |
| 495 | irq_stat >>= PORT_IRQ_RAW_SHIFT; | 552 | irq_stat >>= PORT_IRQ_RAW_SHIFT; |
| 496 | 553 | ||
| 497 | /* restore IRQs */ | ||
| 498 | writel(irq_enable, port + PORT_IRQ_ENABLE_SET); | ||
| 499 | |||
| 500 | if (!(irq_stat & PORT_IRQ_COMPLETE)) { | 554 | if (!(irq_stat & PORT_IRQ_COMPLETE)) { |
| 501 | if (irq_stat & PORT_IRQ_ERROR) | 555 | if (irq_stat & PORT_IRQ_ERROR) |
| 502 | reason = "SRST command error"; | 556 | reason = "SRST command error"; |
| @@ -655,158 +709,134 @@ static void sil24_irq_clear(struct ata_port *ap) | |||
| 655 | /* unused */ | 709 | /* unused */ |
| 656 | } | 710 | } |
| 657 | 711 | ||
| 658 | static int __sil24_restart_controller(void __iomem *port) | 712 | static void sil24_freeze(struct ata_port *ap) |
| 659 | { | 713 | { |
| 660 | u32 tmp; | 714 | void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; |
| 661 | int cnt; | ||
| 662 | |||
| 663 | writel(PORT_CS_INIT, port + PORT_CTRL_STAT); | ||
| 664 | |||
| 665 | /* Max ~10ms */ | ||
| 666 | for (cnt = 0; cnt < 10000; cnt++) { | ||
| 667 | tmp = readl(port + PORT_CTRL_STAT); | ||
| 668 | if (tmp & PORT_CS_RDY) | ||
| 669 | return 0; | ||
| 670 | udelay(1); | ||
| 671 | } | ||
| 672 | |||
| 673 | return -1; | ||
| 674 | } | ||
| 675 | 715 | ||
| 676 | static void sil24_restart_controller(struct ata_port *ap) | 716 | /* Port-wide IRQ mask in HOST_CTRL doesn't really work, clear |
| 677 | { | 717 | * PORT_IRQ_ENABLE instead. |
| 678 | if (__sil24_restart_controller((void __iomem *)ap->ioaddr.cmd_addr)) | 718 | */ |
| 679 | printk(KERN_ERR DRV_NAME | 719 | writel(0xffff, port + PORT_IRQ_ENABLE_CLR); |
| 680 | " ata%u: failed to restart controller\n", ap->id); | ||
| 681 | } | 720 | } |
| 682 | 721 | ||
| 683 | static int __sil24_reset_controller(void __iomem *port) | 722 | static void sil24_thaw(struct ata_port *ap) |
| 684 | { | 723 | { |
| 685 | int cnt; | 724 | void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; |
| 686 | u32 tmp; | 725 | u32 tmp; |
| 687 | 726 | ||
| 688 | /* Reset controller state. Is this correct? */ | 727 | /* clear IRQ */ |
| 689 | writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT); | 728 | tmp = readl(port + PORT_IRQ_STAT); |
| 690 | readl(port + PORT_CTRL_STAT); /* sync */ | 729 | writel(tmp, port + PORT_IRQ_STAT); |
| 691 | |||
| 692 | /* Max ~100ms */ | ||
| 693 | for (cnt = 0; cnt < 1000; cnt++) { | ||
| 694 | udelay(100); | ||
| 695 | tmp = readl(port + PORT_CTRL_STAT); | ||
| 696 | if (!(tmp & PORT_CS_DEV_RST)) | ||
| 697 | break; | ||
| 698 | } | ||
| 699 | |||
| 700 | if (tmp & PORT_CS_DEV_RST) | ||
| 701 | return -1; | ||
| 702 | |||
| 703 | if (tmp & PORT_CS_RDY) | ||
| 704 | return 0; | ||
| 705 | |||
| 706 | return __sil24_restart_controller(port); | ||
| 707 | } | ||
| 708 | |||
| 709 | static void sil24_reset_controller(struct ata_port *ap) | ||
| 710 | { | ||
| 711 | printk(KERN_NOTICE DRV_NAME | ||
| 712 | " ata%u: resetting controller...\n", ap->id); | ||
| 713 | if (__sil24_reset_controller((void __iomem *)ap->ioaddr.cmd_addr)) | ||
| 714 | printk(KERN_ERR DRV_NAME | ||
| 715 | " ata%u: failed to reset controller\n", ap->id); | ||
| 716 | } | ||
| 717 | |||
| 718 | static void sil24_eng_timeout(struct ata_port *ap) | ||
| 719 | { | ||
| 720 | struct ata_queued_cmd *qc; | ||
| 721 | |||
| 722 | qc = ata_qc_from_tag(ap, ap->active_tag); | ||
| 723 | |||
| 724 | ata_port_printk(ap, KERN_ERR, "command timeout\n"); | ||
| 725 | qc->err_mask |= AC_ERR_TIMEOUT; | ||
| 726 | ata_eh_qc_complete(qc); | ||
| 727 | 730 | ||
| 728 | sil24_reset_controller(ap); | 731 | /* turn IRQ back on */ |
| 732 | writel(DEF_PORT_IRQ, port + PORT_IRQ_ENABLE_SET); | ||
| 729 | } | 733 | } |
| 730 | 734 | ||
| 731 | static void sil24_error_intr(struct ata_port *ap, u32 slot_stat) | 735 | static void sil24_error_intr(struct ata_port *ap) |
| 732 | { | 736 | { |
| 733 | struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag); | ||
| 734 | struct sil24_port_priv *pp = ap->private_data; | ||
| 735 | void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; | 737 | void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; |
| 736 | u32 irq_stat, cmd_err, sstatus, serror; | 738 | struct ata_eh_info *ehi = &ap->eh_info; |
| 737 | unsigned int err_mask; | 739 | int freeze = 0; |
| 740 | u32 irq_stat; | ||
| 738 | 741 | ||
| 742 | /* on error, we need to clear IRQ explicitly */ | ||
| 739 | irq_stat = readl(port + PORT_IRQ_STAT); | 743 | irq_stat = readl(port + PORT_IRQ_STAT); |
| 740 | writel(irq_stat, port + PORT_IRQ_STAT); /* clear irq */ | 744 | writel(irq_stat, port + PORT_IRQ_STAT); |
| 741 | 745 | ||
| 742 | if (!(irq_stat & PORT_IRQ_ERROR)) { | 746 | /* first, analyze and record host port events */ |
| 743 | /* ignore non-completion, non-error irqs for now */ | 747 | ata_ehi_clear_desc(ehi); |
| 744 | printk(KERN_WARNING DRV_NAME | ||
| 745 | "ata%u: non-error exception irq (irq_stat %x)\n", | ||
| 746 | ap->id, irq_stat); | ||
| 747 | return; | ||
| 748 | } | ||
| 749 | 748 | ||
| 750 | cmd_err = readl(port + PORT_CMD_ERR); | 749 | ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat); |
| 751 | sstatus = readl(port + PORT_SSTATUS); | ||
| 752 | serror = readl(port + PORT_SERROR); | ||
| 753 | if (serror) | ||
| 754 | writel(serror, port + PORT_SERROR); | ||
| 755 | 750 | ||
| 756 | /* | 751 | if (irq_stat & PORT_IRQ_DEV_XCHG) { |
| 757 | * Don't log ATAPI device errors. They're supposed to happen | 752 | ehi->err_mask |= AC_ERR_ATA_BUS; |
| 758 | * and any serious errors will be logged using sense data by | 753 | /* sil24 doesn't recover very well from phy |
| 759 | * the SCSI layer. | 754 | * disconnection with a softreset. Force hardreset. |
| 760 | */ | ||
| 761 | if (ap->device[0].class != ATA_DEV_ATAPI || cmd_err > PORT_CERR_SDB) | ||
| 762 | printk("ata%u: error interrupt on port%d\n" | ||
| 763 | " stat=0x%x irq=0x%x cmd_err=%d sstatus=0x%x serror=0x%x\n", | ||
| 764 | ap->id, ap->port_no, slot_stat, irq_stat, cmd_err, sstatus, serror); | ||
| 765 | |||
| 766 | if (cmd_err == PORT_CERR_DEV || cmd_err == PORT_CERR_SDB) { | ||
| 767 | /* | ||
| 768 | * Device is reporting error, tf registers are valid. | ||
| 769 | */ | 755 | */ |
| 770 | sil24_update_tf(ap); | 756 | ehi->action |= ATA_EH_HARDRESET; |
| 771 | err_mask = ac_err_mask(pp->tf.command); | 757 | ata_ehi_push_desc(ehi, ", device_exchanged"); |
| 772 | sil24_restart_controller(ap); | 758 | freeze = 1; |
| 773 | } else { | ||
| 774 | /* | ||
| 775 | * Other errors. libata currently doesn't have any | ||
| 776 | * mechanism to report these errors. Just turn on | ||
| 777 | * ATA_ERR. | ||
| 778 | */ | ||
| 779 | err_mask = AC_ERR_OTHER; | ||
| 780 | sil24_reset_controller(ap); | ||
| 781 | } | 759 | } |
| 782 | 760 | ||
| 783 | if (qc) { | 761 | if (irq_stat & PORT_IRQ_UNK_FIS) { |
| 784 | qc->err_mask |= err_mask; | 762 | ehi->err_mask |= AC_ERR_HSM; |
| 785 | ata_qc_complete(qc); | 763 | ehi->action |= ATA_EH_SOFTRESET; |
| 764 | ata_ehi_push_desc(ehi , ", unknown FIS"); | ||
| 765 | freeze = 1; | ||
| 766 | } | ||
| 767 | |||
| 768 | /* deal with command error */ | ||
| 769 | if (irq_stat & PORT_IRQ_ERROR) { | ||
| 770 | struct sil24_cerr_info *ci = NULL; | ||
| 771 | unsigned int err_mask = 0, action = 0; | ||
| 772 | struct ata_queued_cmd *qc; | ||
| 773 | u32 cerr; | ||
| 774 | |||
| 775 | /* analyze CMD_ERR */ | ||
| 776 | cerr = readl(port + PORT_CMD_ERR); | ||
| 777 | if (cerr < ARRAY_SIZE(sil24_cerr_db)) | ||
| 778 | ci = &sil24_cerr_db[cerr]; | ||
| 779 | |||
| 780 | if (ci && ci->desc) { | ||
| 781 | err_mask |= ci->err_mask; | ||
| 782 | action |= ci->action; | ||
| 783 | ata_ehi_push_desc(ehi, ", %s", ci->desc); | ||
| 784 | } else { | ||
| 785 | err_mask |= AC_ERR_OTHER; | ||
| 786 | action |= ATA_EH_SOFTRESET; | ||
| 787 | ata_ehi_push_desc(ehi, ", unknown command error %d", | ||
| 788 | cerr); | ||
| 789 | } | ||
| 790 | |||
| 791 | /* record error info */ | ||
| 792 | qc = ata_qc_from_tag(ap, ap->active_tag); | ||
| 793 | if (qc) { | ||
| 794 | int tag = qc->tag; | ||
| 795 | if (unlikely(ata_tag_internal(tag))) | ||
| 796 | tag = 0; | ||
| 797 | sil24_update_tf(ap); | ||
| 798 | qc->err_mask |= err_mask; | ||
| 799 | } else | ||
| 800 | ehi->err_mask |= err_mask; | ||
| 801 | |||
| 802 | ehi->action |= action; | ||
| 786 | } | 803 | } |
| 804 | |||
| 805 | /* freeze or abort */ | ||
| 806 | if (freeze) | ||
| 807 | ata_port_freeze(ap); | ||
| 808 | else | ||
| 809 | ata_port_abort(ap); | ||
| 787 | } | 810 | } |
| 788 | 811 | ||
| 789 | static inline void sil24_host_intr(struct ata_port *ap) | 812 | static inline void sil24_host_intr(struct ata_port *ap) |
| 790 | { | 813 | { |
| 791 | struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag); | ||
| 792 | void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; | 814 | void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; |
| 815 | struct ata_queued_cmd *qc; | ||
| 793 | u32 slot_stat; | 816 | u32 slot_stat; |
| 794 | 817 | ||
| 795 | slot_stat = readl(port + PORT_SLOT_STAT); | 818 | slot_stat = readl(port + PORT_SLOT_STAT); |
| 796 | if (!(slot_stat & HOST_SSTAT_ATTN)) { | ||
| 797 | struct sil24_port_priv *pp = ap->private_data; | ||
| 798 | 819 | ||
| 799 | if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC) | 820 | if (unlikely(slot_stat & HOST_SSTAT_ATTN)) { |
| 800 | writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT); | 821 | sil24_error_intr(ap); |
| 822 | return; | ||
| 823 | } | ||
| 824 | |||
| 825 | if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC) | ||
| 826 | writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT); | ||
| 801 | 827 | ||
| 802 | if (qc) { | 828 | qc = ata_qc_from_tag(ap, ap->active_tag); |
| 803 | if (qc->flags & ATA_QCFLAG_RESULT_TF) | 829 | if (qc) { |
| 804 | sil24_update_tf(ap); | 830 | if (qc->flags & ATA_QCFLAG_RESULT_TF) |
| 805 | qc->err_mask |= ac_err_mask(pp->tf.command); | 831 | sil24_update_tf(ap); |
| 806 | ata_qc_complete(qc); | 832 | ata_qc_complete(qc); |
| 807 | } | 833 | return; |
| 808 | } else | 834 | } |
| 809 | sil24_error_intr(ap, slot_stat); | 835 | |
| 836 | if (ata_ratelimit()) | ||
| 837 | ata_port_printk(ap, KERN_INFO, "spurious interrupt " | ||
| 838 | "(slot_stat 0x%x active_tag %d)\n", | ||
| 839 | slot_stat, ap->active_tag); | ||
| 810 | } | 840 | } |
| 811 | 841 | ||
| 812 | static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs) | 842 | static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs) |
| @@ -846,6 +876,31 @@ static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs * | |||
| 846 | return IRQ_RETVAL(handled); | 876 | return IRQ_RETVAL(handled); |
| 847 | } | 877 | } |
| 848 | 878 | ||
| 879 | static void sil24_error_handler(struct ata_port *ap) | ||
| 880 | { | ||
| 881 | struct ata_eh_context *ehc = &ap->eh_context; | ||
| 882 | |||
| 883 | if (sil24_init_port(ap)) { | ||
| 884 | ata_eh_freeze_port(ap); | ||
| 885 | ehc->i.action |= ATA_EH_HARDRESET; | ||
| 886 | } | ||
| 887 | |||
| 888 | /* perform recovery */ | ||
| 889 | ata_do_eh(ap, sil24_softreset, sil24_hardreset, ata_std_postreset); | ||
| 890 | } | ||
| 891 | |||
| 892 | static void sil24_post_internal_cmd(struct ata_queued_cmd *qc) | ||
| 893 | { | ||
| 894 | struct ata_port *ap = qc->ap; | ||
| 895 | |||
| 896 | if (qc->flags & ATA_QCFLAG_FAILED) | ||
| 897 | qc->err_mask |= AC_ERR_OTHER; | ||
| 898 | |||
| 899 | /* make DMA engine forget about the failed command */ | ||
| 900 | if (qc->err_mask) | ||
| 901 | sil24_init_port(ap); | ||
| 902 | } | ||
| 903 | |||
| 849 | static inline void sil24_cblk_free(struct sil24_port_priv *pp, struct device *dev) | 904 | static inline void sil24_cblk_free(struct sil24_port_priv *pp, struct device *dev) |
| 850 | { | 905 | { |
| 851 | const size_t cb_size = sizeof(*pp->cmd_block); | 906 | const size_t cb_size = sizeof(*pp->cmd_block); |
| @@ -1058,15 +1113,6 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
| 1058 | /* Always use 64bit activation */ | 1113 | /* Always use 64bit activation */ |
| 1059 | writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR); | 1114 | writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR); |
| 1060 | 1115 | ||
| 1061 | /* Configure interrupts */ | ||
| 1062 | writel(0xffff, port + PORT_IRQ_ENABLE_CLR); | ||
| 1063 | writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR | | ||
| 1064 | PORT_IRQ_SDB_NOTIFY, port + PORT_IRQ_ENABLE_SET); | ||
| 1065 | |||
| 1066 | /* Clear interrupts */ | ||
| 1067 | writel(0x0fff0fff, port + PORT_IRQ_STAT); | ||
| 1068 | writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR); | ||
| 1069 | |||
| 1070 | /* Clear port multiplier enable and resume bits */ | 1116 | /* Clear port multiplier enable and resume bits */ |
| 1071 | writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR); | 1117 | writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR); |
| 1072 | } | 1118 | } |
