aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
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
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')
-rw-r--r--drivers/net/wimax/i2400m/driver.c51
-rw-r--r--drivers/net/wimax/i2400m/fw.c159
-rw-r--r--drivers/net/wimax/i2400m/i2400m.h16
3 files changed, 213 insertions, 13 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);
diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 5719f4a4080f..69f9e45eafbf 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -105,6 +105,13 @@
105 * read an acknolwedgement from it (or an asynchronous notification) 105 * read an acknolwedgement from it (or an asynchronous notification)
106 * from it. 106 * from it.
107 * 107 *
108 * FIRMWARE LOADING
109 *
110 * Note that in some cases, we can't just load a firmware file (for
111 * example, when resuming). For that, we might cache the firmware
112 * file. Thus, when doing the bootstrap, if there is a cache firmware
113 * file, it is used; if not, loading from disk is attempted.
114 *
108 * ROADMAP 115 * ROADMAP
109 * 116 *
110 * i2400m_barker_db_init Called by i2400m_driver_init() 117 * i2400m_barker_db_init Called by i2400m_driver_init()
@@ -114,9 +121,10 @@
114 * 121 *
115 * i2400m_dev_bootstrap Called by __i2400m_dev_start() 122 * i2400m_dev_bootstrap Called by __i2400m_dev_start()
116 * request_firmware 123 * request_firmware
117 * i2400m_fw_check 124 * i2400m_fw_bootstrap
118 * i2400m_fw_hdr_check 125 * i2400m_fw_check
119 * i2400m_fw_dnload 126 * i2400m_fw_hdr_check
127 * i2400m_fw_dnload
120 * release_firmware 128 * release_firmware
121 * 129 *
122 * i2400m_fw_dnload 130 * i2400m_fw_dnload
@@ -141,6 +149,10 @@
141 * 149 *
142 * i2400m_bm_cmd_prepare Used by bus-drivers to prep 150 * i2400m_bm_cmd_prepare Used by bus-drivers to prep
143 * commands before sending 151 * commands before sending
152 *
153 * i2400m_pm_notifier Called on Power Management events
154 * i2400m_fw_cache
155 * i2400m_fw_uncache
144 */ 156 */
145#include <linux/firmware.h> 157#include <linux/firmware.h>
146#include <linux/sched.h> 158#include <linux/sched.h>
@@ -1459,6 +1471,61 @@ error_dev_rebooted:
1459 goto hw_reboot; 1471 goto hw_reboot;
1460} 1472}
1461 1473
1474static
1475int i2400m_fw_bootstrap(struct i2400m *i2400m, const struct firmware *fw,
1476 enum i2400m_bri flags)
1477{
1478 int ret;
1479 struct device *dev = i2400m_dev(i2400m);
1480 const struct i2400m_bcf_hdr *bcf; /* Firmware data */
1481
1482 d_fnstart(5, dev, "(i2400m %p)\n", i2400m);
1483 bcf = (void *) fw->data;
1484 ret = i2400m_fw_check(i2400m, bcf, fw->size);
1485 if (ret >= 0)
1486 ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags);
1487 if (ret < 0)
1488 dev_err(dev, "%s: cannot use: %d, skipping\n",
1489 i2400m->fw_name, ret);
1490 kfree(i2400m->fw_hdrs);
1491 i2400m->fw_hdrs = NULL;
1492 d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret);
1493 return ret;
1494}
1495
1496
1497/* Refcounted container for firmware data */
1498struct i2400m_fw {
1499 struct kref kref;
1500 const struct firmware *fw;
1501};
1502
1503
1504static
1505void i2400m_fw_destroy(struct kref *kref)
1506{
1507 struct i2400m_fw *i2400m_fw =
1508 container_of(kref, struct i2400m_fw, kref);
1509 release_firmware(i2400m_fw->fw);
1510 kfree(i2400m_fw);
1511}
1512
1513
1514static
1515struct i2400m_fw *i2400m_fw_get(struct i2400m_fw *i2400m_fw)
1516{
1517 if (i2400m_fw != NULL && i2400m_fw != (void *) ~0)
1518 kref_get(&i2400m_fw->kref);
1519 return i2400m_fw;
1520}
1521
1522
1523static
1524void i2400m_fw_put(struct i2400m_fw *i2400m_fw)
1525{
1526 kref_put(&i2400m_fw->kref, i2400m_fw_destroy);
1527}
1528
1462 1529
1463/** 1530/**
1464 * i2400m_dev_bootstrap - Bring the device to a known state and upload firmware 1531 * i2400m_dev_bootstrap - Bring the device to a known state and upload firmware
@@ -1479,12 +1546,28 @@ int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags)
1479{ 1546{
1480 int ret, itr; 1547 int ret, itr;
1481 struct device *dev = i2400m_dev(i2400m); 1548 struct device *dev = i2400m_dev(i2400m);
1482 const struct firmware *fw; 1549 struct i2400m_fw *i2400m_fw;
1483 const struct i2400m_bcf_hdr *bcf; /* Firmware data */ 1550 const struct i2400m_bcf_hdr *bcf; /* Firmware data */
1551 const struct firmware *fw;
1484 const char *fw_name; 1552 const char *fw_name;
1485 1553
1486 d_fnstart(5, dev, "(i2400m %p)\n", i2400m); 1554 d_fnstart(5, dev, "(i2400m %p)\n", i2400m);
1487 1555
1556 ret = -ENODEV;
1557 spin_lock(&i2400m->rx_lock);
1558 i2400m_fw = i2400m_fw_get(i2400m->fw_cached);
1559 spin_unlock(&i2400m->rx_lock);
1560 if (i2400m_fw == (void *) ~0) {
1561 dev_err(dev, "can't load firmware now!");
1562 goto out;
1563 } else if (i2400m_fw != NULL) {
1564 dev_info(dev, "firmware %s: loading from cache\n",
1565 i2400m->fw_name);
1566 ret = i2400m_fw_bootstrap(i2400m, i2400m_fw->fw, flags);
1567 i2400m_fw_put(i2400m_fw);
1568 goto out;
1569 }
1570
1488 /* Load firmware files to memory. */ 1571 /* Load firmware files to memory. */
1489 for (itr = 0, bcf = NULL, ret = -ENOENT; ; itr++) { 1572 for (itr = 0, bcf = NULL, ret = -ENOENT; ; itr++) {
1490 fw_name = i2400m->bus_fw_names[itr]; 1573 fw_name = i2400m->bus_fw_names[itr];
@@ -1500,21 +1583,71 @@ int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags)
1500 fw_name, ret); 1583 fw_name, ret);
1501 continue; 1584 continue;
1502 } 1585 }
1503 bcf = (void *) fw->data;
1504 i2400m->fw_name = fw_name; 1586 i2400m->fw_name = fw_name;
1505 ret = i2400m_fw_check(i2400m, bcf, fw->size); 1587 ret = i2400m_fw_bootstrap(i2400m, fw, flags);
1506 if (ret >= 0)
1507 ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags);
1508 if (ret < 0)
1509 dev_err(dev, "%s: cannot use: %d, skipping\n",
1510 fw_name, ret);
1511 kfree(i2400m->fw_hdrs);
1512 i2400m->fw_hdrs = NULL;
1513 release_firmware(fw); 1588 release_firmware(fw);
1514 if (ret >= 0) /* firmware loaded succesfully */ 1589 if (ret >= 0) /* firmware loaded succesfully */
1515 break; 1590 break;
1591 i2400m->fw_name = NULL;
1516 } 1592 }
1593out:
1517 d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); 1594 d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret);
1518 return ret; 1595 return ret;
1519} 1596}
1520EXPORT_SYMBOL_GPL(i2400m_dev_bootstrap); 1597EXPORT_SYMBOL_GPL(i2400m_dev_bootstrap);
1598
1599
1600void i2400m_fw_cache(struct i2400m *i2400m)
1601{
1602 int result;
1603 struct i2400m_fw *i2400m_fw;
1604 struct device *dev = i2400m_dev(i2400m);
1605
1606 /* if there is anything there, free it -- now, this'd be weird */
1607 spin_lock(&i2400m->rx_lock);
1608 i2400m_fw = i2400m->fw_cached;
1609 spin_unlock(&i2400m->rx_lock);
1610 if (i2400m_fw != NULL && i2400m_fw != (void *) ~0) {
1611 i2400m_fw_put(i2400m_fw);
1612 WARN(1, "%s:%u: still cached fw still present?\n",
1613 __func__, __LINE__);
1614 }
1615
1616 if (i2400m->fw_name == NULL) {
1617 dev_err(dev, "firmware n/a: can't cache\n");
1618 i2400m_fw = (void *) ~0;
1619 goto out;
1620 }
1621
1622 i2400m_fw = kzalloc(sizeof(*i2400m_fw), GFP_ATOMIC);
1623 if (i2400m_fw == NULL)
1624 goto out;
1625 kref_init(&i2400m_fw->kref);
1626 result = request_firmware(&i2400m_fw->fw, i2400m->fw_name, dev);
1627 if (result < 0) {
1628 dev_err(dev, "firmware %s: failed to cache: %d\n",
1629 i2400m->fw_name, result);
1630 kfree(i2400m_fw);
1631 i2400m_fw = (void *) ~0;
1632 } else
1633 dev_info(dev, "firmware %s: cached\n", i2400m->fw_name);
1634out:
1635 spin_lock(&i2400m->rx_lock);
1636 i2400m->fw_cached = i2400m_fw;
1637 spin_unlock(&i2400m->rx_lock);
1638}
1639
1640
1641void i2400m_fw_uncache(struct i2400m *i2400m)
1642{
1643 struct i2400m_fw *i2400m_fw;
1644
1645 spin_lock(&i2400m->rx_lock);
1646 i2400m_fw = i2400m->fw_cached;
1647 i2400m->fw_cached = NULL;
1648 spin_unlock(&i2400m->rx_lock);
1649
1650 if (i2400m_fw != NULL && i2400m_fw != (void *) ~0)
1651 i2400m_fw_put(i2400m_fw);
1652}
1653
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 0c165de89b2d..916b1d319299 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -424,11 +424,21 @@ struct i2400m_barker_db;
424 * @fw_hdrs: NULL terminated array of pointers to the firmware 424 * @fw_hdrs: NULL terminated array of pointers to the firmware
425 * headers. This is only available during firmware load time. 425 * headers. This is only available during firmware load time.
426 * 426 *
427 * @fw_cached: Used to cache firmware when the system goes to
428 * suspend/standby/hibernation (as on resume we can't read it). If
429 * NULL, no firmware was cached, read it. If ~0, you can't read
430 * any firmware files (the system still didn't come out of suspend
431 * and failed to cache one), so abort; otherwise, a valid cached
432 * firmware to be used. Access to this variable is protected by
433 * the spinlock i2400m->rx_lock.
434 *
427 * @barker: barker type that the device uses; this is initialized by 435 * @barker: barker type that the device uses; this is initialized by
428 * i2400m_is_boot_barker() the first time it is called. Then it 436 * i2400m_is_boot_barker() the first time it is called. Then it
429 * won't change during the life cycle of the device and everytime 437 * won't change during the life cycle of the device and everytime
430 * a boot barker is received, it is just verified for it being the 438 * a boot barker is received, it is just verified for it being the
431 * same. 439 * same.
440 *
441 * @pm_notifier: used to register for PM events
432 */ 442 */
433struct i2400m { 443struct i2400m {
434 struct wimax_dev wimax_dev; /* FIRST! See doc */ 444 struct wimax_dev wimax_dev; /* FIRST! See doc */
@@ -495,7 +505,10 @@ struct i2400m {
495 const char *fw_name; /* name of the current firmware image */ 505 const char *fw_name; /* name of the current firmware image */
496 unsigned long fw_version; /* version of the firmware interface */ 506 unsigned long fw_version; /* version of the firmware interface */
497 const struct i2400m_bcf_hdr **fw_hdrs; 507 const struct i2400m_bcf_hdr **fw_hdrs;
508 struct i2400m_fw *fw_cached; /* protected by rx_lock */
498 struct i2400m_barker_db *barker; 509 struct i2400m_barker_db *barker;
510
511 struct notifier_block pm_notifier;
499}; 512};
500 513
501 514
@@ -671,6 +684,9 @@ extern void i2400m_tx_release(struct i2400m *);
671extern int i2400m_rx_setup(struct i2400m *); 684extern int i2400m_rx_setup(struct i2400m *);
672extern void i2400m_rx_release(struct i2400m *); 685extern void i2400m_rx_release(struct i2400m *);
673 686
687extern void i2400m_fw_cache(struct i2400m *);
688extern void i2400m_fw_uncache(struct i2400m *);
689
674extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, 690extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned,
675 const void *, int); 691 const void *, int);
676extern void i2400m_net_erx(struct i2400m *, struct sk_buff *, 692extern void i2400m_net_erx(struct i2400m *, struct sk_buff *,