/****************************************************************************** * * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #include #include #include #include #include #include "iwl-eeprom.h" #include "iwl-dev.h" #include "iwl-core.h" #include "iwl-io.h" #include "iwl-commands.h" #include "iwl-debug.h" #include "iwl-power.h" /* * 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 get from mac80211. In order to handle thermal throttling, we can * also use pre-defined power levels. */ /* * This defines the old power levels. They are still used by default * (level 1) and for thermal throttle (levels 3 through 5) */ struct il_power_vec_entry { struct il_powertable_cmd cmd; u8 no_dtim; /* number of skip dtim */ }; static void il_power_sleep_cam_cmd(struct il_priv *il, struct il_powertable_cmd *cmd) { memset(cmd, 0, sizeof(*cmd)); if (il->power_data.pci_pm) cmd->flags |= IL_POWER_PCI_PM_MSK; D_POWER("Sleep command for CAM\n"); } static int il_set_power(struct il_priv *il, struct il_powertable_cmd *cmd) { D_POWER("Sending power/sleep command\n"); D_POWER("Flags value = 0x%08X\n", cmd->flags); D_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); D_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); D_POWER( "Sleep interval vector = { %d , %d , %d , %d , %d }\n", le32_to_cpu(cmd->sleep_interval[0]), le32_to_cpu(cmd->sleep_interval[1]), le32_to_cpu(cmd->sleep_interval[2]), le32_to_cpu(cmd->sleep_interval[3]), le32_to_cpu(cmd->sleep_interval[4])); return il_send_cmd_pdu(il, POWER_TABLE_CMD, sizeof(struct il_powertable_cmd), cmd); } int il_power_set_mode(struct il_priv *il, struct il_powertable_cmd *cmd, bool force) { int ret; bool update_chains; lockdep_assert_held(&il->mutex); /* Don't update the RX chain when chain noise calibration is running */ update_chains = il->chain_noise_data.state == IL_CHAIN_NOISE_DONE || il->chain_noise_data.state == IL_CHAIN_NOISE_ALIVE; if (!memcmp(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) return 0; if (!il_is_ready_rf(il)) return -EIO; /* scan complete use sleep_power_next, need to be updated */ memcpy(&il->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); if (test_bit(STATUS_SCANNING, &il->status) && !force) { D_INFO("Defer power set mode while scanning\n"); return 0; } if (cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK) set_bit(STATUS_POWER_PMI, &il->status); ret = il_set_power(il, cmd); if (!ret) { if (!(cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK)) clear_bit(STATUS_POWER_PMI, &il->status); if (il->cfg->ops->lib->update_chain_flags && update_chains) il->cfg->ops->lib->update_chain_flags(il); else if (il->cfg->ops->lib->update_chain_flags) D_POWER( "Cannot update the power, chain noise " "calibration running: %d\n", il->chain_noise_data.state); memcpy(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)); } else IL_ERR("set power fail, ret = %d", ret); return ret; } int il_power_update_mode(struct il_priv *il, bool force) { struct il_powertable_cmd cmd; il_power_sleep_cam_cmd(il, &cmd); return il_power_set_mode(il, &cmd, force); } EXPORT_SYMBOL(il_power_update_mode); /* initialize to default */ void il_power_initialize(struct il_priv *il) { u16 lctl = il_pcie_link_ctl(il); il->power_data.pci_pm = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN); il->power_data.debug_sleep_level_override = -1; memset(&il->power_data.sleep_cmd, 0, sizeof(il->power_data.sleep_cmd)); } EXPORT_SYMBOL(il_power_initialize);