Commit 8fe2b65a authored by Michael Buesch's avatar Michael Buesch Committed by John W. Linville

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: default avatarMichael Buesch <mb@bu3sch.de>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 5100d5ac
...@@ -43,14 +43,16 @@ MODULE_DEVICE_TABLE(pcmcia, b43_pcmcia_tbl); ...@@ -43,14 +43,16 @@ MODULE_DEVICE_TABLE(pcmcia, b43_pcmcia_tbl);
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int b43_pcmcia_suspend(struct pcmcia_device *dev) static int b43_pcmcia_suspend(struct pcmcia_device *dev)
{ {
//TODO struct ssb_bus *ssb = dev->priv;
return 0;
return ssb_bus_suspend(ssb);
} }
static int b43_pcmcia_resume(struct pcmcia_device *dev) static int b43_pcmcia_resume(struct pcmcia_device *dev)
{ {
//TODO struct ssb_bus *ssb = dev->priv;
return 0;
return ssb_bus_resume(ssb);
} }
#else /* CONFIG_PM */ #else /* CONFIG_PM */
# define b43_pcmcia_suspend NULL # define b43_pcmcia_suspend NULL
......
...@@ -251,7 +251,7 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc) ...@@ -251,7 +251,7 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc)
calc_fast_powerup_delay(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) if (!cc->dev)
return; return;
......
...@@ -120,35 +120,12 @@ static void ssb_device_put(struct ssb_device *dev) ...@@ -120,35 +120,12 @@ static void ssb_device_put(struct ssb_device *dev)
put_device(dev->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) static int ssb_device_resume(struct device *dev)
{ {
struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
struct ssb_driver *ssb_drv; struct ssb_driver *ssb_drv;
struct ssb_bus *bus;
int err = 0; 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) { if (dev->driver) {
ssb_drv = drv_to_ssb_drv(dev->driver); ssb_drv = drv_to_ssb_drv(dev->driver);
if (ssb_drv && ssb_drv->resume) if (ssb_drv && ssb_drv->resume)
...@@ -160,27 +137,10 @@ static int ssb_device_resume(struct device *dev) ...@@ -160,27 +137,10 @@ static int ssb_device_resume(struct device *dev)
return err; 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) static int ssb_device_suspend(struct device *dev, pm_message_t state)
{ {
struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
struct ssb_driver *ssb_drv; struct ssb_driver *ssb_drv;
struct ssb_bus *bus;
int err = 0; int err = 0;
if (dev->driver) { if (dev->driver) {
...@@ -190,17 +150,44 @@ static int ssb_device_suspend(struct device *dev, pm_message_t state) ...@@ -190,17 +150,44 @@ static int ssb_device_suspend(struct device *dev, pm_message_t state)
if (err) if (err)
goto out; goto out;
} }
out:
return err;
}
int ssb_bus_resume(struct ssb_bus *bus)
{
int err;
bus = ssb_dev->bus; /* Reset HW state information in memory, so that HW is
bus->suspend_cnt++; * completely reinitialized. */
if (bus->suspend_cnt == bus->nr_devices) { bus->mapped_device = NULL;
/* All devices suspended. Shutdown the bus. */ #ifdef CONFIG_SSB_DRIVER_PCICORE
ssb_bus_suspend(bus, state); 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 0;
return err; }
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 #ifdef CONFIG_SSB_PCIHOST
int ssb_devices_freeze(struct ssb_bus *bus) int ssb_devices_freeze(struct ssb_bus *bus)
......
...@@ -18,6 +18,12 @@ ...@@ -18,6 +18,12 @@
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state) 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_save_state(dev);
pci_disable_device(dev); pci_disable_device(dev);
pci_set_power_state(dev, pci_choose_state(dev, state)); 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) ...@@ -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) static int ssb_pcihost_resume(struct pci_dev *dev)
{ {
struct ssb_bus *ssb = pci_get_drvdata(dev);
int err; int err;
pci_set_power_state(dev, 0); pci_set_power_state(dev, 0);
...@@ -34,6 +41,9 @@ static int ssb_pcihost_resume(struct pci_dev *dev) ...@@ -34,6 +41,9 @@ static int ssb_pcihost_resume(struct pci_dev *dev)
if (err) if (err)
return err; return err;
pci_restore_state(dev); pci_restore_state(dev);
err = ssb_bus_resume(ssb);
if (err)
return err;
return 0; return 0;
} }
......
...@@ -684,6 +684,29 @@ static int ssb_pcmcia_cor_setup(struct ssb_bus *bus, u8 cor) ...@@ -684,6 +684,29 @@ static int ssb_pcmcia_cor_setup(struct ssb_bus *bus, u8 cor)
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) void ssb_pcmcia_exit(struct ssb_bus *bus)
{ {
if (bus->bustype != SSB_BUSTYPE_PCMCIA) if (bus->bustype != SSB_BUSTYPE_PCMCIA)
...@@ -699,16 +722,7 @@ int ssb_pcmcia_init(struct ssb_bus *bus) ...@@ -699,16 +722,7 @@ int ssb_pcmcia_init(struct ssb_bus *bus)
if (bus->bustype != SSB_BUSTYPE_PCMCIA) if (bus->bustype != SSB_BUSTYPE_PCMCIA)
return 0; return 0;
/* Switch segment to a known state and sync err = ssb_pcmcia_hardware_setup(bus);
* 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);
if (err) if (err)
goto error; goto error;
......
...@@ -81,6 +81,7 @@ extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus, ...@@ -81,6 +81,7 @@ extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
u8 seg); u8 seg);
extern int ssb_pcmcia_get_invariants(struct ssb_bus *bus, extern int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
struct ssb_init_invariants *iv); 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 void ssb_pcmcia_exit(struct ssb_bus *bus);
extern int ssb_pcmcia_init(struct ssb_bus *bus); extern int ssb_pcmcia_init(struct ssb_bus *bus);
extern const struct ssb_bus_ops ssb_pcmcia_ops; extern const struct ssb_bus_ops ssb_pcmcia_ops;
...@@ -100,6 +101,10 @@ static inline int ssb_pcmcia_switch_segment(struct ssb_bus *bus, ...@@ -100,6 +101,10 @@ static inline int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
{ {
return 0; 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 void ssb_pcmcia_exit(struct ssb_bus *bus)
{ {
} }
......
...@@ -260,9 +260,6 @@ struct ssb_bus { ...@@ -260,9 +260,6 @@ struct ssb_bus {
struct ssb_device devices[SSB_MAX_NR_CORES]; struct ssb_device devices[SSB_MAX_NR_CORES];
u8 nr_devices; u8 nr_devices;
/* Reference count. Number of suspended devices. */
u8 suspend_cnt;
/* Software ID number for this bus. */ /* Software ID number for this bus. */
unsigned int busnumber; unsigned int busnumber;
...@@ -334,6 +331,13 @@ extern int ssb_bus_pcmciabus_register(struct ssb_bus *bus, ...@@ -334,6 +331,13 @@ extern int ssb_bus_pcmciabus_register(struct ssb_bus *bus,
extern void ssb_bus_unregister(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); extern u32 ssb_clockspeed(struct ssb_bus *bus);
/* Is the device enabled in hardware? */ /* Is the device enabled in hardware? */
......
...@@ -367,8 +367,7 @@ static inline bool ssb_chipco_available(struct ssb_chipcommon *cc) ...@@ -367,8 +367,7 @@ static inline bool ssb_chipco_available(struct ssb_chipcommon *cc)
extern void ssb_chipcommon_init(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);
extern void ssb_chipco_suspend(struct ssb_chipcommon *cc, pm_message_t state);
extern void ssb_chipco_resume(struct ssb_chipcommon *cc); extern void ssb_chipco_resume(struct ssb_chipcommon *cc);
extern void ssb_chipco_get_clockcpu(struct ssb_chipcommon *cc, extern void ssb_chipco_get_clockcpu(struct ssb_chipcommon *cc,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment