162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
562306a36Sopenharmony_ci * Copyright (C) 2019 Intel Corporation
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Portions of this file are derived from the ipw3945 project, as well
862306a36Sopenharmony_ci * as portions of the ieee80211 subsystem header files.
962306a36Sopenharmony_ci *****************************************************************************/
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <net/mac80211.h>
1662306a36Sopenharmony_ci#include "iwl-io.h"
1762306a36Sopenharmony_ci#include "iwl-debug.h"
1862306a36Sopenharmony_ci#include "iwl-trans.h"
1962306a36Sopenharmony_ci#include "iwl-modparams.h"
2062306a36Sopenharmony_ci#include "dev.h"
2162306a36Sopenharmony_ci#include "agn.h"
2262306a36Sopenharmony_ci#include "commands.h"
2362306a36Sopenharmony_ci#include "power.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic bool force_cam = true;
2662306a36Sopenharmony_cimodule_param(force_cam, bool, 0644);
2762306a36Sopenharmony_ciMODULE_PARM_DESC(force_cam, "force continuously aware mode (no power saving at all)");
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * Setting power level allows the card to go to sleep when not busy.
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * We calculate a sleep command based on the required latency, which
3362306a36Sopenharmony_ci * we get from mac80211. In order to handle thermal throttling, we can
3462306a36Sopenharmony_ci * also use pre-defined power levels.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/*
3862306a36Sopenharmony_ci * This defines the old power levels. They are still used by default
3962306a36Sopenharmony_ci * (level 1) and for thermal throttle (levels 3 through 5)
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct iwl_power_vec_entry {
4362306a36Sopenharmony_ci	struct iwl_powertable_cmd cmd;
4462306a36Sopenharmony_ci	u8 no_dtim;	/* number of skip dtim */
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define IWL_DTIM_RANGE_0_MAX	2
4862306a36Sopenharmony_ci#define IWL_DTIM_RANGE_1_MAX	10
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define NOSLP cpu_to_le16(0), 0, 0
5162306a36Sopenharmony_ci#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0
5262306a36Sopenharmony_ci#define ASLP (IWL_POWER_POWER_SAVE_ENA_MSK |	\
5362306a36Sopenharmony_ci		IWL_POWER_POWER_MANAGEMENT_ENA_MSK | \
5462306a36Sopenharmony_ci		IWL_POWER_ADVANCE_PM_ENA_MSK)
5562306a36Sopenharmony_ci#define ASLP_TOUT(T) cpu_to_le32(T)
5662306a36Sopenharmony_ci#define TU_TO_USEC 1024
5762306a36Sopenharmony_ci#define SLP_TOUT(T) cpu_to_le32((T) * TU_TO_USEC)
5862306a36Sopenharmony_ci#define SLP_VEC(X0, X1, X2, X3, X4) {cpu_to_le32(X0), \
5962306a36Sopenharmony_ci				     cpu_to_le32(X1), \
6062306a36Sopenharmony_ci				     cpu_to_le32(X2), \
6162306a36Sopenharmony_ci				     cpu_to_le32(X3), \
6262306a36Sopenharmony_ci				     cpu_to_le32(X4)}
6362306a36Sopenharmony_ci/* default power management (not Tx power) table values */
6462306a36Sopenharmony_ci/* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */
6562306a36Sopenharmony_ci/* DTIM 0 - 2 */
6662306a36Sopenharmony_cistatic const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = {
6762306a36Sopenharmony_ci	{{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0},
6862306a36Sopenharmony_ci	{{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0},
6962306a36Sopenharmony_ci	{{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0},
7062306a36Sopenharmony_ci	{{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1},
7162306a36Sopenharmony_ci	{{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2}
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */
7662306a36Sopenharmony_ci/* DTIM 3 - 10 */
7762306a36Sopenharmony_cistatic const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = {
7862306a36Sopenharmony_ci	{{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0},
7962306a36Sopenharmony_ci	{{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0},
8062306a36Sopenharmony_ci	{{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0},
8162306a36Sopenharmony_ci	{{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1},
8262306a36Sopenharmony_ci	{{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2}
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* for DTIM period > IWL_DTIM_RANGE_1_MAX */
8662306a36Sopenharmony_ci/* DTIM 11 - */
8762306a36Sopenharmony_cistatic const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = {
8862306a36Sopenharmony_ci	{{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0},
8962306a36Sopenharmony_ci	{{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0},
9062306a36Sopenharmony_ci	{{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0},
9162306a36Sopenharmony_ci	{{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0},
9262306a36Sopenharmony_ci	{{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0}
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/* advance power management */
9662306a36Sopenharmony_ci/* DTIM 0 - 2 */
9762306a36Sopenharmony_cistatic const struct iwl_power_vec_entry apm_range_0[IWL_POWER_NUM] = {
9862306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
9962306a36Sopenharmony_ci		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
10062306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
10162306a36Sopenharmony_ci		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
10262306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
10362306a36Sopenharmony_ci		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
10462306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
10562306a36Sopenharmony_ci		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
10662306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
10762306a36Sopenharmony_ci		SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2}
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */
11262306a36Sopenharmony_ci/* DTIM 3 - 10 */
11362306a36Sopenharmony_cistatic const struct iwl_power_vec_entry apm_range_1[IWL_POWER_NUM] = {
11462306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
11562306a36Sopenharmony_ci		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
11662306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
11762306a36Sopenharmony_ci		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
11862306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
11962306a36Sopenharmony_ci		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
12062306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
12162306a36Sopenharmony_ci		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
12262306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
12362306a36Sopenharmony_ci		SLP_VEC(1, 2, 6, 8, 0xFF), 0}, 2}
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/* for DTIM period > IWL_DTIM_RANGE_1_MAX */
12762306a36Sopenharmony_ci/* DTIM 11 - */
12862306a36Sopenharmony_cistatic const struct iwl_power_vec_entry apm_range_2[IWL_POWER_NUM] = {
12962306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
13062306a36Sopenharmony_ci		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
13162306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
13262306a36Sopenharmony_ci		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
13362306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
13462306a36Sopenharmony_ci		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
13562306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
13662306a36Sopenharmony_ci		SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0},
13762306a36Sopenharmony_ci	{{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50),
13862306a36Sopenharmony_ci		SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2}
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic void iwl_static_sleep_cmd(struct iwl_priv *priv,
14262306a36Sopenharmony_ci				 struct iwl_powertable_cmd *cmd,
14362306a36Sopenharmony_ci				 enum iwl_power_level lvl, int period)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	const struct iwl_power_vec_entry *table;
14662306a36Sopenharmony_ci	int max_sleep[IWL_POWER_VEC_SIZE] = { 0 };
14762306a36Sopenharmony_ci	int i;
14862306a36Sopenharmony_ci	u8 skip;
14962306a36Sopenharmony_ci	u32 slp_itrvl;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (priv->lib->adv_pm) {
15262306a36Sopenharmony_ci		table = apm_range_2;
15362306a36Sopenharmony_ci		if (period <= IWL_DTIM_RANGE_1_MAX)
15462306a36Sopenharmony_ci			table = apm_range_1;
15562306a36Sopenharmony_ci		if (period <= IWL_DTIM_RANGE_0_MAX)
15662306a36Sopenharmony_ci			table = apm_range_0;
15762306a36Sopenharmony_ci	} else {
15862306a36Sopenharmony_ci		table = range_2;
15962306a36Sopenharmony_ci		if (period <= IWL_DTIM_RANGE_1_MAX)
16062306a36Sopenharmony_ci			table = range_1;
16162306a36Sopenharmony_ci		if (period <= IWL_DTIM_RANGE_0_MAX)
16262306a36Sopenharmony_ci			table = range_0;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (WARN_ON(lvl < 0 || lvl >= IWL_POWER_NUM))
16662306a36Sopenharmony_ci		memset(cmd, 0, sizeof(*cmd));
16762306a36Sopenharmony_ci	else
16862306a36Sopenharmony_ci		*cmd = table[lvl].cmd;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (period == 0) {
17162306a36Sopenharmony_ci		skip = 0;
17262306a36Sopenharmony_ci		period = 1;
17362306a36Sopenharmony_ci		for (i = 0; i < IWL_POWER_VEC_SIZE; i++)
17462306a36Sopenharmony_ci			max_sleep[i] =  1;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	} else {
17762306a36Sopenharmony_ci		skip = table[lvl].no_dtim;
17862306a36Sopenharmony_ci		for (i = 0; i < IWL_POWER_VEC_SIZE; i++)
17962306a36Sopenharmony_ci			max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]);
18062306a36Sopenharmony_ci		max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);
18462306a36Sopenharmony_ci	/* figure out the listen interval based on dtim period and skip */
18562306a36Sopenharmony_ci	if (slp_itrvl == 0xFF)
18662306a36Sopenharmony_ci		cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
18762306a36Sopenharmony_ci			cpu_to_le32(period * (skip + 1));
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);
19062306a36Sopenharmony_ci	if (slp_itrvl > period)
19162306a36Sopenharmony_ci		cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
19262306a36Sopenharmony_ci			cpu_to_le32((slp_itrvl / period) * period);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (skip)
19562306a36Sopenharmony_ci		cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK;
19662306a36Sopenharmony_ci	else
19762306a36Sopenharmony_ci		cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (priv->trans->trans_cfg->base_params->shadow_reg_enable)
20062306a36Sopenharmony_ci		cmd->flags |= IWL_POWER_SHADOW_REG_ENA;
20162306a36Sopenharmony_ci	else
20262306a36Sopenharmony_ci		cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (iwl_advanced_bt_coexist(priv)) {
20562306a36Sopenharmony_ci		if (!priv->lib->bt_params->bt_sco_disable)
20662306a36Sopenharmony_ci			cmd->flags |= IWL_POWER_BT_SCO_ENA;
20762306a36Sopenharmony_ci		else
20862306a36Sopenharmony_ci			cmd->flags &= ~IWL_POWER_BT_SCO_ENA;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);
21362306a36Sopenharmony_ci	if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL)
21462306a36Sopenharmony_ci		cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
21562306a36Sopenharmony_ci			cpu_to_le32(IWL_CONN_MAX_LISTEN_INTERVAL);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* enforce max sleep interval */
21862306a36Sopenharmony_ci	for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) {
21962306a36Sopenharmony_ci		if (le32_to_cpu(cmd->sleep_interval[i]) >
22062306a36Sopenharmony_ci		    (max_sleep[i] * period))
22162306a36Sopenharmony_ci			cmd->sleep_interval[i] =
22262306a36Sopenharmony_ci				cpu_to_le32(max_sleep[i] * period);
22362306a36Sopenharmony_ci		if (i != (IWL_POWER_VEC_SIZE - 1)) {
22462306a36Sopenharmony_ci			if (le32_to_cpu(cmd->sleep_interval[i]) >
22562306a36Sopenharmony_ci			    le32_to_cpu(cmd->sleep_interval[i+1]))
22662306a36Sopenharmony_ci				cmd->sleep_interval[i] =
22762306a36Sopenharmony_ci					cmd->sleep_interval[i+1];
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (priv->power_data.bus_pm)
23262306a36Sopenharmony_ci		cmd->flags |= IWL_POWER_PCI_PM_MSK;
23362306a36Sopenharmony_ci	else
23462306a36Sopenharmony_ci		cmd->flags &= ~IWL_POWER_PCI_PM_MSK;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n",
23762306a36Sopenharmony_ci			skip, period);
23862306a36Sopenharmony_ci	/* The power level here is 0-4 (used as array index), but user expects
23962306a36Sopenharmony_ci	to see 1-5 (according to spec). */
24062306a36Sopenharmony_ci	IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1);
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic void iwl_power_sleep_cam_cmd(struct iwl_priv *priv,
24462306a36Sopenharmony_ci				    struct iwl_powertable_cmd *cmd)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	memset(cmd, 0, sizeof(*cmd));
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (priv->power_data.bus_pm)
24962306a36Sopenharmony_ci		cmd->flags |= IWL_POWER_PCI_PM_MSK;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	IWL_DEBUG_POWER(priv, "Sleep command for CAM\n");
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	IWL_DEBUG_POWER(priv, "Sending power/sleep command\n");
25762306a36Sopenharmony_ci	IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags);
25862306a36Sopenharmony_ci	IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout));
25962306a36Sopenharmony_ci	IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout));
26062306a36Sopenharmony_ci	IWL_DEBUG_POWER(priv, "Sleep interval vector = { %d , %d , %d , %d , %d }\n",
26162306a36Sopenharmony_ci			le32_to_cpu(cmd->sleep_interval[0]),
26262306a36Sopenharmony_ci			le32_to_cpu(cmd->sleep_interval[1]),
26362306a36Sopenharmony_ci			le32_to_cpu(cmd->sleep_interval[2]),
26462306a36Sopenharmony_ci			le32_to_cpu(cmd->sleep_interval[3]),
26562306a36Sopenharmony_ci			le32_to_cpu(cmd->sleep_interval[4]));
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, 0,
26862306a36Sopenharmony_ci				sizeof(struct iwl_powertable_cmd), cmd);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void iwl_power_build_cmd(struct iwl_priv *priv,
27262306a36Sopenharmony_ci				struct iwl_powertable_cmd *cmd)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS;
27562306a36Sopenharmony_ci	int dtimper;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (force_cam) {
27862306a36Sopenharmony_ci		iwl_power_sleep_cam_cmd(priv, cmd);
27962306a36Sopenharmony_ci		return;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	dtimper = priv->hw->conf.ps_dtim_period ?: 1;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (priv->wowlan)
28562306a36Sopenharmony_ci		iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper);
28662306a36Sopenharmony_ci	else if (!priv->lib->no_idle_support &&
28762306a36Sopenharmony_ci		 priv->hw->conf.flags & IEEE80211_CONF_IDLE)
28862306a36Sopenharmony_ci		iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20);
28962306a36Sopenharmony_ci	else if (iwl_tt_is_low_power_state(priv)) {
29062306a36Sopenharmony_ci		/* in thermal throttling low power state */
29162306a36Sopenharmony_ci		iwl_static_sleep_cmd(priv, cmd,
29262306a36Sopenharmony_ci		    iwl_tt_current_power_mode(priv), dtimper);
29362306a36Sopenharmony_ci	} else if (!enabled)
29462306a36Sopenharmony_ci		iwl_power_sleep_cam_cmd(priv, cmd);
29562306a36Sopenharmony_ci	else if (priv->power_data.debug_sleep_level_override >= 0)
29662306a36Sopenharmony_ci		iwl_static_sleep_cmd(priv, cmd,
29762306a36Sopenharmony_ci				     priv->power_data.debug_sleep_level_override,
29862306a36Sopenharmony_ci				     dtimper);
29962306a36Sopenharmony_ci	else {
30062306a36Sopenharmony_ci		/* Note that the user parameter is 1-5 (according to spec),
30162306a36Sopenharmony_ci		but we pass 0-4 because it acts as an array index. */
30262306a36Sopenharmony_ci		if (iwlwifi_mod_params.power_level > IWL_POWER_INDEX_1 &&
30362306a36Sopenharmony_ci		    iwlwifi_mod_params.power_level <= IWL_POWER_NUM)
30462306a36Sopenharmony_ci			iwl_static_sleep_cmd(priv, cmd,
30562306a36Sopenharmony_ci				iwlwifi_mod_params.power_level - 1, dtimper);
30662306a36Sopenharmony_ci		else
30762306a36Sopenharmony_ci			iwl_static_sleep_cmd(priv, cmd,
30862306a36Sopenharmony_ci				IWL_POWER_INDEX_1, dtimper);
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ciint iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,
31362306a36Sopenharmony_ci		       bool force)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	int ret;
31662306a36Sopenharmony_ci	bool update_chains;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* Don't update the RX chain when chain noise calibration is running */
32162306a36Sopenharmony_ci	update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE ||
32262306a36Sopenharmony_ci			priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force)
32562306a36Sopenharmony_ci		return 0;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (!iwl_is_ready_rf(priv))
32862306a36Sopenharmony_ci		return -EIO;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* scan complete use sleep_power_next, need to be updated */
33162306a36Sopenharmony_ci	memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd));
33262306a36Sopenharmony_ci	if (test_bit(STATUS_SCANNING, &priv->status) && !force) {
33362306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n");
33462306a36Sopenharmony_ci		return 0;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)
33862306a36Sopenharmony_ci		iwl_dvm_set_pmi(priv, true);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	ret = iwl_set_power(priv, cmd);
34162306a36Sopenharmony_ci	if (!ret) {
34262306a36Sopenharmony_ci		if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK))
34362306a36Sopenharmony_ci			iwl_dvm_set_pmi(priv, false);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		if (update_chains)
34662306a36Sopenharmony_ci			iwl_update_chain_flags(priv);
34762306a36Sopenharmony_ci		else
34862306a36Sopenharmony_ci			IWL_DEBUG_POWER(priv,
34962306a36Sopenharmony_ci					"Cannot update the power, chain noise "
35062306a36Sopenharmony_ci					"calibration running: %d\n",
35162306a36Sopenharmony_ci					priv->chain_noise_data.state);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci		memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd));
35462306a36Sopenharmony_ci	} else
35562306a36Sopenharmony_ci		IWL_ERR(priv, "set power fail, ret = %d\n", ret);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return ret;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ciint iwl_power_update_mode(struct iwl_priv *priv, bool force)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct iwl_powertable_cmd cmd;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	iwl_power_build_cmd(priv, &cmd);
36562306a36Sopenharmony_ci	return iwl_power_set_mode(priv, &cmd, force);
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci/* initialize to default */
36962306a36Sopenharmony_civoid iwl_power_initialize(struct iwl_priv *priv)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	priv->power_data.bus_pm = priv->trans->pm_support;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	priv->power_data.debug_sleep_level_override = -1;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	memset(&priv->power_data.sleep_cmd, 0,
37662306a36Sopenharmony_ci		sizeof(priv->power_data.sleep_cmd));
37762306a36Sopenharmony_ci}
378