brintos

brintos / linux-shallow public Read only

0
0
Text · 11.8 KiB · 6d16a71 Raw
378 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/******************************************************************************3 *4 * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.5 * Copyright (C) 2019 Intel Corporation6 *7 * Portions of this file are derived from the ipw3945 project, as well8 * as portions of the ieee80211 subsystem header files.9 *****************************************************************************/10 11 12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/slab.h>15#include <net/mac80211.h>16#include "iwl-io.h"17#include "iwl-debug.h"18#include "iwl-trans.h"19#include "iwl-modparams.h"20#include "dev.h"21#include "agn.h"22#include "commands.h"23#include "power.h"24 25static bool force_cam = true;26module_param(force_cam, bool, 0644);27MODULE_PARM_DESC(force_cam, "force continuously aware mode (no power saving at all)");28 29/*30 * Setting power level allows the card to go to sleep when not busy.31 *32 * We calculate a sleep command based on the required latency, which33 * we get from mac80211. In order to handle thermal throttling, we can34 * also use pre-defined power levels.35 */36 37/*38 * This defines the old power levels. They are still used by default39 * (level 1) and for thermal throttle (levels 3 through 5)40 */41 42struct iwl_power_vec_entry {43	struct iwl_powertable_cmd cmd;44	u8 no_dtim;	/* number of skip dtim */45};46 47#define IWL_DTIM_RANGE_0_MAX	248#define IWL_DTIM_RANGE_1_MAX	1049 50#define NOSLP cpu_to_le16(0), 0, 051#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 052#define ASLP (IWL_POWER_POWER_SAVE_ENA_MSK |	\53		IWL_POWER_POWER_MANAGEMENT_ENA_MSK | \54		IWL_POWER_ADVANCE_PM_ENA_MSK)55#define ASLP_TOUT(T) cpu_to_le32(T)56#define TU_TO_USEC 102457#define SLP_TOUT(T) cpu_to_le32((T) * TU_TO_USEC)58#define SLP_VEC(X0, X1, X2, X3, X4) {cpu_to_le32(X0), \59				     cpu_to_le32(X1), \60				     cpu_to_le32(X2), \61				     cpu_to_le32(X3), \62				     cpu_to_le32(X4)}63/* default power management (not Tx power) table values */64/* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */65/* DTIM 0 - 2 */66static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = {67	{{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0},68	{{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0},69	{{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0},70	{{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1},71	{{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2}72};73 74 75/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */76/* DTIM 3 - 10 */77static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = {78	{{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0},79	{{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0},80	{{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0},81	{{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1},82	{{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2}83};84 85/* for DTIM period > IWL_DTIM_RANGE_1_MAX */86/* DTIM 11 - */87static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = {88	{{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0},89	{{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0},90	{{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0},91	{{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0},92	{{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0}93};94 95/* advance power management */96/* DTIM 0 - 2 */97static const struct iwl_power_vec_entry apm_range_0[IWL_POWER_NUM] = {98	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),99		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},100	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),101		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},102	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),103		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},104	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),105		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},106	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),107		SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2}108};109 110 111/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */112/* DTIM 3 - 10 */113static const struct iwl_power_vec_entry apm_range_1[IWL_POWER_NUM] = {114	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),115		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},116	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),117		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},118	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),119		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},120	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),121		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},122	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),123		SLP_VEC(1, 2, 6, 8, 0xFF), 0}, 2}124};125 126/* for DTIM period > IWL_DTIM_RANGE_1_MAX */127/* DTIM 11 - */128static const struct iwl_power_vec_entry apm_range_2[IWL_POWER_NUM] = {129	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),130		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},131	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),132		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},133	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),134		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},135	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),136		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},137	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),138		SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2}139};140 141static void iwl_static_sleep_cmd(struct iwl_priv *priv,142				 struct iwl_powertable_cmd *cmd,143				 enum iwl_power_level lvl, int period)144{145	const struct iwl_power_vec_entry *table;146	int max_sleep[IWL_POWER_VEC_SIZE] = { 0 };147	int i;148	u8 skip;149	u32 slp_itrvl;150 151	if (priv->lib->adv_pm) {152		table = apm_range_2;153		if (period <= IWL_DTIM_RANGE_1_MAX)154			table = apm_range_1;155		if (period <= IWL_DTIM_RANGE_0_MAX)156			table = apm_range_0;157	} else {158		table = range_2;159		if (period <= IWL_DTIM_RANGE_1_MAX)160			table = range_1;161		if (period <= IWL_DTIM_RANGE_0_MAX)162			table = range_0;163	}164 165	if (WARN_ON(lvl < 0 || lvl >= IWL_POWER_NUM))166		memset(cmd, 0, sizeof(*cmd));167	else168		*cmd = table[lvl].cmd;169 170	if (period == 0) {171		skip = 0;172		period = 1;173		for (i = 0; i < IWL_POWER_VEC_SIZE; i++)174			max_sleep[i] =  1;175 176	} else {177		skip = table[lvl].no_dtim;178		for (i = 0; i < IWL_POWER_VEC_SIZE; i++)179			max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]);180		max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1;181	}182 183	slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);184	/* figure out the listen interval based on dtim period and skip */185	if (slp_itrvl == 0xFF)186		cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =187			cpu_to_le32(period * (skip + 1));188 189	slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);190	if (slp_itrvl > period)191		cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =192			cpu_to_le32((slp_itrvl / period) * period);193 194	if (skip)195		cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK;196	else197		cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK;198 199	if (priv->trans->trans_cfg->base_params->shadow_reg_enable)200		cmd->flags |= IWL_POWER_SHADOW_REG_ENA;201	else202		cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA;203 204	if (iwl_advanced_bt_coexist(priv)) {205		if (!priv->lib->bt_params->bt_sco_disable)206			cmd->flags |= IWL_POWER_BT_SCO_ENA;207		else208			cmd->flags &= ~IWL_POWER_BT_SCO_ENA;209	}210 211 212	slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);213	if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL)214		cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =215			cpu_to_le32(IWL_CONN_MAX_LISTEN_INTERVAL);216 217	/* enforce max sleep interval */218	for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) {219		if (le32_to_cpu(cmd->sleep_interval[i]) >220		    (max_sleep[i] * period))221			cmd->sleep_interval[i] =222				cpu_to_le32(max_sleep[i] * period);223		if (i != (IWL_POWER_VEC_SIZE - 1)) {224			if (le32_to_cpu(cmd->sleep_interval[i]) >225			    le32_to_cpu(cmd->sleep_interval[i+1]))226				cmd->sleep_interval[i] =227					cmd->sleep_interval[i+1];228		}229	}230 231	if (priv->power_data.bus_pm)232		cmd->flags |= IWL_POWER_PCI_PM_MSK;233	else234		cmd->flags &= ~IWL_POWER_PCI_PM_MSK;235 236	IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n",237			skip, period);238	/* The power level here is 0-4 (used as array index), but user expects239	to see 1-5 (according to spec). */240	IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1);241}242 243static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv,244				    struct iwl_powertable_cmd *cmd)245{246	memset(cmd, 0, sizeof(*cmd));247 248	if (priv->power_data.bus_pm)249		cmd->flags |= IWL_POWER_PCI_PM_MSK;250 251	IWL_DEBUG_POWER(priv, "Sleep command for CAM\n");252}253 254static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd)255{256	IWL_DEBUG_POWER(priv, "Sending power/sleep command\n");257	IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags);258	IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout));259	IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout));260	IWL_DEBUG_POWER(priv, "Sleep interval vector = { %d , %d , %d , %d , %d }\n",261			le32_to_cpu(cmd->sleep_interval[0]),262			le32_to_cpu(cmd->sleep_interval[1]),263			le32_to_cpu(cmd->sleep_interval[2]),264			le32_to_cpu(cmd->sleep_interval[3]),265			le32_to_cpu(cmd->sleep_interval[4]));266 267	return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, 0,268				sizeof(struct iwl_powertable_cmd), cmd);269}270 271static void iwl_power_build_cmd(struct iwl_priv *priv,272				struct iwl_powertable_cmd *cmd)273{274	bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS;275	int dtimper;276 277	if (force_cam) {278		iwl_power_sleep_cam_cmd(priv, cmd);279		return;280	}281 282	dtimper = priv->hw->conf.ps_dtim_period ?: 1;283 284	if (priv->wowlan)285		iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper);286	else if (!priv->lib->no_idle_support &&287		 priv->hw->conf.flags & IEEE80211_CONF_IDLE)288		iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20);289	else if (iwl_tt_is_low_power_state(priv)) {290		/* in thermal throttling low power state */291		iwl_static_sleep_cmd(priv, cmd,292		    iwl_tt_current_power_mode(priv), dtimper);293	} else if (!enabled)294		iwl_power_sleep_cam_cmd(priv, cmd);295	else if (priv->power_data.debug_sleep_level_override >= 0)296		iwl_static_sleep_cmd(priv, cmd,297				     priv->power_data.debug_sleep_level_override,298				     dtimper);299	else {300		/* Note that the user parameter is 1-5 (according to spec),301		but we pass 0-4 because it acts as an array index. */302		if (iwlwifi_mod_params.power_level > IWL_POWER_INDEX_1 &&303		    iwlwifi_mod_params.power_level <= IWL_POWER_NUM)304			iwl_static_sleep_cmd(priv, cmd,305				iwlwifi_mod_params.power_level - 1, dtimper);306		else307			iwl_static_sleep_cmd(priv, cmd,308				IWL_POWER_INDEX_1, dtimper);309	}310}311 312int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,313		       bool force)314{315	int ret;316	bool update_chains;317 318	lockdep_assert_held(&priv->mutex);319 320	/* Don't update the RX chain when chain noise calibration is running */321	update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE ||322			priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE;323 324	if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force)325		return 0;326 327	if (!iwl_is_ready_rf(priv))328		return -EIO;329 330	/* scan complete use sleep_power_next, need to be updated */331	memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd));332	if (test_bit(STATUS_SCANNING, &priv->status) && !force) {333		IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n");334		return 0;335	}336 337	if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)338		iwl_dvm_set_pmi(priv, true);339 340	ret = iwl_set_power(priv, cmd);341	if (!ret) {342		if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK))343			iwl_dvm_set_pmi(priv, false);344 345		if (update_chains)346			iwl_update_chain_flags(priv);347		else348			IWL_DEBUG_POWER(priv,349					"Cannot update the power, chain noise "350					"calibration running: %d\n",351					priv->chain_noise_data.state);352 353		memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd));354	} else355		IWL_ERR(priv, "set power fail, ret = %d\n", ret);356 357	return ret;358}359 360int iwl_power_update_mode(struct iwl_priv *priv, bool force)361{362	struct iwl_powertable_cmd cmd;363 364	iwl_power_build_cmd(priv, &cmd);365	return iwl_power_set_mode(priv, &cmd, force);366}367 368/* initialize to default */369void iwl_power_initialize(struct iwl_priv *priv)370{371	priv->power_data.bus_pm = priv->trans->pm_support;372 373	priv->power_data.debug_sleep_level_override = -1;374 375	memset(&priv->power_data.sleep_cmd, 0,376		sizeof(priv->power_data.sleep_cmd));377}378