[ARM] ecard: add helper function for setting ecard irq ops

Rather than having every driver fiddle about setting its private
IRQ operations and data, provide a helper function to contain
this functionality in one place.

Arrange to remove the driver-private IRQ operations and data when
the device is removed from the driver, and remove the driver
private code to do this.

This fixes potential problems caused by drivers forgetting to
remove these hooks.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c
index bdbd7da..65f1398 100644
--- a/arch/arm/kernel/ecard.c
+++ b/arch/arm/kernel/ecard.c
@@ -958,6 +958,14 @@
 }
 EXPORT_SYMBOL(ecard_release_resources);
 
+void ecard_setirq(struct expansion_card *ec, const struct expansion_card_ops *ops, void *irq_data)
+{
+	ec->irq_data = irq_data;
+	barrier();
+	ec->ops = ops;
+}
+EXPORT_SYMBOL(ecard_setirq);
+
 /*
  * Probe for an expansion card.
  *
@@ -1133,6 +1141,14 @@
 	drv->remove(ec);
 	ecard_release(ec);
 
+	/*
+	 * Restore the default operations.  We ensure that the
+	 * ops are set before we change the data.
+	 */
+	ec->ops = &ecard_default_ops;
+	barrier();
+	ec->irq_data = NULL;
+
 	return 0;
 }
 
diff --git a/drivers/ata/pata_icside.c b/drivers/ata/pata_icside.c
index dbc8ee2..d7621a3 100644
--- a/drivers/ata/pata_icside.c
+++ b/drivers/ata/pata_icside.c
@@ -434,8 +434,8 @@
 
 	ec->irqaddr = base + ICS_ARCIN_V5_INTRSTAT;
 	ec->irqmask = 1;
-	ec->irq_data = state;
-	ec->ops = &pata_icside_ops_arcin_v5;
+
+	ecard_setirq(ec, &pata_icside_ops_arcin_v5, state);
 
 	/*
 	 * Be on the safe side - disable interrupts
@@ -480,8 +480,7 @@
 
 	writeb(sel, ioc_base);
 
-	ec->irq_data = state;
-	ec->ops = &pata_icside_ops_arcin_v6;
+	ecard_setirq(ec, &pata_icside_ops_arcin_v6, state);
 
 	state->irq_port = easi_base;
 	state->ioc_base = ioc_base;
@@ -609,8 +608,7 @@
 	 * this register via that region.
 	 */
 	local_irq_save(flags);
-	if (ec->ops)
-		ec->ops->irqdisable(ec, ec->irq);
+	ec->ops->irqdisable(ec, ec->irq);
 	local_irq_restore(flags);
 
 	/*
@@ -638,9 +636,6 @@
 	 * don't NULL out the drvdata - devres/libata wants it
 	 * to free the ata_host structure.
 	 */
-	ec->ops = NULL;
-	ec->irq_data = NULL;
-
 	if (state->dma != NO_DMA)
 		free_dma(state->dma);
 	if (state->ioc_base)
diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c
index 1fe0457..69c949e 100644
--- a/drivers/ide/arm/icside.c
+++ b/drivers/ide/arm/icside.c
@@ -574,8 +574,8 @@
 
 	ec->irqaddr  = base + ICS_ARCIN_V5_INTRSTAT;
 	ec->irqmask  = 1;
