[IRDA]: nsc-ircc: PM update

This patch brings the nsc-ircc code to a more up to date power
management scheme, following the current device model.

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Signed-off-by: Rudolf Marek <r.marek@sh.cvut.cz>
Signed-off-by: Samuel Ortiz <samuel.ortiz@nokia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c
index 282414b..81a4ccf 100644
--- a/drivers/net/irda/nsc-ircc.c
+++ b/drivers/net/irda/nsc-ircc.c
@@ -55,14 +55,12 @@
 #include <linux/rtnetlink.h>
 #include <linux/dma-mapping.h>
 #include <linux/pnp.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <asm/byteorder.h>
 
-#include <linux/pm.h>
-#include <linux/pm_legacy.h>
-
 #include <net/irda/wrapper.h>
 #include <net/irda/irda.h>
 #include <net/irda/irda_device.h>
@@ -74,6 +72,19 @@
 
 static char *driver_name = "nsc-ircc";
 
+/* Power Management */
+#define NSC_IRCC_DRIVER_NAME                  "nsc-ircc"
+static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state);
+static int nsc_ircc_resume(struct platform_device *dev);
+
+static struct platform_driver nsc_ircc_driver = {
+	.suspend	= nsc_ircc_suspend,
+	.resume		= nsc_ircc_resume,
+	.driver		= {
+		.name	= NSC_IRCC_DRIVER_NAME,
+	},
+};
+
 /* Module parameters */
 static int qos_mtt_bits = 0x07;  /* 1 ms or more */
 static int dongle_id;
@@ -164,7 +175,6 @@
 static int  nsc_ircc_net_close(struct net_device *dev);
 static int  nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev);
-static int nsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data);
 
 /* Globals */
 static int pnp_registered;
@@ -186,6 +196,12 @@
 	int reg;
 	int i = 0;
 
+	ret = platform_driver_register(&nsc_ircc_driver);
+        if (ret) {
+                IRDA_ERROR("%s, Can't register driver!\n", driver_name);
+                return ret;
+        }
+
  	/* Register with PnP subsystem to detect disable ports */
 	ret = pnp_register_driver(&nsc_ircc_pnp_driver);
 
@@ -274,6 +290,7 @@
 	}
 
 	if (ret) {
+		platform_driver_unregister(&nsc_ircc_driver);
 		pnp_unregister_driver(&nsc_ircc_pnp_driver);
 		pnp_registered = 0;
 	}
@@ -291,13 +308,13 @@
 {
 	int i;
 
-	pm_unregister_all(nsc_ircc_pmproc);
-
 	for (i = 0; i < ARRAY_SIZE(dev_self); i++) {
 		if (dev_self[i])
 			nsc_ircc_close(dev_self[i]);
 	}
 
+	platform_driver_unregister(&nsc_ircc_driver);
+
 	if (pnp_registered)
  		pnp_unregister_driver(&nsc_ircc_pnp_driver);
 
@@ -314,7 +331,6 @@
 {
 	struct net_device *dev;
 	struct nsc_ircc_cb *self;
-	struct pm_dev *pmdev;
 	void *ret;
 	int err, chip_index;
 
@@ -444,11 +460,18 @@
 	self->io.dongle_id = dongle_id;
 	nsc_ircc_init_dongle_interface(self->io.fir_base, dongle_id);
 
-        pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, nsc_ircc_pmproc);
-        if (pmdev)
-                pmdev->data = self;
+ 	self->pldev = platform_device_register_simple(NSC_IRCC_DRIVER_NAME,
+ 						      self->index, NULL, 0);
+ 	if (IS_ERR(self->pldev)) {
+ 		err = PTR_ERR(self->pldev);
+ 		goto out5;
+ 	}
+ 	platform_set_drvdata(self->pldev, self);
 
 	return chip_index;
+
+ out5:
+ 	unregister_netdev(dev);
  out4:
 	dma_free_coherent(NULL, self->tx_buff.truesize,
 			  self->tx_buff.head, self->tx_buff_dma);
@@ -479,6 +502,8 @@
 
         iobase = self->io.fir_base;
 
+	platform_device_unregister(self->pldev);
+
 	/* Remove netdevice */
 	unregister_netdev(self->netdev);
 
@@ -2278,47 +2303,85 @@
 	return &self->stats;
 }
 
