[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 * NV-specific details such as register offsets, SATA phy location, 30 * hotplug info, etc. 31 * 32 * 0.09 33 * - Fixed bug introduced by 0.08's MCP51 and MCP55 support. 34 * ··· 130 static void nv_host_stop (struct ata_host_set *host_set); 131 static void nv_enable_hotplug(struct ata_probe_ent *probe_ent); 132 static void nv_disable_hotplug(struct ata_host_set *host_set); 133 - static void nv_check_hotplug(struct ata_host_set *host_set); 134 static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent); 135 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); 137 138 enum nv_host_type 139 { ··· 182 enum nv_host_type host_type; 183 void (*enable_hotplug)(struct ata_probe_ent *probe_ent); 184 void (*disable_hotplug)(struct ata_host_set *host_set); 185 - void (*check_hotplug)(struct ata_host_set *host_set); 186 187 }; 188 static struct nv_host_desc nv_device_tbl[] = { ··· 315 qc = ata_qc_from_tag(ap, ap->active_tag); 316 if (qc && (!(qc->tf.ctl & ATA_NIEN))) 317 handled += ata_host_intr(ap, qc); 318 } 319 320 } 321 322 if (host->host_desc->check_hotplug) 323 - host->host_desc->check_hotplug(host_set); 324 325 spin_unlock_irqrestore(&host_set->lock, flags); 326 ··· 507 outb(intr_mask, host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE); 508 } 509 510 - static void nv_check_hotplug(struct ata_host_set *host_set) 511 { 512 u8 intr_status; 513 ··· 532 if (intr_status & NV_INT_STATUS_SDEV_REMOVED) 533 printk(KERN_WARNING "nv_sata: " 534 "Secondary device removed\n"); 535 } 536 } 537 538 static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent) ··· 574 pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval); 575 } 576 577 - static void nv_check_hotplug_ck804(struct ata_host_set *host_set) 578 { 579 u8 intr_status; 580 ··· 599 if (intr_status & NV_INT_STATUS_SDEV_REMOVED) 600 printk(KERN_WARNING "nv_sata: " 601 "Secondary device removed\n"); 602 } 603 } 604 605 static int __init nv_init(void)
··· 29 * NV-specific details such as register offsets, SATA phy location, 30 * hotplug info, etc. 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 + * 38 * 0.09 39 * - Fixed bug introduced by 0.08's MCP51 and MCP55 support. 40 * ··· 124 static void nv_host_stop (struct ata_host_set *host_set); 125 static void nv_enable_hotplug(struct ata_probe_ent *probe_ent); 126 static void nv_disable_hotplug(struct ata_host_set *host_set); 127 + static int nv_check_hotplug(struct ata_host_set *host_set); 128 static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent); 129 static void nv_disable_hotplug_ck804(struct ata_host_set *host_set); 130 + static int nv_check_hotplug_ck804(struct ata_host_set *host_set); 131 132 enum nv_host_type 133 { ··· 176 enum nv_host_type host_type; 177 void (*enable_hotplug)(struct ata_probe_ent *probe_ent); 178 void (*disable_hotplug)(struct ata_host_set *host_set); 179 + int (*check_hotplug)(struct ata_host_set *host_set); 180 181 }; 182 static struct nv_host_desc nv_device_tbl[] = { ··· 309 qc = ata_qc_from_tag(ap, ap->active_tag); 310 if (qc && (!(qc->tf.ctl & ATA_NIEN))) 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); 316 } 317 318 } 319 320 if (host->host_desc->check_hotplug) 321 + handled += host->host_desc->check_hotplug(host_set); 322 323 spin_unlock_irqrestore(&host_set->lock, flags); 324 ··· 497 outb(intr_mask, host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE); 498 } 499 500 + static int nv_check_hotplug(struct ata_host_set *host_set) 501 { 502 u8 intr_status; 503 ··· 522 if (intr_status & NV_INT_STATUS_SDEV_REMOVED) 523 printk(KERN_WARNING "nv_sata: " 524 "Secondary device removed\n"); 525 + 526 + return 1; 527 } 528 + 529 + return 0; 530 } 531 532 static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent) ··· 560 pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval); 561 } 562 563 + static int nv_check_hotplug_ck804(struct ata_host_set *host_set) 564 { 565 u8 intr_status; 566 ··· 585 if (intr_status & NV_INT_STATUS_SDEV_REMOVED) 586 printk(KERN_WARNING "nv_sata: " 587 "Secondary device removed\n"); 588 + 589 + return 1; 590 } 591 + 592 + return 0; 593 } 594 595 static int __init nv_init(void)