Commit 7812b167 authored by Wey-Yi Guy's avatar Wey-Yi Guy Committed by John W. Linville

iwlwifi: reliable entering of critical temperature state

When uCode detects critical temperature it should send "card state
notification" interrupt to driver and then shut itself down to prevent
overheating. There is a race condition where uCode shuts down before it
can deliver the interrupt to driver.
Additional method provided here for driver to enter CT_KILL state based
on temperature reading.

How it works:
Method 1:
If driver receive "card state notification" interrupt from uCode; it
enters "CT_KILL" state immediately

Method 2:
If the last temperature report by Card reach Critical temperature,
driver will send "statistic notification" request to uCode to verify the
temperature reading, if driver can not get reply from uCode within
300ms, driver will enter CT_KILL state automatically.

Method 3:
If the last temperature report by Card did not reach Critical
temperature, but uCode already shut down due to critical temperature.
All the host commands send to uCode will not get process by uCode;
when command queue reach the limit, driver will check the last reported
temperature reading, if it is within pre-defined margin, enter "CT_KILL"
state immediately. In this case, when uCode ready to exit from "CT_KILL" state,
driver need to restart the adapter in order to reset all the queues and
resume normal operation.

One additional issue being address here, when system is in CT_KILL
state, both tx and rx already stopped, but driver still can send host
command to uCode, it will flood the command queue since card was not
responding; adding STATUS_CT_KILL flag to reject enqueue host commands
to uCode if it is in CT_KILL state, when uCode is ready to come out of
CT_KILL, driver will clear  the STATUS_CT_KILL bit and allow enqueue the host
commands to uCode to recover from CT_KILL state.
Signed-off-by: default avatarWey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: default avatarReinette Chatre <reinette.chatre@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent ffe97932
...@@ -580,6 +580,7 @@ void iwlcore_free_geos(struct iwl_priv *priv); ...@@ -580,6 +580,7 @@ void iwlcore_free_geos(struct iwl_priv *priv);
#define STATUS_HCMD_SYNC_ACTIVE 1 /* sync host command in progress */ #define STATUS_HCMD_SYNC_ACTIVE 1 /* sync host command in progress */
#define STATUS_INT_ENABLED 2 #define STATUS_INT_ENABLED 2
#define STATUS_RF_KILL_HW 3 #define STATUS_RF_KILL_HW 3
#define STATUS_CT_KILL 4
#define STATUS_INIT 5 #define STATUS_INIT 5
#define STATUS_ALIVE 6 #define STATUS_ALIVE 6
#define STATUS_READY 7 #define STATUS_READY 7
...@@ -624,6 +625,11 @@ static inline int iwl_is_rfkill(struct iwl_priv *priv) ...@@ -624,6 +625,11 @@ static inline int iwl_is_rfkill(struct iwl_priv *priv)
return iwl_is_rfkill_hw(priv); return iwl_is_rfkill_hw(priv);
} }
static inline int iwl_is_ctkill(struct iwl_priv *priv)
{
return test_bit(STATUS_CT_KILL, &priv->status);
}
static inline int iwl_is_ready_rf(struct iwl_priv *priv) static inline int iwl_is_ready_rf(struct iwl_priv *priv)
{ {
......
...@@ -535,6 +535,8 @@ static ssize_t iwl_dbgfs_status_read(struct file *file, ...@@ -535,6 +535,8 @@ static ssize_t iwl_dbgfs_status_read(struct file *file,
test_bit(STATUS_INT_ENABLED, &priv->status)); test_bit(STATUS_INT_ENABLED, &priv->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n", pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n",
test_bit(STATUS_RF_KILL_HW, &priv->status)); test_bit(STATUS_RF_KILL_HW, &priv->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_CT_KILL:\t\t %d\n",
test_bit(STATUS_CT_KILL, &priv->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INIT:\t\t %d\n", pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INIT:\t\t %d\n",
test_bit(STATUS_INIT, &priv->status)); test_bit(STATUS_INIT, &priv->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n", pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n",
......
This diff is collapsed.
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#define IWL_ABSOLUTE_ZERO 0 #define IWL_ABSOLUTE_ZERO 0
#define IWL_ABSOLUTE_MAX 0xFFFFFFFF #define IWL_ABSOLUTE_MAX 0xFFFFFFFF
#define IWL_TT_INCREASE_MARGIN 5 #define IWL_TT_INCREASE_MARGIN 5
#define IWL_TT_CT_KILL_MARGIN 3
enum iwl_antenna_ok { enum iwl_antenna_ok {
IWL_ANT_OK_NONE, IWL_ANT_OK_NONE,
...@@ -110,6 +111,7 @@ struct iwl_tt_mgmt { ...@@ -110,6 +111,7 @@ struct iwl_tt_mgmt {
struct iwl_tt_restriction *restriction; struct iwl_tt_restriction *restriction;
struct iwl_tt_trans *transaction; struct iwl_tt_trans *transaction;
struct timer_list ct_kill_exit_tm; struct timer_list ct_kill_exit_tm;
struct timer_list ct_kill_waiting_tm;
}; };
enum iwl_power_level { enum iwl_power_level {
...@@ -129,6 +131,7 @@ struct iwl_power_mgr { ...@@ -129,6 +131,7 @@ struct iwl_power_mgr {
int iwl_power_update_mode(struct iwl_priv *priv, bool force); int iwl_power_update_mode(struct iwl_priv *priv, bool force);
bool iwl_ht_enabled(struct iwl_priv *priv); bool iwl_ht_enabled(struct iwl_priv *priv);
bool iwl_within_ct_kill_margin(struct iwl_priv *priv);
enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv); enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv);
enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv); enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv);
void iwl_tt_enter_ct_kill(struct iwl_priv *priv); void iwl_tt_enter_ct_kill(struct iwl_priv *priv);
......
...@@ -969,13 +969,19 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) ...@@ -969,13 +969,19 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) && BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) &&
!(cmd->flags & CMD_SIZE_HUGE)); !(cmd->flags & CMD_SIZE_HUGE));
if (iwl_is_rfkill(priv)) { if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) {
IWL_DEBUG_INFO(priv, "Not sending command - RF KILL\n"); IWL_DEBUG_INFO(priv, "Not sending command - RF/CT KILL\n");
return -EIO; return -EIO;
} }
if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
IWL_ERR(priv, "No space for Tx\n"); IWL_ERR(priv, "No space for Tx\n");
if (iwl_within_ct_kill_margin(priv))
iwl_tt_enter_ct_kill(priv);
else {
IWL_ERR(priv, "Restarting adapter due to queue full\n");
queue_work(priv->workqueue, &priv->restart);
}
return -ENOSPC; return -ENOSPC;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment