[PATCH] sata_nv, spurious interrupts at system startup with MAXTOR 6H500F0 drive

This patch works around a problem with spurious interrupts seen at boot time when
a MAXTOR 6H500F0 drive is present. An ATA interrupt condition is mysteriously
present at start of day. If we took too long in issuing the first command,
the kernel would basically get tired of the spurious interrupts and turn the interrupt
off. Issuing the first command essentially causes the interrupt condition to
get acknowledged.

I haven't seen this happen with any other drives.

What I basically do is ack ATA status by reading it regardless of whether we're
expecting to have to handle an interrupt. This clears the start-of-day anomalous
interrupt condition, and keeps the kernel from disabling that interrupt due to
too many spurious interrupts.

Also, I fixed a bug where hotplug interrupts weren't getting acknowledged as handled
in the ISR. This was not the cause of the spurious interrupts, but it's the right
thing to do anyway.

Signed-Off-By: Andrew Chew

Signed-off-by: Jeff Garzik <jgarzik@pobox.com>

authored by

Andrew Chew and committed by
Jeff Garzik
b887030a 5367f2d6

+24 -6
+24 -6
drivers/scsi/sata_nv.c
··· 29 29 * NV-specific details such as register offsets, SATA phy location, 30 30 * hotplug info, etc. 31 31 * 32 + * 0.10 33 + * - Fixed spurious interrupts issue seen with the Maxtor 6H500F0 500GB 34 + * drive. Also made the check_hotplug() callbacks return whether there 35 + * was a hotplug interrupt or not. This was not the source of the 36 + * spurious interrupts, but is the right thing to do anyway. 37 + * 32 38 * 0.09 33 39 * - Fixed bug introduced by 0.08's MCP51 and MCP55 support. 34 40 * ··· 130 124 static void nv_host_stop (struct ata_host_set *host_set); 131 125 static void nv_enable_hotplug(struct ata_probe_ent *probe_ent); 132 126 static void nv_disable_hotplug(struct ata_host_set *host_set); 133 - static void nv_check_hotplug(struct ata_host_set *host_set); 127 + static int nv_check_hotplug(struct ata_host_set *host_set); 134 128 static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent); 135 129 static void nv_disable_hotplug_ck804(struct ata_host_set *host_set); 136 - static void nv_check_hotplug_ck804(struct ata_host_set *host_set); 130 + static int nv_check_hotplug_ck804(struct ata_host_set *host_set); 137 131 138 132 enum nv_host_type 139 133 { ··· 182 176 enum nv_host_type host_type; 183 177 void (*enable_hotplug)(struct ata_probe_ent *probe_ent); 184 178 void (*disable_hotplug)(struct ata_host_set *host_set); 185 - void (*check_hotplug)(struct ata_host_set *host_set); 179 + int (*check_hotplug)(struct ata_host_set *host_set); 186 180 187 181 }; 188 182 static struct nv_host_desc nv_device_tbl[] = { ··· 315 309 qc = ata_qc_from_tag(ap, ap->active_tag); 316 310 if (qc && (!(qc->tf.ctl & ATA_NIEN))) 317 311 handled += ata_host_intr(ap, qc); 312 + else 313 + // No request pending? Clear interrupt status 314 + // anyway, in case there's one pending. 315 + ap->ops->check_status(ap); 318 316 } 319 317 320 318 } 321 319 322 320 if (host->host_desc->check_hotplug) 323 - host->host_desc->check_hotplug(host_set); 321 + handled += host->host_desc->check_hotplug(host_set); 324 322 325 323 spin_unlock_irqrestore(&host_set->lock, flags); 326 324 ··· 507 497 outb(intr_mask, host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE); 508 498 } 509 499 510 - static void nv_check_hotplug(struct ata_host_set *host_set) 500 + static int nv_check_hotplug(struct ata_host_set *host_set) 511 501 { 512 502 u8 intr_status; 513 503 ··· 532 522 if (intr_status & NV_INT_STATUS_SDEV_REMOVED) 533 523 printk(KERN_WARNING "nv_sata: " 534 524 "Secondary device removed\n"); 525 + 526 + return 1; 535 527 } 528 + 529 + return 0; 536 530 } 537 531 538 532 static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent) ··· 574 560 pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval); 575 561 } 576 562 577 - static void nv_check_hotplug_ck804(struct ata_host_set *host_set) 563 + static int nv_check_hotplug_ck804(struct ata_host_set *host_set) 578 564 { 579 565 u8 intr_status; 580 566 ··· 599 585 if (intr_status & NV_INT_STATUS_SDEV_REMOVED) 600 586 printk(KERN_WARNING "nv_sata: " 601 587 "Secondary device removed\n"); 588 + 589 + return 1; 602 590 } 591 + 592 + return 0; 603 593 } 604 594 605 595 static int __init nv_init(void)