-	ec->irq_data = state;
-	ec->ops      = &icside_ops_arcin_v5;
+
+	ecard_setirq(ec, &icside_ops_arcin_v5, state);
 
 	/*
 	 * Be on the safe side - disable interrupts
@@ -630,8 +630,7 @@
 
 	writeb(sel, ioc_base);
 
-	ec->irq_data      = state;
-	ec->ops           = &icside_ops_arcin_v6;
+	ecard_setirq(ec, &icside_ops_arcin_v6, state);
 
 	state->irq_port   = easi_base;
 	state->ioc_base   = ioc_base;
@@ -793,8 +792,6 @@
 	}
 
 	ecard_set_drvdata(ec, NULL);
-	ec->ops = NULL;
-	ec->irq_data = NULL;
 
 	if (state->ioc_base)
 		iounmap(state->ioc_base);
diff --git a/drivers/net/arm/etherh.c b/drivers/net/arm/etherh.c
index 61f574a..387f1e3 100644
--- a/drivers/net/arm/etherh.c
+++ b/drivers/net/arm/etherh.c
@@ -710,8 +710,7 @@
 	 * IRQ and control port handling - only for non-NIC slot cards.
 	 */
 	if (ec->slot_no != 8) {
-		ec->ops		= &etherh_ops;
-		ec->irq_data	= eh;
+		ecard_setirq(ec, &etherh_ops, eh);
 	} else {
 		/*
 		 * If we're in the NIC slot, make sure the IRQ is enabled
@@ -778,7 +777,6 @@
 	ecard_set_drvdata(ec, NULL);
 
 	unregister_netdev(dev);
-	ec->ops = NULL;
 
 	if (eh->ioc_fast)
 		iounmap(eh->ioc_fast);
diff --git a/drivers/scsi/arm/cumana_2.c b/drivers/scsi/arm/cumana_2.c
index 82add77..f51aa34 100644
--- a/drivers/scsi/arm/cumana_2.c
+++ b/drivers/scsi/arm/cumana_2.c
@@ -450,8 +450,8 @@
 
 	ec->irqaddr	= info->base + CUMANASCSI2_STATUS;
 	ec->irqmask	= STATUS_INT;
-	ec->irq_data	= info;
-	ec->ops		= &cumanascsi_2_ops;
+
+	ecard_setirq(ec, &cumanascsi_2_ops, info);
 
 	ret = fas216_init(host);
 	if (ret)
diff --git a/drivers/scsi/arm/eesox.c b/drivers/scsi/arm/eesox.c
index ed06a8c..cc5d513 100644
--- a/drivers/scsi/arm/eesox.c
+++ b/drivers/scsi/arm/eesox.c
@@ -569,8 +569,8 @@
 
 	ec->irqaddr	= base + EESOX_DMASTAT;
 	ec->irqmask	= EESOX_STAT_INTR;
-	ec->irq_data	= info;
-	ec->ops		= &eesoxscsi_ops;
+
+	ecard_setirq(ec, &eesoxscsi_ops, info);
 
 	device_create_file(&ec->dev, &dev_attr_bus_term);
 
diff --git a/drivers/scsi/arm/powertec.c b/drivers/scsi/arm/powertec.c
index 159047a..3cbd525 100644
--- a/drivers/scsi/arm/powertec.c
+++ b/drivers/scsi/arm/powertec.c
@@ -361,8 +361,8 @@
 
 	ec->irqaddr	= base + POWERTEC_INTR_STATUS;
 	ec->irqmask	= POWERTEC_INTR_BIT;
-	ec->irq_data	= info;
-	ec->ops		= &powertecscsi_ops;
+
+	ecard_setirq(ec, &powertecscsi_ops, info);
 
 	device_create_file(&ec->dev, &dev_attr_bus_term);
 
diff --git a/include/asm-arm/ecard.h b/include/asm-arm/ecard.h
index 3a6d3eb..8f1000e 100644
--- a/include/asm-arm/ecard.h
+++ b/include/asm-arm/ecard.h
@@ -121,7 +121,7 @@
 typedef struct expansion_card ecard_t;
 typedef unsigned long *loader_t;
 
-typedef struct {			/* Card handler routines	*/
+typedef struct expansion_card_ops {	/* Card handler routines	*/
 	void (*irqenable)(ecard_t *ec, int irqnr);
 	void (*irqdisable)(ecard_t *ec, int irqnr);
 	int  (*irqpending)(ecard_t *ec);
@@ -179,6 +179,8 @@
 	u64			dma_mask;
 };
 
+void ecard_setirq(struct expansion_card *ec, const struct expansion_card_ops *ops, void *irq_data);
+
 struct in_chunk_dir {
 	unsigned int start_offset;
 	union {