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 <mb@bu3sch.de> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
5100d5ac81
commit
8fe2b65a18
8 changed files with 89 additions and 68 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 @@ static int ssb_device_resume(struct device *dev)
|
|||
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,18 +150,45 @@ static int ssb_device_suspend(struct device *dev, pm_message_t state)
|
|||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -18,6 +18,12 @@
|
|||
#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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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? */
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue