Commit dbdac2b5 authored by Stanislaw Gruszka's avatar Stanislaw Gruszka Committed by John W. Linville

iwlegacy: properly enable power saving

Even if we mark PS on, device still worked in normal mode. Patch
corrects that and now we send proper powertable command to device,
which put it in sleep mode when PS is on.
Reported-and-tested-by: default avatarTino Keitel <tino.keitel@tikei.de>
Tested-by: default avatarPedro Francisco <pedrogfrancisco@gmail.com>
Signed-off-by: default avatarStanislaw Gruszka <sgruszka@redhat.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 8e67427a
...@@ -2271,6 +2271,7 @@ struct il_spectrum_notification { ...@@ -2271,6 +2271,7 @@ struct il_spectrum_notification {
#define IL_POWER_VEC_SIZE 5 #define IL_POWER_VEC_SIZE 5
#define IL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0)) #define IL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0))
#define IL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2))
#define IL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3)) #define IL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3))
struct il3945_powertable_cmd { struct il3945_powertable_cmd {
......
...@@ -1078,29 +1078,82 @@ EXPORT_SYMBOL(il_get_channel_info); ...@@ -1078,29 +1078,82 @@ EXPORT_SYMBOL(il_get_channel_info);
* Setting power level allows the card to go to sleep when not busy. * Setting power level allows the card to go to sleep when not busy.
* *
* We calculate a sleep command based on the required latency, which * We calculate a sleep command based on the required latency, which
* we get from mac80211. In order to handle thermal throttling, we can * we get from mac80211.
* also use pre-defined power levels.
*/ */
/* #define SLP_VEC(X0, X1, X2, X3, X4) { \
* This defines the old power levels. They are still used by default cpu_to_le32(X0), \
* (level 1) and for thermal throttle (levels 3 through 5) cpu_to_le32(X1), \
*/ cpu_to_le32(X2), \
cpu_to_le32(X3), \
struct il_power_vec_entry { cpu_to_le32(X4) \
struct il_powertable_cmd cmd; }
u8 no_dtim; /* number of skip dtim */
};
static void static void
il_power_sleep_cam_cmd(struct il_priv *il, struct il_powertable_cmd *cmd) il_build_powertable_cmd(struct il_priv *il, struct il_powertable_cmd *cmd)
{ {
const __le32 interval[3][IL_POWER_VEC_SIZE] = {
SLP_VEC(2, 2, 4, 6, 0xFF),
SLP_VEC(2, 4, 7, 10, 10),
SLP_VEC(4, 7, 10, 10, 0xFF)
};
int i, dtim_period, no_dtim;
u32 max_sleep;
bool skip;
memset(cmd, 0, sizeof(*cmd)); memset(cmd, 0, sizeof(*cmd));
if (il->power_data.pci_pm) if (il->power_data.pci_pm)
cmd->flags |= IL_POWER_PCI_PM_MSK; cmd->flags |= IL_POWER_PCI_PM_MSK;
D_POWER("Sleep command for CAM\n"); /* if no Power Save, we are done */
if (il->power_data.ps_disabled)
return;
cmd->flags = IL_POWER_DRIVER_ALLOW_SLEEP_MSK;
cmd->keep_alive_seconds = 0;
cmd->debug_flags = 0;
cmd->rx_data_timeout = cpu_to_le32(25 * 1024);
cmd->tx_data_timeout = cpu_to_le32(25 * 1024);
cmd->keep_alive_beacons = 0;
dtim_period = il->vif ? il->vif->bss_conf.dtim_period : 0;
if (dtim_period <= 2) {
memcpy(cmd->sleep_interval, interval[0], sizeof(interval[0]));
no_dtim = 2;
} else if (dtim_period <= 10) {
memcpy(cmd->sleep_interval, interval[1], sizeof(interval[1]));
no_dtim = 2;
} else {
memcpy(cmd->sleep_interval, interval[2], sizeof(interval[2]));
no_dtim = 0;
}
if (dtim_period == 0) {
dtim_period = 1;
skip = false;
} else {
skip = !!no_dtim;
}
if (skip) {
__le32 tmp = cmd->sleep_interval[IL_POWER_VEC_SIZE - 1];
max_sleep = le32_to_cpu(tmp);
if (max_sleep == 0xFF)
max_sleep = dtim_period * (skip + 1);
else if (max_sleep > dtim_period)
max_sleep = (max_sleep / dtim_period) * dtim_period;
cmd->flags |= IL_POWER_SLEEP_OVER_DTIM_MSK;
} else {
max_sleep = dtim_period;
cmd->flags &= ~IL_POWER_SLEEP_OVER_DTIM_MSK;
}
for (i = 0; i < IL_POWER_VEC_SIZE; i++)
if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep)
cmd->sleep_interval[i] = cpu_to_le32(max_sleep);
} }
static int static int
...@@ -1173,7 +1226,8 @@ il_power_update_mode(struct il_priv *il, bool force) ...@@ -1173,7 +1226,8 @@ il_power_update_mode(struct il_priv *il, bool force)
{ {
struct il_powertable_cmd cmd; struct il_powertable_cmd cmd;
il_power_sleep_cam_cmd(il, &cmd); il_build_powertable_cmd(il, &cmd);
return il_power_set_mode(il, &cmd, force); return il_power_set_mode(il, &cmd, force);
} }
EXPORT_SYMBOL(il_power_update_mode); EXPORT_SYMBOL(il_power_update_mode);
...@@ -5081,6 +5135,7 @@ il_mac_config(struct ieee80211_hw *hw, u32 changed) ...@@ -5081,6 +5135,7 @@ il_mac_config(struct ieee80211_hw *hw, u32 changed)
} }
if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) { if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) {
il->power_data.ps_disabled = !(conf->flags & IEEE80211_CONF_PS);
ret = il_power_update_mode(il, false); ret = il_power_update_mode(il, false);
if (ret) if (ret)
D_MAC80211("Error setting sleep level\n"); D_MAC80211("Error setting sleep level\n");
......
...@@ -1123,6 +1123,7 @@ struct il_power_mgr { ...@@ -1123,6 +1123,7 @@ struct il_power_mgr {
struct il_powertable_cmd sleep_cmd_next; struct il_powertable_cmd sleep_cmd_next;
int debug_sleep_level_override; int debug_sleep_level_override;
bool pci_pm; bool pci_pm;
bool ps_disabled;
}; };
struct il_priv { struct il_priv {
......
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