]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/wireless/iwlwifi/iwl-power.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / net / wireless / iwlwifi / iwl-power.c
index 49d7788937a9eccc80891c703ea4220c1a98f100..1eec18d909d8622680877985fbb797565f84ad6c 100644 (file)
@@ -75,6 +75,10 @@ struct iwl_power_vec_entry {
 
 #define NOSLP cpu_to_le16(0), 0, 0
 #define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0
+#define ASLP (IWL_POWER_POWER_SAVE_ENA_MSK |   \
+               IWL_POWER_POWER_MANAGEMENT_ENA_MSK | \
+               IWL_POWER_ADVANCE_PM_ENA_MSK)
+#define ASLP_TOUT(T) cpu_to_le32(T)
 #define TU_TO_USEC 1024
 #define SLP_TOUT(T) cpu_to_le32((T) * TU_TO_USEC)
 #define SLP_VEC(X0, X1, X2, X3, X4) {cpu_to_le32(X0), \
@@ -114,6 +118,52 @@ static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = {
        {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0}
 };
 
+/* advance power management */
+/* DTIM 0 - 2 */
+static const struct iwl_power_vec_entry apm_range_0[IWL_POWER_NUM] = {
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2}
+};
+
+
+/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */
+/* DTIM 3 - 10 */
+static const struct iwl_power_vec_entry apm_range_1[IWL_POWER_NUM] = {
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 6, 8, 0xFF), 0}, 2}
+};
+
+/* for DTIM period > IWL_DTIM_RANGE_1_MAX */
+/* DTIM 11 - */
+static const struct iwl_power_vec_entry apm_range_2[IWL_POWER_NUM] = {
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
+       {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
+               SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2}
+};
+
 static void iwl_static_sleep_cmd(struct iwl_priv *priv,
                                 struct iwl_powertable_cmd *cmd,
                                 enum iwl_power_level lvl, int period)
@@ -124,11 +174,19 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
        u8 skip;
        u32 slp_itrvl;
 
-       table = range_2;
-       if (period <= IWL_DTIM_RANGE_1_MAX)
-               table = range_1;
-       if (period <= IWL_DTIM_RANGE_0_MAX)
-               table = range_0;
+       if (priv->cfg->adv_pm) {
+               table = apm_range_2;
+               if (period <= IWL_DTIM_RANGE_1_MAX)
+                       table = apm_range_1;
+               if (period <= IWL_DTIM_RANGE_0_MAX)
+                       table = apm_range_0;
+       } else {
+               table = range_2;
+               if (period <= IWL_DTIM_RANGE_1_MAX)
+                       table = range_1;
+               if (period <= IWL_DTIM_RANGE_0_MAX)
+                       table = range_0;
+       }
 
        BUG_ON(lvl < 0 || lvl >= IWL_POWER_NUM);
 
@@ -163,6 +221,20 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
        else
                cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK;
 
+       if (priv->cfg->base_params->shadow_reg_enable)
+               cmd->flags |= IWL_POWER_SHADOW_REG_ENA;
+       else
+               cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA;
+
+       if (priv->cfg->bt_params &&
+           priv->cfg->bt_params->advanced_bt_coexist) {
+               if (!priv->cfg->bt_params->bt_sco_disable)
+                       cmd->flags |= IWL_POWER_BT_SCO_ENA;
+               else
+                       cmd->flags &= ~IWL_POWER_BT_SCO_ENA;
+       }
+
+
        slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);
        if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL)
                cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
@@ -236,6 +308,19 @@ static void iwl_power_fill_sleep_cmd(struct iwl_priv *priv,
        if (priv->power_data.pci_pm)
                cmd->flags |= IWL_POWER_PCI_PM_MSK;
 
+       if (priv->cfg->base_params->shadow_reg_enable)
+               cmd->flags |= IWL_POWER_SHADOW_REG_ENA;
+       else
+               cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA;
+
+       if (priv->cfg->bt_params &&
+           priv->cfg->bt_params->advanced_bt_coexist) {
+               if (!priv->cfg->bt_params->bt_sco_disable)
+                       cmd->flags |= IWL_POWER_BT_SCO_ENA;
+               else
+                       cmd->flags &= ~IWL_POWER_BT_SCO_ENA;
+       }
+
        cmd->rx_data_timeout = cpu_to_le32(1000 * dynps_ms);
        cmd->tx_data_timeout = cpu_to_le32(1000 * dynps_ms);
 
@@ -263,70 +348,95 @@ static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd)
                                sizeof(struct iwl_powertable_cmd), cmd);
 }
 
