Search Linux Wireless

[PATCH] ssb: Turn suspend/resume upside down

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Turn the SSB bus suspend mechanism upside down.
Instead of deciding by an internal reference count when to suspend/resume,
let the parent bus call us in their suspend/resume routine.

Signed-off-by: Michael Buesch <mb@xxxxxxxxx>

---

John, this is for 2.6.26


Index: wireless-testing/drivers/ssb/main.c
===================================================================
--- wireless-testing.orig/drivers/ssb/main.c	2008-03-29 20:09:09.000000000 +0100
+++ wireless-testing/drivers/ssb/main.c	2008-03-29 23:52:26.000000000 +0100
@@ -117,93 +117,80 @@ static struct ssb_device *ssb_device_get
 static void ssb_device_put(struct ssb_device *dev)
 {
 	if (dev)
 		put_device(dev->dev);
 }
 
-static int ssb_bus_resume(struct ssb_bus *bus)
-{
-	int err;
-
-	ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1);
-	err = ssb_pcmcia_init(bus);
-	if (err) {
-		/* No need to disable XTAL, as we don't have one on PCMCIA. */
-		return err;
-	}
-	ssb_chipco_resume(&bus->chipco);
-
-	return 0;
-}
-
 static int ssb_device_resume(struct device *dev)
 {
 	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
 	struct ssb_driver *ssb_drv;
-	struct ssb_bus *bus;
 	int err = 0;
 
-	bus = ssb_dev->bus;
-	if (bus->suspend_cnt == bus->nr_devices) {
-		err = ssb_bus_resume(bus);
-		if (err)
-			return err;
-	}
-	bus->suspend_cnt--;
 	if (dev->driver) {
 		ssb_drv = drv_to_ssb_drv(dev->driver);
 		if (ssb_drv && ssb_drv->resume)
 			err = ssb_drv->resume(ssb_dev);
 		if (err)
 			goto out;
 	}
 out:
 	return err;
 }
 
-static void ssb_bus_suspend(struct ssb_bus *bus, pm_message_t state)
-{
-	ssb_chipco_suspend(&bus->chipco, state);
-	ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0);
-
-	/* Reset HW state information in memory, so that HW is
-	 * completely reinitialized on resume. */
-	bus->mapped_device = NULL;
-#ifdef CONFIG_SSB_DRIVER_PCICORE
-	bus->pcicore.setup_done = 0;
-#endif
-#ifdef CONFIG_SSB_DEBUG
-	bus->powered_up = 0;
-#endif
-}
-
 static int ssb_device_suspend(struct device *dev, pm_message_t state)
 {
 	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
 	struct ssb_driver *ssb_drv;
-	struct ssb_bus *bus;
 	int err = 0;
 
 	if (dev->driver) {
 		ssb_drv = drv_to_ssb_drv(dev->driver);
 		if (ssb_drv && ssb_drv->suspend)
 			err = ssb_drv->suspend(ssb_dev, state);
 		if (err)
 			goto out;
 	}
+out:
+	return err;
+}
+
+int ssb_bus_resume(struct ssb_bus *bus)
+{
+	int err;
+
+	/* Reset HW state information in memory, so that HW is
+	 * completely reinitialized. */
+	bus->mapped_device = NULL;
+#ifdef CONFIG_SSB_DRIVER_PCICORE
+	bus->pcicore.setup_done = 0;
+#endif
 
-	bus = ssb_dev->bus;
-	bus->suspend_cnt++;
-	if (bus->suspend_cnt == bus->nr_devices) {
-		/* All devices suspended. Shutdown the bus. */
-		ssb_bus_suspend(bus, state);
+	err = ssb_bus_powerup(bus, 0);
+	if (err)
+		return err;
+	err = ssb_pcmcia_hardware_setup(bus);
+	if (err) {
+		ssb_bus_may_powerdown(bus);
+		return err;
 	}
+	ssb_chipco_resume(&bus->chipco);
+	ssb_bus_may_powerdown(bus);
 
-out:
-	return err;
+	return 0;
+}
+EXPORT_SYMBOL(ssb_bus_resume);
+
+int ssb_bus_suspend(struct ssb_bus *bus)
+{
+	ssb_chipco_suspend(&bus->chipco);
+	ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0);
+
+	return 0;
 }
+EXPORT_SYMBOL(ssb_bus_suspend);
 
 #ifdef CONFIG_SSB_PCIHOST
 int ssb_devices_freeze(struct ssb_bus *bus)
 {
 	struct ssb_device *dev;
 	struct ssb_driver *drv;
Index: wireless-testing/drivers/ssb/pcmcia.c
===================================================================
--- wireless-testing.orig/drivers/ssb/pcmcia.c	2008-03-29 20:09:09.000000000 +0100
+++ wireless-testing/drivers/ssb/pcmcia.c	2008-03-29 22:37:46.000000000 +0100
@@ -681,12 +681,35 @@ static int ssb_pcmcia_cor_setup(struct s
 		return err;
 	msleep(40);
 
 	return 0;
 }
 
+/* Initialize the PCMCIA hardware. This is called on Init and Resume. */
+int ssb_pcmcia_hardware_setup(struct ssb_bus *bus)
+{
+	int err;
+
+	if (bus->bustype != SSB_BUSTYPE_PCMCIA)
+		return 0;
+
+	/* Switch segment to a known state and sync
+	 * bus->mapped_pcmcia_seg with hardware state. */
+	ssb_pcmcia_switch_segment(bus, 0);
+	/* Init the COR register. */
+	err = ssb_pcmcia_cor_setup(bus, CISREG_COR);
+	if (err)
+		return err;
+	/* Some cards also need this register to get poked. */
+	err = ssb_pcmcia_cor_setup(bus, CISREG_COR + 0x80);
+	if (err)
+		return err;
+
+	return 0;
+}
+
 void ssb_pcmcia_exit(struct ssb_bus *bus)
 {
 	if (bus->bustype != SSB_BUSTYPE_PCMCIA)
 		return;
 
 	device_remove_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom);
@@ -696,22 +719,13 @@ int ssb_pcmcia_init(struct ssb_bus *bus)
 {
 	int err;
 
 	if (bus->bustype != SSB_BUSTYPE_PCMCIA)
 		return 0;
 
-	/* Switch segment to a known state and sync
-	 * bus->mapped_pcmcia_seg with hardware state. */
-	ssb_pcmcia_switch_segment(bus, 0);
-
-	/* Init the COR register. */
-	err = ssb_pcmcia_cor_setup(bus, CISREG_COR);
-	if (err)
-		goto error;
-	/* Some cards also need this register to get poked. */
-	err = ssb_pcmcia_cor_setup(bus, CISREG_COR + 0x80);
+	err = ssb_pcmcia_hardware_setup(bus);
 	if (err)
 		goto error;
 
 	bus->sprom_size = SSB_PCMCIA_SPROM_SIZE;
 	mutex_init(&bus->sprom_mutex);
 	err = device_create_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom);
Index: wireless-testing/drivers/ssb/ssb_private.h
===================================================================
--- wireless-testing.orig/drivers/ssb/ssb_private.h	2008-03-22 17:51:22.000000000 +0100
+++ wireless-testing/drivers/ssb/ssb_private.h	2008-03-29 22:37:20.000000000 +0100
@@ -78,12 +78,13 @@ extern int ssb_pcmcia_switch_core(struct
 extern int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
 				     u8 coreidx);
 extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
 				     u8 seg);
 extern int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
 				     struct ssb_init_invariants *iv);
+extern int ssb_pcmcia_hardware_setup(struct ssb_bus *bus);
 extern void ssb_pcmcia_exit(struct ssb_bus *bus);
 extern int ssb_pcmcia_init(struct ssb_bus *bus);
 extern const struct ssb_bus_ops ssb_pcmcia_ops;
 #else /* CONFIG_SSB_PCMCIAHOST */
 static inline int ssb_pcmcia_switch_core(struct ssb_bus *bus,
 					 struct ssb_device *dev)
@@ -97,12 +98,16 @@ static inline int ssb_pcmcia_switch_core
 }
 static inline int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
 					    u8 seg)
 {
 	return 0;
 }
+static inline int ssb_pcmcia_hardware_setup(struct ssb_bus *bus)
+{
+	return 0;
+}
 static inline void ssb_pcmcia_exit(struct ssb_bus *bus)
 {
 }
 static inline int ssb_pcmcia_init(struct ssb_bus *bus)
 {
 	return 0;
Index: wireless-testing/drivers/ssb/driver_chipcommon.c
===================================================================
--- wireless-testing.orig/drivers/ssb/driver_chipcommon.c	2008-02-29 11:56:41.000000000 +0100
+++ wireless-testing/drivers/ssb/driver_chipcommon.c	2008-03-29 23:35:29.000000000 +0100
@@ -248,13 +248,13 @@ void ssb_chipcommon_init(struct ssb_chip
 		return; /* We don't have a ChipCommon */
 	chipco_powercontrol_init(cc);
 	ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
 	calc_fast_powerup_delay(cc);
 }
 
-void ssb_chipco_suspend(struct ssb_chipcommon *cc, pm_message_t state)
+void ssb_chipco_suspend(struct ssb_chipcommon *cc)
 {
 	if (!cc->dev)
 		return;
 	ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW);
 }
 
