summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Garzik <jgarzik@pobox.com>2004-07-03 14:40:48 -0400
committerJeff Garzik <jgarzik@pobox.com>2004-07-03 14:40:48 -0400
commitea21e4ac2f5d655db154b64329c437688c92f7d5 (patch)
treea0e598bc5b94d821e06f359474ea55414ccb4da7
parente69207190a6f3c9d0413c4a8c9518dd07d18e0a3 (diff)
[libata] create, and use, ->irq_clear hook
This is more conservative in general, and so applies to multiple controllers. Specifically it attempts to address irq-related issues on the Intel ICH5/6 hardware. On Intel ICH5/6, the BMDMA 'interrupt' status bit will be set even on non-DMA commands, which software (and I) did not expect. This change clears pending interrupts once upon initialization, and then each time ata_irq_on() is called.
-rw-r--r--drivers/scsi/ata_piix.c2
-rw-r--r--drivers/scsi/libata-core.c17
-rw-r--r--drivers/scsi/sata_nv.c1
-rw-r--r--drivers/scsi/sata_promise.c10
-rw-r--r--drivers/scsi/sata_sil.c1
-rw-r--r--drivers/scsi/sata_sis.c1
-rw-r--r--drivers/scsi/sata_svw.c1
-rw-r--r--drivers/scsi/sata_sx4.c12
-rw-r--r--drivers/scsi/sata_via.c1
-rw-r--r--drivers/scsi/sata_vsc.c1
-rw-r--r--include/linux/libata.h10
11 files changed, 50 insertions, 7 deletions
diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c
index c668625bcedc..c030c0a6838e 100644
--- a/drivers/scsi/ata_piix.c
+++ b/drivers/scsi/ata_piix.c
@@ -143,6 +143,7 @@ static struct ata_port_operations piix_pata_ops = {
.eng_timeout = ata_eng_timeout,
.irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
.port_start = ata_port_start,
.port_stop = ata_port_stop,
@@ -168,6 +169,7 @@ static struct ata_port_operations piix_sata_ops = {
.eng_timeout = ata_eng_timeout,
.irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
.port_start = ata_port_start,
.port_stop = ata_port_stop,
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 63c2982cab28..98689e54277f 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -2537,6 +2537,11 @@ void ata_bmdma_start_pio (struct ata_queued_cmd *qc)
ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
}
+void ata_bmdma_irq_clear(struct ata_port *ap)
+{
+ ata_bmdma_ack_irq(ap);
+}
+
/**
* ata_host_intr - Handle host interrupt for given (port, task)
* @ap: Port on which interrupt arrived (possibly...)
@@ -2879,6 +2884,7 @@ int ata_device_add(struct ata_probe_ent *ent)
host_set->irq = ent->irq;
host_set->mmio_base = ent->mmio_base;
host_set->private_data = ent->private_data;
+ host_set->ops = ent->port_ops;
/* register each port bound to this device */
for (i = 0; i < ent->n_ports; i++) {
@@ -2901,6 +2907,8 @@ int ata_device_add(struct ata_probe_ent *ent)
ap->ioaddr.bmdma_addr,
ent->irq);
+ ata_chk_status(ap);
+ host_set->ops->irq_clear(ap);
count++;
}
@@ -2909,10 +2917,6 @@ int ata_device_add(struct ata_probe_ent *ent)
return 0;
}
- /* TODO: ack irq here, to ensure it won't scream
- * when we enable it?
- */
-
/* obtain irq, that is shared between channels */
if (request_irq(ent->irq, ent->port_ops->irq_handler, ent->irq_flags,
DRV_NAME, host_set))
@@ -3238,8 +3242,8 @@ void ata_pci_remove_one (struct pci_dev *pdev)
free_irq(host_set->irq, host_set);
if (host_set->mmio_base)
iounmap(host_set->mmio_base);
- if (host_set->ports[0]->ops->host_stop)
- host_set->ports[0]->ops->host_stop(host_set);
+ if (host_set->ops->host_stop)
+ host_set->ops->host_stop(host_set);
for (i = 0; i < host_set->n_ports; i++) {
ap = host_set->ports[i];
@@ -3363,6 +3367,7 @@ EXPORT_SYMBOL_GPL(ata_bmdma_setup_pio);
EXPORT_SYMBOL_GPL(ata_bmdma_start_pio);
EXPORT_SYMBOL_GPL(ata_bmdma_setup_mmio);
EXPORT_SYMBOL_GPL(ata_bmdma_start_mmio);
+EXPORT_SYMBOL_GPL(ata_bmdma_irq_clear);
EXPORT_SYMBOL_GPL(ata_port_probe);
EXPORT_SYMBOL_GPL(sata_phy_reset);
EXPORT_SYMBOL_GPL(ata_bus_reset);
diff --git a/drivers/scsi/sata_nv.c b/drivers/scsi/sata_nv.c
index 8e8fa4c6b182..bf1932e4930e 100644
--- a/drivers/scsi/sata_nv.c
+++ b/drivers/scsi/sata_nv.c
@@ -142,6 +142,7 @@ static struct ata_port_operations nv_ops = {
.qc_issue = ata_qc_issue_prot,
.eng_timeout = ata_eng_timeout,
.irq_handler = nv_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
.scr_read = nv_scr_read,
.scr_write = nv_scr_write,
.port_start = ata_port_start,
diff --git a/drivers/scsi/sata_promise.c b/drivers/scsi/sata_promise.c
index 86f88e159b4c..dcaebcf7110c 100644
--- a/drivers/scsi/sata_promise.c
+++ b/drivers/scsi/sata_promise.c
@@ -86,6 +86,7 @@ static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf);
static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf);
static inline void pdc_dma_complete (struct ata_port *ap,
struct ata_queued_cmd *qc, int have_err);
+static void pdc_irq_clear(struct ata_port *ap);
static Scsi_Host_Template pdc_sata_sht = {
.module = THIS_MODULE,
@@ -118,6 +119,7 @@ static struct ata_port_operations pdc_sata_ops = {
.qc_issue = ata_qc_issue_prot,
.eng_timeout = pdc_eng_timeout,
.irq_handler = pdc_interrupt,
+ .irq_clear = pdc_irq_clear,
.scr_read = pdc_sata_scr_read,
.scr_write = pdc_sata_scr_write,
.port_start = pdc_port_start,
@@ -379,6 +381,14 @@ static inline unsigned int pdc_host_intr( struct ata_port *ap,
return handled;
}
+static void pdc_irq_clear(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ void *mmio = host_set->mmio_base;
+
+ readl(mmio + PDC_INT_SEQMASK);
+}
+
static irqreturn_t pdc_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
{
struct ata_host_set *host_set = dev_instance;
diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c
index 2c247d340d46..72a89d1beb69 100644
--- a/drivers/scsi/sata_sil.c
+++ b/drivers/scsi/sata_sil.c
@@ -136,6 +136,7 @@ static struct ata_port_operations sil_ops = {
.qc_issue = ata_qc_issue_prot,
.eng_timeout = ata_eng_timeout,
.irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
.scr_read = sil_scr_read,
.scr_write = sil_scr_write,
.port_start = ata_port_start,
diff --git a/drivers/scsi/sata_sis.c b/drivers/scsi/sata_sis.c
index df102f1d2a9c..88b7a02ead5c 100644
--- a/drivers/scsi/sata_sis.c
+++ b/drivers/scsi/sata_sis.c
@@ -104,6 +104,7 @@ static struct ata_port_operations sis_ops = {
.qc_issue = ata_qc_issue_prot,
.eng_timeout = ata_eng_timeout,
.irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
.scr_read = sis_scr_read,
.scr_write = sis_scr_write,
.port_start = ata_port_start,
diff --git a/drivers/scsi/sata_svw.c b/drivers/scsi/sata_svw.c
index 4393d494c97b..398f24f27ed2 100644
--- a/drivers/scsi/sata_svw.c
+++ b/drivers/scsi/sata_svw.c
@@ -237,6 +237,7 @@ static struct ata_port_operations k2_sata_ops = {
.qc_issue = ata_qc_issue_prot,
.eng_timeout = ata_eng_timeout,
.irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
.scr_read = k2_sata_scr_read,
.scr_write = k2_sata_scr_write,
.port_start = ata_port_start,
diff --git a/drivers/scsi/sata_sx4.c b/drivers/scsi/sata_sx4.c
index b315dfda0d56..3b71fa2853f6 100644
--- a/drivers/scsi/sata_sx4.c
+++ b/drivers/scsi/sata_sx4.c
@@ -171,6 +171,7 @@ static void pdc20621_get_from_dimm(struct ata_probe_ent *pe,
#endif
static void pdc20621_put_to_dimm(struct ata_probe_ent *pe,
void *psource, u32 offset, u32 size);
+static void pdc20621_irq_clear(struct ata_port *ap);
static Scsi_Host_Template pdc_sata_sht = {
@@ -204,6 +205,7 @@ static struct ata_port_operations pdc_20621_ops = {
.qc_issue = ata_qc_issue_prot,
.eng_timeout = pdc_eng_timeout,
.irq_handler = pdc20621_interrupt,
+ .irq_clear = pdc20621_irq_clear,
.port_start = pdc_port_start,
.port_stop = pdc_port_stop,
.host_stop = pdc20621_host_stop,
@@ -703,6 +705,16 @@ static inline unsigned int pdc20621_host_intr( struct ata_port *ap,
return handled;
}
+static void pdc20621_irq_clear(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ void *mmio = host_set->mmio_base;
+
+ mmio += PDC_CHIP0_OFS;
+
+ readl(mmio + PDC_20621_SEQMASK);
+}
+
static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
{
struct ata_host_set *host_set = dev_instance;
diff --git a/drivers/scsi/sata_via.c b/drivers/scsi/sata_via.c
index 1c4df92a7a51..993edfc99590 100644
--- a/drivers/scsi/sata_via.c
+++ b/drivers/scsi/sata_via.c
@@ -114,6 +114,7 @@ static struct ata_port_operations svia_sata_ops = {
.eng_timeout = ata_eng_timeout,
.irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
.scr_read = svia_scr_read,
.scr_write = svia_scr_write,
diff --git a/drivers/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c
index adb61f709e09..d63a339cc11d 100644
--- a/drivers/scsi/sata_vsc.c
+++ b/drivers/scsi/sata_vsc.c
@@ -219,6 +219,7 @@ static struct ata_port_operations vsc_sata_ops = {
.qc_issue = ata_qc_issue_prot,
.eng_timeout = ata_eng_timeout,
.irq_handler = vsc_sata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
.scr_read = vsc_sata_scr_read,
.scr_write = vsc_sata_scr_write,
.port_start = ata_port_start,
diff --git a/include/linux/libata.h b/include/linux/libata.h
index c57be1ab010d..7fc0c2763f88 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -202,6 +202,7 @@ struct ata_host_set {
void *mmio_base;
unsigned int n_ports;
void *private_data;
+ struct ata_port_operations *ops;
struct ata_port * ports[0];
};
@@ -318,6 +319,7 @@ struct ata_port_operations {
void (*eng_timeout) (struct ata_port *ap);
irqreturn_t (*irq_handler)(int, void *, struct pt_regs *);
+ void (*irq_clear) (struct ata_port *);
u32 (*scr_read) (struct ata_port *ap, unsigned int sc_reg);
void (*scr_write) (struct ata_port *ap, unsigned int sc_reg,
@@ -382,6 +384,7 @@ extern void ata_bmdma_setup_mmio (struct ata_queued_cmd *qc);
extern void ata_bmdma_start_mmio (struct ata_queued_cmd *qc);
extern void ata_bmdma_setup_pio (struct ata_queued_cmd *qc);
extern void ata_bmdma_start_pio (struct ata_queued_cmd *qc);
+extern void ata_bmdma_irq_clear(struct ata_port *ap);
extern int pci_test_config_bits(struct pci_dev *pdev, struct pci_bits *bits);
extern void ata_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat);
extern void ata_eng_timeout(struct ata_port *ap);
@@ -484,6 +487,7 @@ static inline void ata_tf_init(struct ata_port *ap, struct ata_taskfile *tf, uns
static inline u8 ata_irq_on(struct ata_port *ap)
{
struct ata_ioports *ioaddr = &ap->ioaddr;
+ u8 tmp;
ap->ctl &= ~ATA_NIEN;
ap->last_ctl = ap->ctl;
@@ -492,7 +496,11 @@ static inline u8 ata_irq_on(struct ata_port *ap)
writeb(ap->ctl, ioaddr->ctl_addr);
else
outb(ap->ctl, ioaddr->ctl_addr);
- return ata_wait_idle(ap);
+ tmp = ata_wait_idle(ap);
+
+ ap->ops->irq_clear(ap);
+
+ return tmp;
}
static inline u8 ata_irq_ack(struct ata_port *ap, unsigned int chk_drq)