From: Michael Buesch <mb@bu3sch.de>
To: John Linville <linville@tuxdriver.com>
Cc: bcm43xx-dev@lists.berlios.de, linux-wireless@vger.kernel.org
Subject: [PATCH] ssb: Turn suspend/resume upside down
Date: Sun, 30 Mar 2008 00:10:50 +0100 [thread overview]
Message-ID: <200803300010.51166.mb@bu3sch.de> (raw)
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>
---
John, this is for 2.6.26
Index: wireless-testing/drivers/ssb/main.c
===================================================================
--- wireless-testing.orig/drivers/ssb/main.c 2008-03-29 20:09:09.000000000 +0100
+++ wireless-testing/drivers/ssb/main.c 2008-03-29 23:52:26.000000000 +0100
@@ -117,93 +117,80 @@ static struct ssb_device *ssb_device_get
static 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;
Index: wireless-testing/drivers/ssb/pcmcia.c
===================================================================
--- wireless-testing.orig/drivers/ssb/pcmcia.c 2008-03-29 20:09:09.000000000 +0100
+++ wireless-testing/drivers/ssb/pcmcia.c 2008-03-29 22:37:46.000000000 +0100
@@ -681,12 +681,35 @@ static int ssb_pcmcia_cor_setup(struct s
return 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);
Index: wireless-testing/drivers/ssb/ssb_private.h
===================================================================
--- wireless-testing.orig/drivers/ssb/ssb_private.h 2008-03-22 17:51:22.000000000 +0100
+++ wireless-testing/drivers/ssb/ssb_private.h 2008-03-29 22:37:20.000000000 +0100
@@ -78,12 +78,13 @@ extern int ssb_pcmcia_switch_core(struct
extern 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;
Index: wireless-testing/drivers/ssb/driver_chipcommon.c
===================================================================
--- wireless-testing.orig/drivers/ssb/driver_chipcommon.c 2008-02-29 11:56:41.000000000 +0100
+++ wireless-testing/drivers/ssb/driver_chipcommon.c 2008-03-29 23:35:29.000000000 +0100
@@ -248,13 +248,13 @@ void ssb_chipcommon_init(struct ssb_chip
return; /* 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);
}
Index: wireless-testing/drivers/ssb/pcihost_wrapper.c
===================================================================
--- wireless-testing.orig/drivers/ssb/pcihost_wrapper.c 2008-02-15 21:39:59.000000000 +0100
+++ wireless-testing/drivers/ssb/pcihost_wrapper.c 2008-03-29 23:39:48.000000000 +0100
@@ -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
Index: wireless-testing/include/linux/ssb/ssb.h
===================================================================
--- wireless-testing.orig/include/linux/ssb/ssb.h 2008-03-22 17:51:22.000000000 +0100
+++ wireless-testing/include/linux/ssb/ssb.h 2008-03-29 23:32:37.000000000 +0100
@@ -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(st
struct 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. */
Index: wireless-testing/include/linux/ssb/ssb_driver_chipcommon.h
===================================================================
--- wireless-testing.orig/include/linux/ssb/ssb_driver_chipcommon.h 2008-02-29 11:56:43.000000000 +0100
+++ wireless-testing/include/linux/ssb/ssb_driver_chipcommon.h 2008-03-29 23:35:47.000000000 +0100
@@ -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);
Index: wireless-testing/drivers/net/wireless/b43/pcmcia.c
===================================================================
--- wireless-testing.orig/drivers/net/wireless/b43/pcmcia.c 2008-03-29 20:09:09.000000000 +0100
+++ wireless-testing/drivers/net/wireless/b43/pcmcia.c 2008-03-29 23:40:58.000000000 +0100
@@ -40,20 +40,22 @@ static /*const */ struct pcmcia_device_i
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
# define b43_pcmcia_resume NULL
#endif /* CONFIG_PM */
reply other threads:[~2008-03-29 23:13 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=200803300010.51166.mb@bu3sch.de \
--to=mb@bu3sch.de \
--cc=bcm43xx-dev@lists.berlios.de \
--cc=linux-wireless@vger.kernel.org \
--cc=linville@tuxdriver.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).