aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax/i2400m/driver.c
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2009-09-14 17:10:16 -0400
committerInaky Perez-Gonzalez <inaky@linux.intel.com>2009-10-19 02:56:02 -0400
commit7b43ca708a767a5f68eeeb732c569c0f11a7d6f7 (patch)
tree8811c30691a5e4355c56e9b52a7f8fb9fe9108fb /drivers/net/wimax/i2400m/driver.c
parent3ef6129e57b04c116b1907b72c7a20720e6dde75 (diff)
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 <inaky@linux.intel.com>
Diffstat (limited to 'drivers/net/wimax/i2400m/driver.c')
-rw-r--r--drivers/net/wimax/i2400m/driver.c51
1 files changed, 51 insertions, 0 deletions
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 @@
66#include <linux/wimax/i2400m.h> 66#include <linux/wimax/i2400m.h>
67#include <linux/module.h> 67#include <linux/module.h>
68#include <linux/moduleparam.h> 68#include <linux/moduleparam.h>
69#include <linux/suspend.h>
69 70
70#define D_SUBMODULE driver 71#define D_SUBMODULE driver
71#include "debug-levels.h" 72#include "debug-levels.h"
@@ -555,6 +556,51 @@ void i2400m_dev_stop(struct i2400m *i2400m)
555 556
556 557
557/* 558/*
559 * Listen to PM events to cache the firmware before suspend/hibernation
560 *
561 * When the device comes out of suspend, it might go into reset and
562 * firmware has to be uploaded again. At resume, most of the times, we
563 * can't load firmware images from disk, so we need to cache it.
564 *
565 * i2400m_fw_cache() will allocate a kobject and attach the firmware
566 * to it; that way we don't have to worry too much about the fw loader
567 * hitting a race condition.
568 *
569 * Note: modus operandi stolen from the Orinoco driver; thx.
570 */
571static
572int i2400m_pm_notifier(struct notifier_block *notifier,
573 unsigned long pm_event,
574 void *unused)
575{
576 struct i2400m *i2400m =
577 container_of(notifier, struct i2400m, pm_notifier);
578 struct device *dev = i2400m_dev(i2400m);
579
580 d_fnstart(3, dev, "(i2400m %p pm_event %lx)\n", i2400m, pm_event);
581 switch (pm_event) {
582 case PM_HIBERNATION_PREPARE:
583 case PM_SUSPEND_PREPARE:
584 i2400m_fw_cache(i2400m);
585 break;
586 case PM_POST_RESTORE:
587 /* Restore from hibernation failed. We need to clean
588 * up in exactly the same way, so fall through. */
589 case PM_POST_HIBERNATION:
590 case PM_POST_SUSPEND:
591 i2400m_fw_uncache(i2400m);
592 break;
593
594 case PM_RESTORE_PREPARE:
595 default:
596 break;
597 }
598 d_fnend(3, dev, "(i2400m %p pm_event %lx) = void\n", i2400m, pm_event);
599 return NOTIFY_DONE;
600}
601
602
603/*
558 * The device has rebooted; fix up the device and the driver 604 * The device has rebooted; fix up the device and the driver
559 * 605 *
560 * Tear down the driver communication with the device, reload the 606 * Tear down the driver communication with the device, reload the
@@ -738,6 +784,9 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)
738 goto error_read_mac_addr; 784 goto error_read_mac_addr;
739 random_ether_addr(i2400m->src_mac_addr); 785 random_ether_addr(i2400m->src_mac_addr);
740 786
787 i2400m->pm_notifier.notifier_call = i2400m_pm_notifier;
788 register_pm_notifier(&i2400m->pm_notifier);
789
741 result = register_netdev(net_dev); /* Okey dokey, bring it up */ 790 result = register_netdev(net_dev); /* Okey dokey, bring it up */
742 if (result < 0) { 791 if (result < 0) {
743 dev_err(dev, "cannot register i2400m network device: %d\n", 792 dev_err(dev, "cannot register i2400m network device: %d\n",
@@ -783,6 +832,7 @@ error_wimax_dev_add:
783error_dev_start: 832error_dev_start:
784 unregister_netdev(net_dev); 833 unregister_netdev(net_dev);
785error_register_netdev: 834error_register_netdev:
835 unregister_pm_notifier(&i2400m->pm_notifier);
786error_read_mac_addr: 836error_read_mac_addr:
787error_bootrom_init: 837error_bootrom_init:
788 d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); 838 d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
@@ -809,6 +859,7 @@ void i2400m_release(struct i2400m *i2400m)
809 wimax_dev_rm(&i2400m->wimax_dev); 859 wimax_dev_rm(&i2400m->wimax_dev);
810 i2400m_dev_stop(i2400m); 860 i2400m_dev_stop(i2400m);
811 unregister_netdev(i2400m->wimax_dev.net_dev); 861 unregister_netdev(i2400m->wimax_dev.net_dev);
862 unregister_pm_notifier(&i2400m->pm_notifier);
812 kfree(i2400m->bm_ack_buf); 863 kfree(i2400m->bm_ack_buf);
813 kfree(i2400m->bm_cmd_buf); 864 kfree(i2400m->bm_cmd_buf);
814 d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); 865 d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);