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>
 #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
 
        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;
 
                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)
        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) {
                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)
 
 #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));
 
 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);
        if (err)
                return err;
        pci_restore_state(dev);
+       err = ssb_bus_resume(ssb);
+       if (err)
+               return err;
 
        return 0;
 }
 
        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)
        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;
 
 
                                     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;
 {
        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)
 {
 }
 
        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;
 
 
 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? */
 
 
 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,