-static void nsc_ircc_suspend(struct nsc_ircc_cb *self)
+static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state)
 {
-	IRDA_MESSAGE("%s, Suspending\n", driver_name);
+     	struct nsc_ircc_cb *self = platform_get_drvdata(dev);
+ 	int bank;
+	unsigned long flags;
+ 	int iobase = self->io.fir_base;
 
 	if (self->io.suspended)
-		return;
+		return 0;
 
-	nsc_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);
+		spin_lock_irqsave(&self->lock, flags);
+		/* Save current bank */
+		bank = inb(iobase+BSR);
+
+		/* Disable interrupts */
+		switch_bank(iobase, BANK0);
+		outb(0, iobase+IER);
+
+		/* Restore bank register */
+		outb(bank, iobase+BSR);
+
+		spin_unlock_irqrestore(&self->lock, flags);
+		free_irq(self->io.irq, self->netdev);
+		disable_dma(self->io.dma);
+	}
 	self->io.suspended = 1;
-}
+	rtnl_unlock();
 
-static void nsc_ircc_wakeup(struct nsc_ircc_cb *self)
-{
-	if (!self->io.suspended)
-		return;
-
-	nsc_ircc_setup(&self->io);
-	nsc_ircc_net_open(self->netdev);
-	
-	IRDA_MESSAGE("%s, Waking up\n", driver_name);
-
-	self->io.suspended = 0;
-}
-
-static int nsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data)
-{
-        struct nsc_ircc_cb *self = (struct nsc_ircc_cb*) dev->data;
-        if (self) {
-                switch (rqst) {
-                case PM_SUSPEND:
-                        nsc_ircc_suspend(self);
-                        break;
-                case PM_RESUME:
-                        nsc_ircc_wakeup(self);
-                        break;
-                }
-        }
 	return 0;
 }
 
+static int nsc_ircc_resume(struct platform_device *dev)
+{
+ 	struct nsc_ircc_cb *self = platform_get_drvdata(dev);
+ 	unsigned long flags;
+
+	if (!self->io.suspended)
+		return 0;
+
+	IRDA_DEBUG(1, "%s, Waking up\n", driver_name);
+
+	rtnl_lock();
+	nsc_ircc_setup(&self->io);
+	nsc_ircc_init_dongle_interface(self->io.fir_base, self->io.dongle_id);
+
+	if (netif_running(self->netdev)) {
+		if (request_irq(self->io.irq, nsc_ircc_interrupt, 0,
+				self->netdev->name, self->netdev)) {
+ 		    	IRDA_WARNING("%s, unable to allocate irq=%d\n",
+				     driver_name, self->io.irq);
+
+			/*
+			 * Don't fail resume process, just kill this
+			 * network interface
+			 */
+			unregister_netdevice(self->netdev);
+		} else {
+			spin_lock_irqsave(&self->lock, flags);
+			nsc_ircc_change_speed(self, self->io.speed);
+			spin_unlock_irqrestore(&self->lock, flags);
+			netif_device_attach(self->netdev);
+		}
+
+	} else {
+		spin_lock_irqsave(&self->lock, flags);
+		nsc_ircc_change_speed(self, 9600);
+		spin_unlock_irqrestore(&self->lock, flags);
+	}
+	self->io.suspended = 0;
+	rtnl_unlock();
+
+ 	return 0;
+}
+
 MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
 MODULE_DESCRIPTION("NSC IrDA Device Driver");
 MODULE_LICENSE("GPL");