Blob Blame History Raw
Backport of the following upstream commit...

commit a93e7973d0983d22fcbe5f691244736211639fe7
Author: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Date:   Wed Feb 3 11:47:19 2010 -0800

    iwlwifi: multiple force reset mode
    
    Provide the function to perform different type of uCode reset/reload operation.
    When uCode detect error and can not fix itself, this iwl_force_reset()
    function allow driver to perform the necessary reset/reload functions and help
    to bring uCode back to normal operation state.
    
    Currently only 2 type of force reset are available:
     - reset radio
     - reload firmware
    
    Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
    Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>

diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.c
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig	2010-04-13 13:36:35.000000000 -0400
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.c	2010-04-13 13:38:40.000000000 -0400
@@ -3197,7 +3197,7 @@ void iwl_update_stats(struct iwl_priv *p
 EXPORT_SYMBOL(iwl_update_stats);
 #endif
 
-void iwl_force_rf_reset(struct iwl_priv *priv)
+static void iwl_force_rf_reset(struct iwl_priv *priv)
 {
 	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
 		return;
@@ -3219,7 +3219,47 @@ void iwl_force_rf_reset(struct iwl_priv 
 	iwl_internal_short_hw_scan(priv);
 	return;
 }
-EXPORT_SYMBOL(iwl_force_rf_reset);
+
+#define IWL_DELAY_NEXT_FORCE_RESET (HZ*3)
+
+int iwl_force_reset(struct iwl_priv *priv, int mode)
+{
+	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+		return -EINVAL;
+
+	if (priv->last_force_reset_jiffies &&
+	    time_after(priv->last_force_reset_jiffies +
+		       IWL_DELAY_NEXT_FORCE_RESET, jiffies)) {
+		IWL_DEBUG_INFO(priv, "force reset rejected\n");
+		return -EAGAIN;
+	}
+
+	IWL_DEBUG_INFO(priv, "perform force reset (%d)\n", mode);
+
+	switch (mode) {
+	case IWL_RF_RESET:
+		iwl_force_rf_reset(priv);
+		break;
+	case IWL_FW_RESET:
+		IWL_ERR(priv, "On demand firmware reload\n");
+		/* Set the FW error flag -- cleared on iwl_down */
+		set_bit(STATUS_FW_ERROR, &priv->status);
+		wake_up_interruptible(&priv->wait_command_queue);
+		/*
+		 * Keep the restart process from trying to send host
+		 * commands by clearing the INIT status bit
+		 */
+		clear_bit(STATUS_READY, &priv->status);
+		queue_work(priv->workqueue, &priv->restart);
+		break;
+	default:
+		IWL_DEBUG_INFO(priv, "invalid reset request.\n");
+		return -EINVAL;
+	}
+	priv->last_force_reset_jiffies = jiffies;
+
+	return 0;
+}
 
 #ifdef CONFIG_PM
 
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.h
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig	2010-04-13 13:36:50.000000000 -0400
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.h	2010-04-13 13:38:40.000000000 -0400
@@ -501,7 +501,7 @@ int iwl_scan_cancel(struct iwl_priv *pri
 int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
 int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req);
 int iwl_internal_short_hw_scan(struct iwl_priv *priv);
-void iwl_force_rf_reset(struct iwl_priv *priv);
+int iwl_force_reset(struct iwl_priv *priv, int mode);
 u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
 		       const u8 *ie, int ie_len, int left);
 void iwl_setup_rx_scan_handlers(struct iwl_priv *priv);
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig	2010-04-13 13:36:50.000000000 -0400
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h	2010-04-13 13:38:40.000000000 -0400
@@ -993,6 +993,11 @@ struct iwl_switch_rxon {
 #define IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF	(100)
 #define IWL_MAX_PLCP_ERR_THRESHOLD_MAX	(255)
 
+enum iwl_reset {
+	IWL_RF_RESET = 0,
+	IWL_FW_RESET,
+};
+
 struct iwl_priv {
 
 	/* ieee device used by generic ieee processing code */
@@ -1024,6 +1029,9 @@ struct iwl_priv {
 	/* storing the jiffies when the plcp error rate is received */
 	unsigned long plcp_jiffies;
 
+	/* force reset */
+	unsigned long last_force_reset_jiffies;
+
 	/* we allocate array of iwl4965_channel_info for NIC's valid channels.
 	 *    Access via channel # using indirect index array */
 	struct iwl_channel_info *channel_info;	/* channel info array */
@@ -1046,7 +1054,6 @@ struct iwl_priv {
 	unsigned long scan_start;
 	unsigned long scan_pass_start;
 	unsigned long scan_start_tsf;
-	unsigned long last_internal_scan_jiffies;
 	void *scan;
 	int scan_bands;
 	struct cfg80211_scan_request *scan_request;
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig	2010-04-13 13:36:50.000000000 -0400
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c	2010-04-13 13:38:40.000000000 -0400
@@ -662,7 +662,7 @@ void iwl_rx_statistics(struct iwl_priv *
 			 * Reset the RF radio due to the high plcp
 			 * error rate
 			 */
-			iwl_force_rf_reset(priv);
+			iwl_force_reset(priv, IWL_RF_RESET);
 		}
 	}
 
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c.orig	2010-04-13 13:36:42.000000000 -0400
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c	2010-04-13 13:38:40.000000000 -0400
@@ -254,8 +254,6 @@ static void iwl_rx_scan_complete_notif(s
 	priv->last_scan_jiffies = jiffies;
 	if (!priv->is_internal_short_scan)
 		priv->next_scan_jiffies = 0;
-	else
-		priv->last_internal_scan_jiffies = jiffies;
 
 	IWL_DEBUG_INFO(priv, "Setting scan to off\n");
 
@@ -564,8 +562,6 @@ EXPORT_SYMBOL(iwl_mac_hw_scan);
  * internal short scan, this function should only been called while associated.
  * It will reset and tune the radio to prevent possible RF related problem
  */
-#define IWL_DELAY_NEXT_INTERNAL_SCAN (HZ*1)
-
 int iwl_internal_short_hw_scan(struct iwl_priv *priv)
 {
 	int ret = 0;
@@ -585,12 +581,6 @@ int iwl_internal_short_hw_scan(struct iw
 		ret = -EAGAIN;
 		goto out;
 	}
-	if (priv->last_internal_scan_jiffies &&
-	    time_after(priv->last_internal_scan_jiffies +
-		       IWL_DELAY_NEXT_INTERNAL_SCAN, jiffies)) {
-		IWL_DEBUG_SCAN(priv, "internal scan rejected\n");
-		goto out;
-	}
 
 	priv->scan_bands = 0;
 	if (priv->band == IEEE80211_BAND_5GHZ)