From: Dmitry Torokhov <[email-masked]>

smsc-ircc2 - avoid closing network device when suspending; just release
interrupt and disable DMA ourselves.  Also make sure to reset chip when
resuming.

Signed-off-by: Dmitry Torokhov <[email-masked]>
Cc: Jean Tourrilhes <[email-masked]>
Signed-off-by: Andrew Morton <[email-masked]>
---

 drivers/net/irda/smsc-ircc2.c |  127 +++++++++++++++++++++++++++---------------
 1 files changed, 84 insertions(+), 43 deletions(-)

diff -puN drivers/net/irda/smsc-ircc2.c~smsc-ircc2-pm-cleanup-do-not-close-device-when-suspending drivers/net/irda/smsc-ircc2.c
--- devel/drivers/net/irda/smsc-ircc2.c~smsc-ircc2-pm-cleanup-do-not-close-device-when-suspending	2005-09-13 18:02:26.000000000 -0700
+++ devel-akpm/drivers/net/irda/smsc-ircc2.c	2005-09-13 18:02:26.000000000 -0700
@@ -638,21 +638,14 @@ static void smsc_ircc_setup_qos(struct s
  */
 static void smsc_ircc_init_chip(struct smsc_ircc_cb *self)
 {
-	int iobase, ir_mode, ctrl, fast;
-
-	IRDA_ASSERT(self != NULL, return;);
-
-	iobase = self->io.fir_base;
-	ir_mode = IRCC_CFGA_IRDA_SIR_A;
-	ctrl = 0;
-	fast = 0;
+	int iobase = self->io.fir_base;
 
 	register_bank(iobase, 0);
 	outb(IRCC_MASTER_RESET, iobase + IRCC_MASTER);
 	outb(0x00, iobase + IRCC_MASTER);
 
 	register_bank(iobase, 1);
-	outb(((inb(iobase + IRCC_SCE_CFGA) & 0x87) | ir_mode),
+	outb(((inb(iobase + IRCC_SCE_CFGA) & 0x87) | IRCC_CFGA_IRDA_SIR_A),
 	     iobase + IRCC_SCE_CFGA);
 
 #ifdef smsc_669 /* Uses pin 88/89 for Rx/Tx */
@@ -666,10 +659,10 @@ static void smsc_ircc_init_chip(struct s
 	outb(SMSC_IRCC2_FIFO_THRESHOLD, iobase + IRCC_FIFO_THRESHOLD);
 
 	register_bank(iobase, 4);
-	outb((inb(iobase + IRCC_CONTROL) & 0x30) | ctrl, iobase + IRCC_CONTROL);
+	outb((inb(iobase + IRCC_CONTROL) & 0x30), iobase + IRCC_CONTROL);
 
 	register_bank(iobase, 0);
-	outb(fast, iobase + IRCC_LCR_A);
+	outb(0, iobase + IRCC_LCR_A);
 
 	smsc_ircc_set_sir_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED);
 
@@ -1556,6 +1549,46 @@ static int ircc_is_receiving(struct smsc
 }
 #endif /* unused */
 
+static int smsc_ircc_request_irq(struct smsc_ircc_cb *self)
+{
+	int error;
+
+	error = request_irq(self->io.irq, smsc_ircc_interrupt, 0,
+			    self->netdev->name, self->netdev);
+	if (error)
+		IRDA_DEBUG(0, "%s(), unable to allocate irq=%d, err=%d\n",
+			   __FUNCTION__, self->io.irq, error);
+
+	return error;
+}
+
+static void smsc_ircc_start_interrupts(struct smsc_ircc_cb *self)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&self->lock, flags);
+
+	self->io.speed = 0;
+	smsc_ircc_change_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED);
+
+	spin_unlock_irqrestore(&self->lock, flags);
+}
+
+static void smsc_ircc_stop_interrupts(struct smsc_ircc_cb *self)
+{
+	int iobase = self->io.fir_base;
+	unsigned long flags;
+
+	spin_lock_irqsave(&self->lock, flags);
+
+	register_bank(iobase, 0);
+	outb(0, iobase + IRCC_IER);
+	outb(IRCC_MASTER_RESET, iobase + IRCC_MASTER);
+	outb(0x00, iobase + IRCC_MASTER);
+
+	spin_unlock_irqrestore(&self->lock, flags);
+}
+
 
 /*
  * Function smsc_ircc_net_open (dev)
@@ -1567,7 +1600,6 @@ static int smsc_ircc_net_open(struct net
 {
 	struct smsc_ircc_cb *self;
 	char hwname[16];
-	unsigned long flags;
 
 	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
 
@@ -1575,6 +1607,11 @@ static int smsc_ircc_net_open(struct net
 	self = netdev_priv(dev);
 	IRDA_ASSERT(self != NULL, return 0;);
 
+	if (self->io.suspended) {
+		IRDA_DEBUG(0, "%s(), device is suspended\n", __FUNCTION__);
+		return -EAGAIN;
+	}
+
 	if (request_irq(self->io.irq, smsc_ircc_interrupt, 0, dev->name,
 			(void *) dev)) {
 		IRDA_DEBUG(0, "%s(), unable to allocate irq=%d\n",
@@ -1582,11 +1619,7 @@ static int smsc_ircc_net_open(struct net
 		return -EAGAIN;
 	}
 
-	spin_lock_irqsave(&self->lock, flags);
-	/*smsc_ircc_sir_start(self);*/
-	self->io.speed = 0;
-	smsc_ircc_change_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED);
-	spin_unlock_irqrestore(&self->lock, flags);
+	smsc_ircc_start_interrupts(self);
 
 	/* Give self a hardware name */
 	/* It would be cool to offer the chip revision here - Jean II */
