diff options
author | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2009-09-14 17:10:16 -0400 |
---|---|---|
committer | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2009-10-19 02:56:02 -0400 |
commit | 7b43ca708a767a5f68eeeb732c569c0f11a7d6f7 (patch) | |
tree | 8811c30691a5e4355c56e9b52a7f8fb9fe9108fb /drivers/net/wimax/i2400m/driver.c | |
parent | 3ef6129e57b04c116b1907b72c7a20720e6dde75 (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.c | 51 |
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 | */ | ||
571 | static | ||
572 | int 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: | |||
783 | error_dev_start: | 832 | error_dev_start: |
784 | unregister_netdev(net_dev); | 833 | unregister_netdev(net_dev); |
785 | error_register_netdev: | 834 | error_register_netdev: |
835 | unregister_pm_notifier(&i2400m->pm_notifier); | ||
786 | error_read_mac_addr: | 836 | error_read_mac_addr: |
787 | error_bootrom_init: | 837 | error_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); |