-/* priv->mutex must be held */
-int iwl_power_update_mode(struct iwl_priv *priv, bool force)
+static void iwl_power_build_cmd(struct iwl_priv *priv,
+                               struct iwl_powertable_cmd *cmd)
 {
-       int ret = 0;
        bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS;
-       bool update_chains;
-       struct iwl_powertable_cmd cmd;
        int dtimper;
 
-       /* Don't update the RX chain when chain noise calibration is running */
-       update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE ||
-                       priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE;
-
        dtimper = priv->hw->conf.ps_dtim_period ?: 1;
 
        if (priv->cfg->base_params->broken_powersave)
-               iwl_power_sleep_cam_cmd(priv, &cmd);
+               iwl_power_sleep_cam_cmd(priv, cmd);
        else if (priv->cfg->base_params->supports_idle &&
                 priv->hw->conf.flags & IEEE80211_CONF_IDLE)
-               iwl_static_sleep_cmd(priv, &cmd, IWL_POWER_INDEX_5, 20);
+               iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20);
        else if (priv->cfg->ops->lib->tt_ops.lower_power_detection &&
                 priv->cfg->ops->lib->tt_ops.tt_power_mode &&
                 priv->cfg->ops->lib->tt_ops.lower_power_detection(priv)) {
                /* in thermal throttling low power state */
-               iwl_static_sleep_cmd(priv, &cmd,
+               iwl_static_sleep_cmd(priv, cmd,
                    priv->cfg->ops->lib->tt_ops.tt_power_mode(priv), dtimper);
        } else if (!enabled)
-               iwl_power_sleep_cam_cmd(priv, &cmd);
+               iwl_power_sleep_cam_cmd(priv, cmd);
        else if (priv->power_data.debug_sleep_level_override >= 0)
-               iwl_static_sleep_cmd(priv, &cmd,
+               iwl_static_sleep_cmd(priv, cmd,
                                     priv->power_data.debug_sleep_level_override,
                                     dtimper);
        else if (no_sleep_autoadjust)
-               iwl_static_sleep_cmd(priv, &cmd, IWL_POWER_INDEX_1, dtimper);
+               iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_1, dtimper);
        else
-               iwl_power_fill_sleep_cmd(priv, &cmd,
+               iwl_power_fill_sleep_cmd(priv, cmd,
                                         priv->hw->conf.dynamic_ps_timeout,
                                         priv->hw->conf.max_sleep_period);
+}
+
+int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,
+                      bool force)
+{
+       int ret;
+       bool update_chains;
+
+       lockdep_assert_held(&priv->mutex);
+
+       /* Don't update the RX chain when chain noise calibration is running */
+       update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE ||
+                       priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE;
+
+       if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force)
+               return 0;
+
+       if (!iwl_is_ready_rf(priv))
+               return -EIO;
 
-       if (iwl_is_ready_rf(priv) &&
-           (memcmp(&priv->power_data.sleep_cmd, &cmd, sizeof(cmd)) || force)) {
-               if (cmd.flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)
-                       set_bit(STATUS_POWER_PMI, &priv->status);
-
-               ret = iwl_set_power(priv, &cmd);
-               if (!ret) {
-                       if (!(cmd.flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK))
-                               clear_bit(STATUS_POWER_PMI, &priv->status);
-
-                       if (priv->cfg->ops->lib->update_chain_flags &&
-                           update_chains)
-                               priv->cfg->ops->lib->update_chain_flags(priv);
-                       else if (priv->cfg->ops->lib->update_chain_flags)
-                               IWL_DEBUG_POWER(priv,
+       /* scan complete use sleep_power_next, need to be updated */
+       memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd));
+       if (test_bit(STATUS_SCANNING, &priv->status) && !force) {
+               IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n");
+               return 0;
+       }
+
+       if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)
+               set_bit(STATUS_POWER_PMI, &priv->status);
+
+       ret = iwl_set_power(priv, cmd);
+       if (!ret) {
+               if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK))
+                       clear_bit(STATUS_POWER_PMI, &priv->status);
+
+               if (priv->cfg->ops->lib->update_chain_flags && update_chains)
+                       priv->cfg->ops->lib->update_chain_flags(priv);
+               else if (priv->cfg->ops->lib->update_chain_flags)
+                       IWL_DEBUG_POWER(priv,
                                        "Cannot update the power, chain noise "
                                        "calibration running: %d\n",
                                        priv->chain_noise_data.state);
-                       memcpy(&priv->power_data.sleep_cmd, &cmd, sizeof(cmd));
-               } else
-                       IWL_ERR(priv, "set power fail, ret = %d", ret);
-       }
+
+               memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd));
+       } else
+               IWL_ERR(priv, "set power fail, ret = %d", ret);
 
        return ret;
 }
+EXPORT_SYMBOL(iwl_power_set_mode);
+
+int iwl_power_update_mode(struct iwl_priv *priv, bool force)
+{
+       struct iwl_powertable_cmd cmd;
+
+       iwl_power_build_cmd(priv, &cmd);
+       return iwl_power_set_mode(priv, &cmd, force);
+}
 EXPORT_SYMBOL(iwl_power_update_mode);
 
 /* initialize to default */