@@ -1639,7 +1672,12 @@ static int smsc_ircc_net_close(struct ne
 		irlap_close(self->irlap);
 	self->irlap = NULL;
 
-	free_irq(self->io.irq, dev);
+	smsc_ircc_stop_interrupts(self);
+
+	/* if we are called from smsc_ircc_resume we don't have IRQ reserved */
+	if (!self->io.suspended)
+		free_irq(self->io.irq, dev);
+
 	disable_dma(self->io.dma);
 	free_dma(self->io.dma);
 
@@ -1650,11 +1688,18 @@ static int smsc_ircc_suspend(struct devi
 {
 	struct smsc_ircc_cb *self = dev_get_drvdata(dev);
 
-	IRDA_MESSAGE("%s, Suspending\n", driver_name);
-
 	if (level == SUSPEND_DISABLE && !self->io.suspended) {
-		smsc_ircc_net_close(self->netdev);
+		IRDA_DEBUG(1, "%s, Suspending\n", driver_name);
+
+		rtnl_lock();
+		if (netif_running(self->netdev)) {
+			netif_device_detach(self->netdev);
+			smsc_ircc_stop_interrupts(self);
+			free_irq(self->io.irq, self->netdev);
+			disable_dma(self->io.dma);
+		}
 		self->io.suspended = 1;
+		rtnl_unlock();
 	}
 
 	return 0;
@@ -1665,11 +1710,25 @@ static int smsc_ircc_resume(struct devic
 	struct smsc_ircc_cb *self = dev_get_drvdata(dev);
 
 	if (level == RESUME_ENABLE && self->io.suspended) {
+		IRDA_DEBUG(1, "%s, Waking up\n", driver_name);
 
-		smsc_ircc_net_open(self->netdev);
+		rtnl_lock();
+		smsc_ircc_init_chip(self);
+		if (netif_running(self->netdev)) {
+			if (smsc_ircc_request_irq(self)) {
+				/*
+				 * Don't fail resume process, just kill this
+				 * network interface
+				 */
+				unregister_netdevice(self->netdev);
+			} else {
+				enable_dma(self->io.dma);
+				smsc_ircc_start_interrupts(self);
+				netif_device_attach(self->netdev);
+			}
+		}
 		self->io.suspended = 0;
-
-		IRDA_MESSAGE("%s, Waking up\n", driver_name);
+		rtnl_unlock();
 	}
 	return 0;
 }
@@ -1682,9 +1741,6 @@ static int smsc_ircc_resume(struct devic
  */
 static int __exit smsc_ircc_close(struct smsc_ircc_cb *self)
 {
-	int iobase;
-	unsigned long flags;
-
 	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
 
 	IRDA_ASSERT(self != NULL, return -1;);
@@ -1694,22 +1750,7 @@ static int __exit smsc_ircc_close(struct
 	/* Remove netdevice */
 	unregister_netdev(self->netdev);
 
-	/* Make sure the irq handler is not exectuting */
-	spin_lock_irqsave(&self->lock, flags);
-
-	/* Stop interrupts */
-	iobase = self->io.fir_base;
-	register_bank(iobase, 0);
-	outb(0, iobase + IRCC_IER);
-	outb(IRCC_MASTER_RESET, iobase + IRCC_MASTER);
-	outb(0x00, iobase + IRCC_MASTER);
-#if 0
-	/* Reset to SIR mode */
-	register_bank(iobase, 1);
-        outb(IRCC_CFGA_IRDA_SIR_A|IRCC_CFGA_TX_POLARITY, iobase + IRCC_SCE_CFGA);
-        outb(IRCC_CFGB_IR, iobase + IRCC_SCE_CFGB);
-#endif
-	spin_unlock_irqrestore(&self->lock, flags);
+	smsc_ircc_stop_interrupts(self);
 
 	/* Release the PORTS that this driver is using */
 	IRDA_DEBUG(0, "%s(), releasing 0x%03x\n",  __FUNCTION__,
_