]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | From: Tejun Heo <tj@kernel.org> |
2 | Subject: ata_piix: detect and clear spurious IRQs | |
3 | References: bnc#445872 | |
4 | ||
5 | The DMA_IRQ bit in the bmdma status register is always set when IDEIRQ | |
6 | is asserted allowing spurious IRQ detection. Detect spurious IRQs and | |
7 | clear them. This protects ata_piix against nobody-cared which gets | |
8 | reported not so rarely. | |
9 | ||
10 | Signed-off-by: Tejun Heo <tj@kernel.org> | |
11 | Signed-off-by: Tejun Heo <teheo@suse.de> | |
12 | --- | |
13 | drivers/ata/ata_piix.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++- | |
14 | 1 file changed, 53 insertions(+), 1 deletion(-) | |
15 | ||
16 | --- a/drivers/ata/ata_piix.c | |
17 | +++ b/drivers/ata/ata_piix.c | |
18 | @@ -923,6 +923,58 @@ static int piix_sidpr_scr_read(struct at | |
19 | return 0; | |
20 | } | |
21 | ||
22 | +static irqreturn_t piix_interrupt(int irq, void *dev_instance) | |
23 | +{ | |
24 | + struct ata_host *host = dev_instance; | |
25 | + unsigned int i; | |
26 | + unsigned int handled = 0; | |
27 | + unsigned long flags; | |
28 | + | |
29 | + spin_lock_irqsave(&host->lock, flags); | |
30 | + | |
31 | + for (i = 0; i < host->n_ports; i++) { | |
32 | + struct ata_port *ap = host->ports[i]; | |
33 | + struct ata_queued_cmd *qc; | |
34 | + u8 host_stat; | |
35 | + | |
36 | + if (ata_port_is_dummy(ap)) | |
37 | + continue; | |
38 | + | |
39 | + qc = ata_qc_from_tag(ap, ap->link.active_tag); | |
40 | + if (qc && !(qc->tf.flags & ATA_TFLAG_POLLING)) { | |
41 | + handled |= ata_sff_host_intr(ap, qc); | |
42 | + continue; | |
43 | + } | |
44 | + | |
45 | + /* | |
46 | + * Control reaches here if HSM is not expecting IRQ. | |
47 | + * If the controller is actually asserting IRQ line, | |
48 | + * this will lead to nobody cared. Fortuantely, | |
49 | + * DMA_INTR of PIIX is set whenever IDEIRQ is set so | |
50 | + * it can be used to detect spurious IRQs. As the | |
51 | + * driver is not expecting IRQ at all, clearing IRQ | |
52 | + * here won't lead to loss of IRQ event. | |
53 | + */ | |
54 | + if (unlikely(!ap->ioaddr.bmdma_addr)) | |
55 | + continue; | |
56 | + | |
57 | + host_stat = ap->ops->bmdma_status(ap); | |
58 | + if (!(host_stat & ATA_DMA_INTR)) | |
59 | + continue; | |
60 | + | |
61 | + if (printk_ratelimit()) | |
62 | + ata_port_printk(ap, KERN_INFO, | |
63 | + "clearing spurious IRQ\n"); | |
64 | + ap->ops->sff_check_status(ap); | |
65 | + ap->ops->sff_irq_clear(ap); | |
66 | + handled |= 1; | |
67 | + } | |
68 | + | |
69 | + spin_unlock_irqrestore(&host->lock, flags); | |
70 | + | |
71 | + return IRQ_RETVAL(handled); | |
72 | +} | |
73 | + | |
74 | static int piix_sidpr_scr_write(struct ata_link *link, | |
75 | unsigned int reg, u32 val) | |
76 | { | |
77 | @@ -1543,7 +1595,7 @@ static int __devinit piix_init_one(struc | |
78 | } | |
79 | ||
80 | pci_set_master(pdev); | |
81 | - return ata_pci_sff_activate_host(host, ata_sff_interrupt, &piix_sht); | |
82 | + return ata_pci_sff_activate_host(host, piix_interrupt, &piix_sht); | |
83 | } | |
84 | ||
85 | static int __init piix_init(void) |