ssb: Turn suspend/resume upside down
PATCH 1/1 ssb: Turn suspend/resume upside down
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 <[email protected]>
---
John, this is for 2.6.26
drivers/ssb/main.c +34 -47
@@ -117,93 +117,80 @@ static struct ssb_device *ssb_device_getstatic 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;
drivers/ssb/pcmcia.c +24 -10
@@ -681,12 +681,35 @@ static int ssb_pcmcia_cor_setup(struct sreturn 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);
drivers/ssb/ssb_private.h +5 -0
@@ -78,12 +78,13 @@ extern int ssb_pcmcia_switch_core(structextern 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 ;
drivers/ssb/driver_chipcommon.c +1 -1
@@ -248,13 +248,13 @@ void ssb_chipcommon_init(struct ssb_chipreturn ;/* 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 ); }
drivers/ssb/pcihost_wrapper.c +10 -0
@@ -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
include/linux/ssb/ssb.h +7 -3
@@ -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(ststruct 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. */
include/linux/ssb/ssb_driver_chipcommon.h +1 -2
@@ -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);
drivers/net/wireless/b43/pcmcia.c +6 -4
@@ -40,20 +40,22 @@ static /*const */ struct pcmcia_device_iMODULE_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 */