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 | } |