diff options
author | Eliad Peller <eliad@wizery.com> | 2014-11-20 10:33:43 -0500 |
---|---|---|
committer | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2014-12-28 13:00:18 -0500 |
commit | 7616f334e6d441aa9824221b1352ebec9de57ad7 (patch) | |
tree | fb5806f182791d19c932fe2f790a7c400d57a53d /drivers/net | |
parent | a549b296228497cec90d3a5f5ecaa1934cec4bf1 (diff) |
iwlwifi: pcie: add basic reference accounting
Implement the ref/unref trans ops and track both tx and
host command queues (and hold references while they
are not empty).
Signed-off-by: Eliad Peller <eliadx.peller@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-drv.c | 5 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-modparams.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/internal.h | 8 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/trans.c | 39 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/tx.c | 34 |
5 files changed, 83 insertions, 5 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index afa63f7b2d3e..0381dc495b1c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c | |||
@@ -1362,6 +1362,7 @@ struct iwl_mod_params iwlwifi_mod_params = { | |||
1362 | .bt_coex_active = true, | 1362 | .bt_coex_active = true, |
1363 | .power_level = IWL_POWER_INDEX_1, | 1363 | .power_level = IWL_POWER_INDEX_1, |
1364 | .wd_disable = true, | 1364 | .wd_disable = true, |
1365 | .d0i3_disable = true, | ||
1365 | #ifndef CONFIG_IWLWIFI_UAPSD | 1366 | #ifndef CONFIG_IWLWIFI_UAPSD |
1366 | .uapsd_disable = true, | 1367 | .uapsd_disable = true, |
1367 | #endif /* CONFIG_IWLWIFI_UAPSD */ | 1368 | #endif /* CONFIG_IWLWIFI_UAPSD */ |
@@ -1478,6 +1479,10 @@ MODULE_PARM_DESC(wd_disable, | |||
1478 | module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO); | 1479 | module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO); |
1479 | MODULE_PARM_DESC(nvm_file, "NVM file name"); | 1480 | MODULE_PARM_DESC(nvm_file, "NVM file name"); |
1480 | 1481 | ||
1482 | module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable, | ||
1483 | bool, S_IRUGO); | ||
1484 | MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)"); | ||
1485 | |||
1481 | module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, | 1486 | module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, |
1482 | bool, S_IRUGO); | 1487 | bool, S_IRUGO); |
1483 | #ifdef CONFIG_IWLWIFI_UAPSD | 1488 | #ifdef CONFIG_IWLWIFI_UAPSD |
diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index 71507cf490e6..2a8cf4b2445c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h | |||
@@ -103,6 +103,7 @@ enum iwl_disable_11n { | |||
103 | * @power_level: power level, default = 1 | 103 | * @power_level: power level, default = 1 |
104 | * @debug_level: levels are IWL_DL_* | 104 | * @debug_level: levels are IWL_DL_* |
105 | * @ant_coupling: antenna coupling in dB, default = 0 | 105 | * @ant_coupling: antenna coupling in dB, default = 0 |
106 | * @d0i3_disable: disable d0i3, default = 1, | ||
106 | * @fw_monitor: allow to use firmware monitor | 107 | * @fw_monitor: allow to use firmware monitor |
107 | */ | 108 | */ |
108 | struct iwl_mod_params { | 109 | struct iwl_mod_params { |
@@ -121,6 +122,7 @@ struct iwl_mod_params { | |||
121 | int ant_coupling; | 122 | int ant_coupling; |
122 | char *nvm_file; | 123 | char *nvm_file; |
123 | bool uapsd_disable; | 124 | bool uapsd_disable; |
125 | bool d0i3_disable; | ||
124 | bool fw_monitor; | 126 | bool fw_monitor; |
125 | }; | 127 | }; |
126 | 128 | ||
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 1aea6b66c594..e5652d82d79e 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h | |||
@@ -318,6 +318,11 @@ struct iwl_trans_pcie { | |||
318 | /*protect hw register */ | 318 | /*protect hw register */ |
319 | spinlock_t reg_lock; | 319 | spinlock_t reg_lock; |
320 | bool cmd_in_flight; | 320 | bool cmd_in_flight; |
321 | bool ref_cmd_in_flight; | ||
322 | |||
323 | /* protect ref counter */ | ||
324 | spinlock_t ref_lock; | ||
325 | u32 ref_count; | ||
321 | 326 | ||
322 | dma_addr_t fw_mon_phys; | 327 | dma_addr_t fw_mon_phys; |
323 | struct page *fw_mon_page; | 328 | struct page *fw_mon_page; |
@@ -381,6 +386,9 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, | |||
381 | struct sk_buff_head *skbs); | 386 | struct sk_buff_head *skbs); |
382 | void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); | 387 | void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); |
383 | 388 | ||
389 | void iwl_trans_pcie_ref(struct iwl_trans *trans); | ||
390 | void iwl_trans_pcie_unref(struct iwl_trans *trans); | ||
391 | |||
384 | static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) | 392 | static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) |
385 | { | 393 | { |
386 | struct iwl_tfd_tb *tb = &tfd->tbs[idx]; | 394 | struct iwl_tfd_tb *tb = &tfd->tbs[idx]; |
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 4b42de3b0674..fbdbec89ad70 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c | |||
@@ -931,6 +931,7 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans, | |||
931 | static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, | 931 | static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, |
932 | const struct fw_img *fw, bool run_in_rfkill) | 932 | const struct fw_img *fw, bool run_in_rfkill) |
933 | { | 933 | { |
934 | struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); | ||
934 | int ret; | 935 | int ret; |
935 | bool hw_rfkill; | 936 | bool hw_rfkill; |
936 | 937 | ||
@@ -960,6 +961,9 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, | |||
960 | return ret; | 961 | return ret; |
961 | } | 962 | } |
962 | 963 | ||
964 | /* init ref_count to 1 (should be cleared when ucode is loaded) */ | ||
965 | trans_pcie->ref_count = 1; | ||
966 | |||
963 | /* make sure rfkill handshake bits are cleared */ | 967 | /* make sure rfkill handshake bits are cleared */ |
964 | iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); | 968 | iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); |
965 | iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, | 969 | iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, |
@@ -1550,6 +1554,38 @@ static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg, | |||
1550 | spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); | 1554 | spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); |
1551 | } | 1555 | } |
1552 | 1556 | ||
1557 | void iwl_trans_pcie_ref(struct iwl_trans *trans) | ||
1558 | { | ||
1559 | struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); | ||
1560 | unsigned long flags; | ||
1561 | |||
1562 | if (iwlwifi_mod_params.d0i3_disable) | ||
1563 | return; | ||
1564 | |||
1565 | spin_lock_irqsave(&trans_pcie->ref_lock, flags); | ||
1566 | IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count); | ||
1567 | trans_pcie->ref_count++; | ||
1568 | spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); | ||
1569 | } | ||
1570 | |||
1571 | void iwl_trans_pcie_unref(struct iwl_trans *trans) | ||
1572 | { | ||
1573 | struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); | ||
1574 | unsigned long flags; | ||
1575 | |||
1576 | if (iwlwifi_mod_params.d0i3_disable) | ||
1577 | return; | ||
1578 | |||
1579 | spin_lock_irqsave(&trans_pcie->ref_lock, flags); | ||
1580 | IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count); | ||
1581 | if (WARN_ON_ONCE(trans_pcie->ref_count == 0)) { | ||
1582 | spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); | ||
1583 | return; | ||
1584 | } | ||
1585 | trans_pcie->ref_count--; | ||
1586 | spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); | ||
1587 | } | ||
1588 | |||
1553 | static const char *get_csr_string(int cmd) | 1589 | static const char *get_csr_string(int cmd) |
1554 | { | 1590 | { |
1555 | #define IWL_CMD(x) case x: return #x | 1591 | #define IWL_CMD(x) case x: return #x |
@@ -2274,6 +2310,9 @@ static const struct iwl_trans_ops trans_ops_pcie = { | |||
2274 | .release_nic_access = iwl_trans_pcie_release_nic_access, | 2310 | .release_nic_access = iwl_trans_pcie_release_nic_access, |
2275 | .set_bits_mask = iwl_trans_pcie_set_bits_mask, | 2311 | .set_bits_mask = iwl_trans_pcie_set_bits_mask, |
2276 | 2312 | ||
2313 | .ref = iwl_trans_pcie_ref, | ||
2314 | .unref = iwl_trans_pcie_unref, | ||
2315 | |||
2277 | .dump_data = iwl_trans_pcie_dump_data, | 2316 | .dump_data = iwl_trans_pcie_dump_data, |
2278 | }; | 2317 | }; |
2279 | 2318 | ||
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 8a6c7a084aa1..c1c4c75026b2 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c | |||
@@ -985,17 +985,31 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, | |||
985 | 985 | ||
986 | if (iwl_queue_space(&txq->q) > txq->q.low_mark) | 986 | if (iwl_queue_space(&txq->q) > txq->q.low_mark) |
987 | iwl_wake_queue(trans, txq); | 987 | iwl_wake_queue(trans, txq); |
988 | |||
989 | if (q->read_ptr == q->write_ptr) { | ||
990 | IWL_DEBUG_RPM(trans, "Q %d - last tx reclaimed\n", q->id); | ||
991 | iwl_trans_pcie_unref(trans); | ||
992 | } | ||
993 | |||
988 | out: | 994 | out: |
989 | spin_unlock_bh(&txq->lock); | 995 | spin_unlock_bh(&txq->lock); |
990 | } | 996 | } |
991 | 997 | ||
992 | static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans) | 998 | static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans, |
999 | const struct iwl_host_cmd *cmd) | ||
993 | { | 1000 | { |
994 | struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); | 1001 | struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); |
995 | int ret; | 1002 | int ret; |
996 | 1003 | ||
997 | lockdep_assert_held(&trans_pcie->reg_lock); | 1004 | lockdep_assert_held(&trans_pcie->reg_lock); |
998 | 1005 | ||
1006 | if (!(cmd->flags & CMD_SEND_IN_IDLE) && | ||
1007 | !trans_pcie->ref_cmd_in_flight) { | ||
1008 | trans_pcie->ref_cmd_in_flight = true; | ||
1009 | IWL_DEBUG_RPM(trans, "set ref_cmd_in_flight - ref\n"); | ||
1010 | iwl_trans_pcie_ref(trans); | ||
1011 | } | ||
1012 | |||
999 | if (trans_pcie->cmd_in_flight) | 1013 | if (trans_pcie->cmd_in_flight) |
1000 | return 0; | 1014 | return 0; |
1001 | 1015 | ||
@@ -1036,6 +1050,12 @@ static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans) | |||
1036 | 1050 | ||
1037 | lockdep_assert_held(&trans_pcie->reg_lock); | 1051 | lockdep_assert_held(&trans_pcie->reg_lock); |
1038 | 1052 | ||
1053 | if (trans_pcie->ref_cmd_in_flight) { | ||
1054 | trans_pcie->ref_cmd_in_flight = false; | ||
1055 | IWL_DEBUG_RPM(trans, "clear ref_cmd_in_flight - unref\n"); | ||
1056 | iwl_trans_pcie_unref(trans); | ||
1057 | } | ||
1058 | |||
1039 | if (WARN_ON(!trans_pcie->cmd_in_flight)) | 1059 | if (WARN_ON(!trans_pcie->cmd_in_flight)) |
1040 | return 0; | 1060 | return 0; |
1041 | 1061 | ||
@@ -1473,7 +1493,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, | |||
1473 | mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); | 1493 | mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); |
1474 | 1494 | ||
1475 | spin_lock_irqsave(&trans_pcie->reg_lock, flags); | 1495 | spin_lock_irqsave(&trans_pcie->reg_lock, flags); |
1476 | ret = iwl_pcie_set_cmd_in_flight(trans); | 1496 | ret = iwl_pcie_set_cmd_in_flight(trans, cmd); |
1477 | if (ret < 0) { | 1497 | if (ret < 0) { |
1478 | idx = ret; | 1498 | idx = ret; |
1479 | spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); | 1499 | spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); |
@@ -1819,9 +1839,13 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, | |||
1819 | wait_write_ptr = ieee80211_has_morefrags(fc); | 1839 | wait_write_ptr = ieee80211_has_morefrags(fc); |
1820 | 1840 | ||
1821 | /* start timer if queue currently empty */ | 1841 | /* start timer if queue currently empty */ |
1822 | if (txq->need_update && q->read_ptr == q->write_ptr && | 1842 | if (q->read_ptr == q->write_ptr) { |
1823 | trans_pcie->wd_timeout) | 1843 | if (txq->need_update && trans_pcie->wd_timeout) |
1824 | mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); | 1844 | mod_timer(&txq->stuck_timer, |
1845 | jiffies + trans_pcie->wd_timeout); | ||
1846 | IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", q->id); | ||
1847 | iwl_trans_pcie_ref(trans); | ||
1848 | } | ||
1825 | 1849 | ||
1826 | /* Tell device the write index *just past* this latest filled TFD */ | 1850 | /* Tell device the write index *just past* this latest filled TFD */ |
1827 | q->write_ptr = iwl_queue_inc_wrap(q->write_ptr); | 1851 | q->write_ptr = iwl_queue_inc_wrap(q->write_ptr); |