Index: wireless-testing/drivers/ssb/pcihost_wrapper.c
===================================================================
--- wireless-testing.orig/drivers/ssb/pcihost_wrapper.c	2008-02-15 21:39:59.000000000 +0100
+++ wireless-testing/drivers/ssb/pcihost_wrapper.c	2008-03-29 23:39:48.000000000 +0100
@@ -15,28 +15,38 @@
 #include <linux/ssb/ssb.h>
 
 
 #ifdef CONFIG_PM
 static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state)
 {
+	struct ssb_bus *ssb = pci_get_drvdata(dev);
+	int err;
+
+	err = ssb_bus_suspend(ssb);
+	if (err)
+		return err;
 	pci_save_state(dev);
 	pci_disable_device(dev);
 	pci_set_power_state(dev, pci_choose_state(dev, state));
 
 	return 0;
 }
 
 static int ssb_pcihost_resume(struct pci_dev *dev)
 {
+	struct ssb_bus *ssb = pci_get_drvdata(dev);
 	int err;
 
 	pci_set_power_state(dev, 0);
 	err = pci_enable_device(dev);
 	if (err)
 		return err;
 	pci_restore_state(dev);
+	err = ssb_bus_resume(ssb);
+	if (err)
+		return err;
 
 	return 0;
 }
 #else /* CONFIG_PM */
 # define ssb_pcihost_suspend	NULL
 # define ssb_pcihost_resume	NULL
Index: wireless-testing/include/linux/ssb/ssb.h
===================================================================
--- wireless-testing.orig/include/linux/ssb/ssb.h	2008-03-22 17:51:22.000000000 +0100
+++ wireless-testing/include/linux/ssb/ssb.h	2008-03-29 23:32:37.000000000 +0100
@@ -257,15 +257,12 @@ struct ssb_bus {
 	u8 chip_package;
 
 	/* List of devices (cores) on the backplane. */
 	struct ssb_device devices[SSB_MAX_NR_CORES];
 	u8 nr_devices;
 
-	/* Reference count. Number of suspended devices. */
-	u8 suspend_cnt;
-
 	/* Software ID number for this bus. */
 	unsigned int busnumber;
 
 	/* The ChipCommon device (if available). */
 	struct ssb_chipcommon chipco;
 	/* The PCI-core device (if available). */
@@ -331,12 +328,19 @@ extern int ssb_bus_pcmciabus_register(st
 				      struct pcmcia_device *pcmcia_dev,
 				      unsigned long baseaddr);
 #endif /* CONFIG_SSB_PCMCIAHOST */
 
 extern void ssb_bus_unregister(struct ssb_bus *bus);
 
+/* Suspend a SSB bus.
+ * Call this from the parent bus suspend routine. */
+extern int ssb_bus_suspend(struct ssb_bus *bus);
+/* Resume a SSB bus.
+ * Call this from the parent bus resume routine. */
+extern int ssb_bus_resume(struct ssb_bus *bus);
+
 extern u32 ssb_clockspeed(struct ssb_bus *bus);
 
 /* Is the device enabled in hardware? */
 int ssb_device_is_enabled(struct ssb_device *dev);
 /* Enable a device and pass device-specific SSB_TMSLOW flags.
  * If no device-specific flags are available, use 0. */
Index: wireless-testing/include/linux/ssb/ssb_driver_chipcommon.h
===================================================================
--- wireless-testing.orig/include/linux/ssb/ssb_driver_chipcommon.h	2008-02-29 11:56:43.000000000 +0100
+++ wireless-testing/include/linux/ssb/ssb_driver_chipcommon.h	2008-03-29 23:35:47.000000000 +0100
@@ -364,14 +364,13 @@ static inline bool ssb_chipco_available(
 {
 	return (cc->dev != NULL);
 }
 
 extern void ssb_chipcommon_init(struct ssb_chipcommon *cc);
 
-#include <linux/pm.h>
-extern void ssb_chipco_suspend(struct ssb_chipcommon *cc, pm_message_t state);
+extern void ssb_chipco_suspend(struct ssb_chipcommon *cc);
 extern void ssb_chipco_resume(struct ssb_chipcommon *cc);
 
 extern void ssb_chipco_get_clockcpu(struct ssb_chipcommon *cc,
                                     u32 *plltype, u32 *n, u32 *m);
 extern void ssb_chipco_get_clockcontrol(struct ssb_chipcommon *cc,
 					u32 *plltype, u32 *n, u32 *m);
Index: wireless-testing/drivers/net/wireless/b43/pcmcia.c
===================================================================
--- wireless-testing.orig/drivers/net/wireless/b43/pcmcia.c	2008-03-29 20:09:09.000000000 +0100
+++ wireless-testing/drivers/net/wireless/b43/pcmcia.c	2008-03-29 23:40:58.000000000 +0100
@@ -40,20 +40,22 @@ static /*const */ struct pcmcia_device_i
 
 MODULE_DEVICE_TABLE(pcmcia, b43_pcmcia_tbl);
 
 #ifdef CONFIG_PM
 static int b43_pcmcia_suspend(struct pcmcia_device *dev)
 {
-	//TODO
-	return 0;
+	struct ssb_bus *ssb = dev->priv;
+
+	return ssb_bus_suspend(ssb);
 }
 
 static int b43_pcmcia_resume(struct pcmcia_device *dev)
 {
-	//TODO
-	return 0;
+	struct ssb_bus *ssb = dev->priv;
+
+	return ssb_bus_resume(ssb);
 }
 #else /* CONFIG_PM */
 # define b43_pcmcia_suspend		NULL
 # define b43_pcmcia_resume		NULL
 #endif /* CONFIG_PM */
 
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux