ssb: Turn suspend/resume upside down
Michael Buesch [Sat, 29 Mar 2008 23:10:50 +0000 (00:10 +0100)]
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@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

drivers/net/wireless/b43/pcmcia.c
drivers/ssb/driver_chipcommon.c
drivers/ssb/main.c
drivers/ssb/pcihost_wrapper.c
drivers/ssb/pcmcia.c
drivers/ssb/ssb_private.h
include/linux/ssb/ssb.h
include/linux/ssb/ssb_driver_chipcommon.h

index 371e4a1..b8aa163 100644 (file)
@@ -43,14 +43,16 @@ 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
index 45b672a..571f4fd 100644 (file)
@@ -251,7 +251,7 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc)
        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;
index 2fcfd73..c0cbdba 100644 (file)
@@ -120,35 +120,12 @@ static void ssb_device_put(struct ssb_device *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)
@@ -160,27 +137,10 @@ 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) {
@@ -190,17 +150,44 @@ static int ssb_device_suspend(struct device *dev, pm_message_t state)
                if (err)
                        goto out;
        }
+out:
+       return err;
+}
+
+int ssb_bus_resume(struct ssb_bus *bus)
+{
+       int err;
 
-       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);
+       /* 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
+
+       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)
index 82a10ab..e82db4a 100644 (file)
 #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));
@@ -27,6 +33,7 @@ static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state)
 
 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);
@@ -34,6 +41,9 @@ static int ssb_pcihost_resume(struct pci_dev *dev)
        if (err)
                return err;
        pci_restore_state(dev);
+       err = ssb_bus_resume(ssb);
+       if (err)
+               return err;
 
        return 0;
 }
index d674cef..dcaf241 100644 (file)
@@ -684,6 +684,29 @@ static int ssb_pcmcia_cor_setup(struct ssb_bus *bus, u8 cor)
        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)
@@ -699,16 +722,7 @@ int ssb_pcmcia_init(struct ssb_bus *bus)
        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;
 
index a83bf7a..ebc32d8 100644 (file)
@@ -81,6 +81,7 @@ 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;
@@ -100,6 +101,10 @@ static inline int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
 {
        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)
 {
 }
index 8644e03..a8ca396 100644 (file)
@@ -260,9 +260,6 @@ struct ssb_bus {
        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;
 
@@ -334,6 +331,13 @@ extern int ssb_bus_pcmciabus_register(struct ssb_bus *bus,
 
 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? */
index b548a54..7d7e03d 100644 (file)
@@ -367,8 +367,7 @@ static inline bool ssb_chipco_available(struct ssb_chipcommon *cc)
 
 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,