aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2013-12-22 08:09:40 -0500
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2013-12-31 12:03:43 -0500
commitb9439491055a18ee075614139abadfd74c1b887f (patch)
tree41845bfb442438e01acb78c3181adbd72a1ce761
parenta4a1247847ca9ae2fd96e0684a74acd551791000 (diff)
iwlwifi: pcie: keep the NIC awake when commands are in flight
Under very specific circumstances, the firmware might ignore a host command. This was debugged and we ended up seeing that the power management hardware was faulty. In order to workaround this issue, we keep the NIC awake as long as we have host commands in flight. This will avoid to put the hardware into buggy condition. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/internal.h29
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c35
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c71
3 files changed, 82 insertions, 53 deletions
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index 9dfee7ed2e3e..809052442e91 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -262,6 +262,7 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx)
262 * @rx_page_order: page order for receive buffer size 262 * @rx_page_order: page order for receive buffer size
263 * @wd_timeout: queue watchdog timeout (jiffies) 263 * @wd_timeout: queue watchdog timeout (jiffies)
264 * @reg_lock: protect hw register access 264 * @reg_lock: protect hw register access
265 * @cmd_in_flight: true when we have a host command in flight
265 */ 266 */
266struct iwl_trans_pcie { 267struct iwl_trans_pcie {
267 struct iwl_rxq rxq; 268 struct iwl_rxq rxq;
@@ -310,6 +311,7 @@ struct iwl_trans_pcie {
310 311
311 /*protect hw register */ 312 /*protect hw register */
312 spinlock_t reg_lock; 313 spinlock_t reg_lock;
314 bool cmd_in_flight;
313}; 315};
314 316
315#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ 317#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
@@ -459,4 +461,31 @@ static inline bool iwl_is_rfkill_set(struct iwl_trans *trans)
459 CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); 461 CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW);
460} 462}
461 463
464static inline void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans,
465 u32 reg, u32 mask, u32 value)
466{
467 u32 v;
468
469#ifdef CONFIG_IWLWIFI_DEBUG
470 WARN_ON_ONCE(value & ~mask);
471#endif
472
473 v = iwl_read32(trans, reg);
474 v &= ~mask;
475 v |= value;
476 iwl_write32(trans, reg, v);
477}
478
479static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans,
480 u32 reg, u32 mask)
481{
482 __iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0);
483}
484
485static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans,
486 u32 reg, u32 mask)
487{
488 __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask);
489}
490
462#endif /* __iwl_trans_int_pcie_h__ */ 491#endif /* __iwl_trans_int_pcie_h__ */
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 9a07cf3062ea..d9ccb4edc602 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -75,33 +75,6 @@
75#include "iwl-agn-hw.h" 75#include "iwl-agn-hw.h"
76#include "internal.h" 76#include "internal.h"
77 77
78static void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans,
79 u32 reg, u32 mask, u32 value)
80{
81 u32 v;
82
83#ifdef CONFIG_IWLWIFI_DEBUG
84 WARN_ON_ONCE(value & ~mask);
85#endif
86
87 v = iwl_read32(trans, reg);
88 v &= ~mask;
89 v |= value;
90 iwl_write32(trans, reg, v);
91}
92
93static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans,
94 u32 reg, u32 mask)
95{
96 __iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0);
97}
98
99static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans,
100 u32 reg, u32 mask)
101{
102 __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask);
103}
104
105static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux) 78static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
106{ 79{
107 if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold)) 80 if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold))
@@ -929,6 +902,9 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
929 902
930 spin_lock_irqsave(&trans_pcie->reg_lock, *flags); 903 spin_lock_irqsave(&trans_pcie->reg_lock, *flags);
931 904
905 if (trans_pcie->cmd_in_flight)
906 goto out;
907
932 /* this bit wakes up the NIC */ 908 /* this bit wakes up the NIC */
933 __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, 909 __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
934 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 910 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
@@ -968,6 +944,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
968 } 944 }
969 } 945 }
970 946
947out:
971 /* 948 /*
972 * Fool sparse by faking we release the lock - sparse will 949 * Fool sparse by faking we release the lock - sparse will
973 * track nic_access anyway. 950 * track nic_access anyway.
@@ -989,6 +966,9 @@ static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans,
989 */ 966 */
990 __acquire(&trans_pcie->reg_lock); 967 __acquire(&trans_pcie->reg_lock);
991 968
969 if (trans_pcie->cmd_in_flight)
970 goto out;
971
992 __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, 972 __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
993 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 973 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
994 /* 974 /*
@@ -998,6 +978,7 @@ static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans,
998 * scheduled on different CPUs (after we drop reg_lock). 978 * scheduled on different CPUs (after we drop reg_lock).
999 */ 979 */
1000 mmiowb(); 980 mmiowb();
981out:
1001 spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags); 982 spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags);
1002} 983}
1003 984
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index b49a185355e4..2417af9ad2c6 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -1001,6 +1001,7 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
1001 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);
1002 struct iwl_txq *txq = &trans_pcie->txq[txq_id]; 1002 struct iwl_txq *txq = &trans_pcie->txq[txq_id];
1003 struct iwl_queue *q = &txq->q; 1003 struct iwl_queue *q = &txq->q;
1004 unsigned long flags;
1004 int nfreed = 0; 1005 int nfreed = 0;
1005 1006
1006 lockdep_assert_held(&txq->lock); 1007 lockdep_assert_held(&txq->lock);
@@ -1023,6 +1024,16 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
1023 } 1024 }
1024 } 1025 }
1025 1026
1027 if (q->read_ptr == q->write_ptr) {
1028 spin_lock_irqsave(&trans_pcie->reg_lock, flags);
1029 WARN_ON(!trans_pcie->cmd_in_flight);
1030 trans_pcie->cmd_in_flight = false;
1031 __iwl_trans_pcie_clear_bit(trans,
1032 CSR_GP_CNTRL,
1033 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
1034 spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
1035 }
1036
1026 iwl_pcie_txq_progress(trans_pcie, txq); 1037 iwl_pcie_txq_progress(trans_pcie, txq);
1027} 1038}
1028 1039
@@ -1174,12 +1185,13 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
1174 struct iwl_queue *q = &txq->q; 1185 struct iwl_queue *q = &txq->q;
1175 struct iwl_device_cmd *out_cmd; 1186 struct iwl_device_cmd *out_cmd;
1176 struct iwl_cmd_meta *out_meta; 1187 struct iwl_cmd_meta *out_meta;
1188 unsigned long flags;
1177 void *dup_buf = NULL; 1189 void *dup_buf = NULL;
1178 dma_addr_t phys_addr; 1190 dma_addr_t phys_addr;
1179 int idx; 1191 int idx;
1180 u16 copy_size, cmd_size, scratch_size; 1192 u16 copy_size, cmd_size, scratch_size;
1181 bool had_nocopy = false; 1193 bool had_nocopy = false;
1182 int i; 1194 int i, ret;
1183 u32 cmd_pos; 1195 u32 cmd_pos;
1184 const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD]; 1196 const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD];
1185 u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD]; 1197 u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD];
@@ -1377,10 +1389,38 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
1377 if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) 1389 if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout)
1378 mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); 1390 mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
1379 1391
1392 spin_lock_irqsave(&trans_pcie->reg_lock, flags);
1393
1394 /*
1395 * wake up the NIC to make sure that the firmware will see the host
1396 * command - we will let the NIC sleep once all the host commands
1397 * returned.
1398 */
1399 if (!trans_pcie->cmd_in_flight) {
1400 trans_pcie->cmd_in_flight = true;
1401 __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
1402 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
1403 ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
1404 CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
1405 (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
1406 CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP),
1407 15000);
1408 if (ret < 0) {
1409 __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
1410 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
1411 spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
1412 trans_pcie->cmd_in_flight = false;
1413 idx = -EIO;
1414 goto out;
1415 }
1416 }
1417
1380 /* Increment and update queue's write index */ 1418 /* Increment and update queue's write index */
1381 q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); 1419 q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
1382 iwl_pcie_txq_inc_wr_ptr(trans, txq); 1420 iwl_pcie_txq_inc_wr_ptr(trans, txq);
1383 1421
1422 spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
1423
1384 out: 1424 out:
1385 spin_unlock_bh(&txq->lock); 1425 spin_unlock_bh(&txq->lock);
1386 free_dup_buf: 1426 free_dup_buf:
@@ -1462,7 +1502,6 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
1462} 1502}
1463 1503
1464#define HOST_COMPLETE_TIMEOUT (2 * HZ) 1504#define HOST_COMPLETE_TIMEOUT (2 * HZ)
1465#define COMMAND_POKE_TIMEOUT (HZ / 10)
1466 1505
1467static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans, 1506static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans,
1468 struct iwl_host_cmd *cmd) 1507 struct iwl_host_cmd *cmd)
@@ -1490,7 +1529,6 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
1490 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 1529 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
1491 int cmd_idx; 1530 int cmd_idx;
1492 int ret; 1531 int ret;
1493 int timeout = HOST_COMPLETE_TIMEOUT;
1494 1532
1495 IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", 1533 IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n",
1496 get_cmd_string(trans_pcie, cmd->id)); 1534 get_cmd_string(trans_pcie, cmd->id));
@@ -1514,29 +1552,10 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
1514 return ret; 1552 return ret;
1515 } 1553 }
1516 1554
1517 while (timeout > 0) { 1555 ret = wait_event_timeout(trans_pcie->wait_command_queue,
1518 unsigned long flags; 1556 !test_bit(STATUS_SYNC_HCMD_ACTIVE,
1519 1557 &trans->status),
1520 timeout -= COMMAND_POKE_TIMEOUT; 1558 HOST_COMPLETE_TIMEOUT);
1521 ret = wait_event_timeout(trans_pcie->wait_command_queue,
1522 !test_bit(STATUS_SYNC_HCMD_ACTIVE,
1523 &trans->status),
1524 COMMAND_POKE_TIMEOUT);
1525 if (ret)
1526 break;
1527 /* poke the device - it may have lost the command */
1528 if (iwl_trans_grab_nic_access(trans, true, &flags)) {
1529 iwl_trans_release_nic_access(trans, &flags);
1530 IWL_DEBUG_INFO(trans,
1531 "Tried to wake NIC for command %s\n",
1532 get_cmd_string(trans_pcie, cmd->id));
1533 } else {
1534 IWL_ERR(trans, "Failed to poke NIC for command %s\n",
1535 get_cmd_string(trans_pcie, cmd->id));
1536 break;
1537 }
1538 }
1539
1540 if (!ret) { 1559 if (!ret) {
1541 struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; 1560 struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue];
1542 struct iwl_queue *q = &txq->q; 1561 struct iwl_queue *q = &txq->q;