diff options
author | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2013-12-22 08:09:40 -0500 |
---|---|---|
committer | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2013-12-31 12:03:43 -0500 |
commit | b9439491055a18ee075614139abadfd74c1b887f (patch) | |
tree | 41845bfb442438e01acb78c3181adbd72a1ce761 /drivers/net/wireless/iwlwifi/pcie/tx.c | |
parent | a4a1247847ca9ae2fd96e0684a74acd551791000 (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>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/pcie/tx.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/tx.c | 71 |
1 files changed, 45 insertions, 26 deletions
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 | ||
1467 | static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans, | 1506 | static 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; |