Commit | Line | Data |
---|---|---|
be663ab6 WYG |
1 | /****************************************************************************** |
2 | * | |
3 | * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved. | |
4 | * | |
5 | * Portions of this file are derived from the ipw3945 project, as well | |
6 | * as portions of the ieee80211 subsystem header files. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of version 2 of the GNU General Public License as | |
10 | * published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | * more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along with | |
18 | * this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA | |
20 | * | |
21 | * The full GNU General Public License is included in this distribution in the | |
22 | * file called LICENSE. | |
23 | * | |
24 | * Contact Information: | |
25 | * Intel Linux Wireless <ilw@linux.intel.com> | |
26 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | |
27 | *****************************************************************************/ | |
28 | ||
29 | ||
30 | #include <linux/kernel.h> | |
31 | #include <linux/module.h> | |
32 | #include <linux/slab.h> | |
33 | #include <linux/init.h> | |
34 | ||
35 | #include <net/mac80211.h> | |
36 | ||
37 | #include "iwl-eeprom.h" | |
38 | #include "iwl-dev.h" | |
39 | #include "iwl-core.h" | |
40 | #include "iwl-io.h" | |
41 | #include "iwl-commands.h" | |
42 | #include "iwl-debug.h" | |
43 | #include "iwl-power.h" | |
44 | ||
45 | /* | |
46 | * Setting power level allows the card to go to sleep when not busy. | |
47 | * | |
48 | * We calculate a sleep command based on the required latency, which | |
49 | * we get from mac80211. In order to handle thermal throttling, we can | |
50 | * also use pre-defined power levels. | |
51 | */ | |
52 | ||
53 | /* | |
54 | * This defines the old power levels. They are still used by default | |
55 | * (level 1) and for thermal throttle (levels 3 through 5) | |
56 | */ | |
57 | ||
58 | struct iwl_power_vec_entry { | |
59 | struct iwl_powertable_cmd cmd; | |
60 | u8 no_dtim; /* number of skip dtim */ | |
61 | }; | |
62 | ||
63 | static void iwl_legacy_power_sleep_cam_cmd(struct iwl_priv *priv, | |
64 | struct iwl_powertable_cmd *cmd) | |
65 | { | |
66 | memset(cmd, 0, sizeof(*cmd)); | |
67 | ||
68 | if (priv->power_data.pci_pm) | |
69 | cmd->flags |= IWL_POWER_PCI_PM_MSK; | |
70 | ||
71 | IWL_DEBUG_POWER(priv, "Sleep command for CAM\n"); | |
72 | } | |
73 | ||
74 | static int | |
75 | iwl_legacy_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) | |
76 | { | |
77 | IWL_DEBUG_POWER(priv, "Sending power/sleep command\n"); | |
78 | IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags); | |
79 | IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", | |
80 | le32_to_cpu(cmd->tx_data_timeout)); | |
81 | IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", | |
82 | le32_to_cpu(cmd->rx_data_timeout)); | |
83 | IWL_DEBUG_POWER(priv, | |
84 | "Sleep interval vector = { %d , %d , %d , %d , %d }\n", | |
85 | le32_to_cpu(cmd->sleep_interval[0]), | |
86 | le32_to_cpu(cmd->sleep_interval[1]), | |
87 | le32_to_cpu(cmd->sleep_interval[2]), | |
88 | le32_to_cpu(cmd->sleep_interval[3]), | |
89 | le32_to_cpu(cmd->sleep_interval[4])); | |
90 | ||
91 | return iwl_legacy_send_cmd_pdu(priv, POWER_TABLE_CMD, | |
92 | sizeof(struct iwl_powertable_cmd), cmd); | |
93 | } | |
94 | ||
95 | int | |
96 | iwl_legacy_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, | |
97 | bool force) | |
98 | { | |
99 | int ret; | |
100 | bool update_chains; | |
101 | ||
102 | lockdep_assert_held(&priv->mutex); | |
103 | ||
104 | /* Don't update the RX chain when chain noise calibration is running */ | |
105 | update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE || | |
106 | priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE; | |
107 | ||
108 | if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) | |
109 | return 0; | |
110 | ||
111 | if (!iwl_legacy_is_ready_rf(priv)) | |
112 | return -EIO; | |
113 | ||
114 | /* scan complete use sleep_power_next, need to be updated */ | |
115 | memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); | |
116 | if (test_bit(STATUS_SCANNING, &priv->status) && !force) { | |
117 | IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n"); | |
118 | return 0; | |
119 | } | |
120 | ||
121 | if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK) | |
122 | set_bit(STATUS_POWER_PMI, &priv->status); | |
123 | ||
124 | ret = iwl_legacy_set_power(priv, cmd); | |
125 | if (!ret) { | |
126 | if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)) | |
127 | clear_bit(STATUS_POWER_PMI, &priv->status); | |
128 | ||
129 | if (priv->cfg->ops->lib->update_chain_flags && update_chains) | |
130 | priv->cfg->ops->lib->update_chain_flags(priv); | |
131 | else if (priv->cfg->ops->lib->update_chain_flags) | |
132 | IWL_DEBUG_POWER(priv, | |
133 | "Cannot update the power, chain noise " | |
134 | "calibration running: %d\n", | |
135 | priv->chain_noise_data.state); | |
136 | ||
137 | memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)); | |
138 | } else | |
139 | IWL_ERR(priv, "set power fail, ret = %d", ret); | |
140 | ||
141 | return ret; | |
142 | } | |
143 | ||
144 | int iwl_legacy_power_update_mode(struct iwl_priv *priv, bool force) | |
145 | { | |
146 | struct iwl_powertable_cmd cmd; | |
147 | ||
148 | iwl_legacy_power_sleep_cam_cmd(priv, &cmd); | |
149 | return iwl_legacy_power_set_mode(priv, &cmd, force); | |
150 | } | |
151 | EXPORT_SYMBOL(iwl_legacy_power_update_mode); | |
152 | ||
153 | /* initialize to default */ | |
154 | void iwl_legacy_power_initialize(struct iwl_priv *priv) | |
155 | { | |
156 | u16 lctl = iwl_legacy_pcie_link_ctl(priv); | |
157 | ||
158 | priv->power_data.pci_pm = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN); | |
159 | ||
160 | priv->power_data.debug_sleep_level_override = -1; | |
161 | ||
162 | memset(&priv->power_data.sleep_cmd, 0, | |
163 | sizeof(priv->power_data.sleep_cmd)); | |
164 | } | |
165 | EXPORT_SYMBOL(iwl_legacy_power_initialize); |