From 7b43ca708a767a5f68eeeb732c569c0f11a7d6f7 Mon Sep 17 00:00:00 2001 From: Inaky Perez-Gonzalez Date: Mon, 14 Sep 2009 14:10:16 -0700 Subject: wimax/i2400m: cache firmware on system suspend In preparation for a reset_resume implementation, have the firmware image be cached in memory when the system goes to suspend and released when out. This is needed in case the device resets during suspend; the driver can't load firmware until resume is completed or bad deadlocks happen. The modus operandi for this was copied from the Orinoco USB driver. The caching is done with a kobject to avoid race conditions when releasing it. The fw loader path is altered only to first check for a cached image before trying to load from disk. A Power Management event notifier is register to call i2400m_fw_cache() or i2400m_fw_uncache() which take care of the actual cache management. Signed-off-by: Inaky Perez-Gonzalez --- drivers/net/wimax/i2400m/driver.c | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'drivers/net/wimax/i2400m/driver.c') diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index f07d8527b03b..07d12be0cf81 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c @@ -66,6 +66,7 @@ #include #include #include +#include #define D_SUBMODULE driver #include "debug-levels.h" @@ -554,6 +555,51 @@ void i2400m_dev_stop(struct i2400m *i2400m) } +/* + * Listen to PM events to cache the firmware before suspend/hibernation + * + * When the device comes out of suspend, it might go into reset and + * firmware has to be uploaded again. At resume, most of the times, we + * can't load firmware images from disk, so we need to cache it. + * + * i2400m_fw_cache() will allocate a kobject and attach the firmware + * to it; that way we don't have to worry too much about the fw loader + * hitting a race condition. + * + * Note: modus operandi stolen from the Orinoco driver; thx. + */ +static +int i2400m_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, + void *unused) +{ + struct i2400m *i2400m = + container_of(notifier, struct i2400m, pm_notifier); + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(3, dev, "(i2400m %p pm_event %lx)\n", i2400m, pm_event); + switch (pm_event) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + i2400m_fw_cache(i2400m); + break; + case PM_POST_RESTORE: + /* Restore from hibernation failed. We need to clean + * up in exactly the same way, so fall through. */ + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + i2400m_fw_uncache(i2400m); + break; + + case PM_RESTORE_PREPARE: + default: + break; + } + d_fnend(3, dev, "(i2400m %p pm_event %lx) = void\n", i2400m, pm_event); + return NOTIFY_DONE; +} + + /* * The device has rebooted; fix up the device and the driver * @@ -738,6 +784,9 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) goto error_read_mac_addr; random_ether_addr(i2400m->src_mac_addr); + i2400m->pm_notifier.notifier_call = i2400m_pm_notifier; + register_pm_notifier(&i2400m->pm_notifier); + result = register_netdev(net_dev); /* Okey dokey, bring it up */ if (result < 0) { dev_err(dev, "cannot register i2400m network device: %d\n", @@ -783,6 +832,7 @@ error_wimax_dev_add: error_dev_start: unregister_netdev(net_dev); error_register_netdev: + unregister_pm_notifier(&i2400m->pm_notifier); error_read_mac_addr: error_bootrom_init: d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); @@ -809,6 +859,7 @@ void i2400m_release(struct i2400m *i2400m) wimax_dev_rm(&i2400m->wimax_dev); i2400m_dev_stop(i2400m); unregister_netdev(i2400m->wimax_dev.net_dev); + unregister_pm_notifier(&i2400m->pm_notifier); kfree(i2400m->bm_ack_buf); kfree(i2400m->bm_cmd_buf); d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); -- cgit v1.2.3