Commit | Line | Data |
---|---|---|
bbefb871 MSS |
1 | /* |
2 | * Copyright (c) 2008-2011 Atheros Communications Inc. | |
3 | * | |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
17 | #include <linux/export.h> | |
18 | #include "hw.h" | |
528e5d36 | 19 | #include "hw-ops.h" |
bbefb871 MSS |
20 | #include "ar9003_phy.h" |
21 | #include "ar9003_mci.h" | |
22 | ||
23 | static void ar9003_mci_reset_req_wakeup(struct ath_hw *ah) | |
24 | { | |
bbefb871 MSS |
25 | REG_RMW_FIELD(ah, AR_MCI_COMMAND2, |
26 | AR_MCI_COMMAND2_RESET_REQ_WAKEUP, 1); | |
27 | udelay(1); | |
28 | REG_RMW_FIELD(ah, AR_MCI_COMMAND2, | |
29 | AR_MCI_COMMAND2_RESET_REQ_WAKEUP, 0); | |
30 | } | |
31 | ||
32 | static int ar9003_mci_wait_for_interrupt(struct ath_hw *ah, u32 address, | |
33 | u32 bit_position, int time_out) | |
34 | { | |
35 | struct ath_common *common = ath9k_hw_common(ah); | |
36 | ||
37 | while (time_out) { | |
4f6bd1a8 RM |
38 | if (!(REG_READ(ah, address) & bit_position)) { |
39 | udelay(10); | |
40 | time_out -= 10; | |
bbefb871 | 41 | |
4f6bd1a8 RM |
42 | if (time_out < 0) |
43 | break; | |
44 | else | |
45 | continue; | |
46 | } | |
47 | REG_WRITE(ah, address, bit_position); | |
bbefb871 | 48 | |
4f6bd1a8 | 49 | if (address != AR_MCI_INTERRUPT_RX_MSG_RAW) |
bbefb871 | 50 | break; |
4f6bd1a8 RM |
51 | |
52 | if (bit_position & AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE) | |
53 | ar9003_mci_reset_req_wakeup(ah); | |
54 | ||
55 | if (bit_position & (AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING | | |
56 | AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) | |
57 | REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, | |
58 | AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE); | |
59 | ||
60 | REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_RX_MSG); | |
61 | break; | |
bbefb871 MSS |
62 | } |
63 | ||
64 | if (time_out <= 0) { | |
d2182b69 JP |
65 | ath_dbg(common, MCI, |
66 | "MCI Wait for Reg 0x%08x = 0x%08x timeout\n", | |
bbefb871 | 67 | address, bit_position); |
d2182b69 JP |
68 | ath_dbg(common, MCI, |
69 | "MCI INT_RAW = 0x%08x, RX_MSG_RAW = 0x%08x\n", | |
bbefb871 MSS |
70 | REG_READ(ah, AR_MCI_INTERRUPT_RAW), |
71 | REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_RAW)); | |
72 | time_out = 0; | |
73 | } | |
74 | ||
75 | return time_out; | |
76 | } | |
77 | ||
a3f846f1 | 78 | static void ar9003_mci_remote_reset(struct ath_hw *ah, bool wait_done) |
bbefb871 MSS |
79 | { |
80 | u32 payload[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffff00}; | |
81 | ||
82 | ar9003_mci_send_message(ah, MCI_REMOTE_RESET, 0, payload, 16, | |
83 | wait_done, false); | |
84 | udelay(5); | |
85 | } | |
86 | ||
a3f846f1 | 87 | static void ar9003_mci_send_lna_transfer(struct ath_hw *ah, bool wait_done) |
bbefb871 MSS |
88 | { |
89 | u32 payload = 0x00000000; | |
90 | ||
91 | ar9003_mci_send_message(ah, MCI_LNA_TRANS, 0, &payload, 1, | |
92 | wait_done, false); | |
93 | } | |
94 | ||
95 | static void ar9003_mci_send_req_wake(struct ath_hw *ah, bool wait_done) | |
96 | { | |
97 | ar9003_mci_send_message(ah, MCI_REQ_WAKE, MCI_FLAG_DISABLE_TIMESTAMP, | |
98 | NULL, 0, wait_done, false); | |
99 | udelay(5); | |
100 | } | |
101 | ||
a3f846f1 | 102 | static void ar9003_mci_send_sys_waking(struct ath_hw *ah, bool wait_done) |
bbefb871 MSS |
103 | { |
104 | ar9003_mci_send_message(ah, MCI_SYS_WAKING, MCI_FLAG_DISABLE_TIMESTAMP, | |
105 | NULL, 0, wait_done, false); | |
106 | } | |
107 | ||
108 | static void ar9003_mci_send_lna_take(struct ath_hw *ah, bool wait_done) | |
109 | { | |
110 | u32 payload = 0x70000000; | |
111 | ||
112 | ar9003_mci_send_message(ah, MCI_LNA_TAKE, 0, &payload, 1, | |
113 | wait_done, false); | |
114 | } | |
115 | ||
116 | static void ar9003_mci_send_sys_sleeping(struct ath_hw *ah, bool wait_done) | |
117 | { | |
118 | ar9003_mci_send_message(ah, MCI_SYS_SLEEPING, | |
119 | MCI_FLAG_DISABLE_TIMESTAMP, | |
120 | NULL, 0, wait_done, false); | |
121 | } | |
122 | ||
123 | static void ar9003_mci_send_coex_version_query(struct ath_hw *ah, | |
124 | bool wait_done) | |
125 | { | |
bbefb871 MSS |
126 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
127 | u32 payload[4] = {0, 0, 0, 0}; | |
128 | ||
4f6bd1a8 RM |
129 | if (mci->bt_version_known || |
130 | (mci->bt_state == MCI_BT_SLEEP)) | |
131 | return; | |
132 | ||
133 | MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, | |
134 | MCI_GPM_COEX_VERSION_QUERY); | |
135 | ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); | |
bbefb871 MSS |
136 | } |
137 | ||
138 | static void ar9003_mci_send_coex_version_response(struct ath_hw *ah, | |
37cd9d78 | 139 | bool wait_done) |
bbefb871 | 140 | { |
bbefb871 MSS |
141 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
142 | u32 payload[4] = {0, 0, 0, 0}; | |
143 | ||
bbefb871 | 144 | MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, |
37cd9d78 | 145 | MCI_GPM_COEX_VERSION_RESPONSE); |
bbefb871 MSS |
146 | *(((u8 *)payload) + MCI_GPM_COEX_B_MAJOR_VERSION) = |
147 | mci->wlan_ver_major; | |
148 | *(((u8 *)payload) + MCI_GPM_COEX_B_MINOR_VERSION) = | |
149 | mci->wlan_ver_minor; | |
150 | ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); | |
151 | } | |
152 | ||
153 | static void ar9003_mci_send_coex_wlan_channels(struct ath_hw *ah, | |
37cd9d78 | 154 | bool wait_done) |
bbefb871 MSS |
155 | { |
156 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
157 | u32 *payload = &mci->wlan_channels[0]; | |
158 | ||
4f6bd1a8 RM |
159 | if (!mci->wlan_channels_update || |
160 | (mci->bt_state == MCI_BT_SLEEP)) | |
161 | return; | |
162 | ||
163 | MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, | |
164 | MCI_GPM_COEX_WLAN_CHANNELS); | |
165 | ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); | |
166 | MCI_GPM_SET_TYPE_OPCODE(payload, 0xff, 0xff); | |
bbefb871 MSS |
167 | } |
168 | ||
169 | static void ar9003_mci_send_coex_bt_status_query(struct ath_hw *ah, | |
170 | bool wait_done, u8 query_type) | |
171 | { | |
bbefb871 MSS |
172 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
173 | u32 payload[4] = {0, 0, 0, 0}; | |
4f6bd1a8 | 174 | bool query_btinfo; |
bbefb871 | 175 | |
4f6bd1a8 RM |
176 | if (mci->bt_state == MCI_BT_SLEEP) |
177 | return; | |
bbefb871 | 178 | |
4f6bd1a8 RM |
179 | query_btinfo = !!(query_type & (MCI_GPM_COEX_QUERY_BT_ALL_INFO | |
180 | MCI_GPM_COEX_QUERY_BT_TOPOLOGY)); | |
181 | MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, | |
182 | MCI_GPM_COEX_STATUS_QUERY); | |
37cd9d78 | 183 | |
4f6bd1a8 | 184 | *(((u8 *)payload) + MCI_GPM_COEX_B_BT_BITMAP) = query_type; |
bbefb871 | 185 | |
4f6bd1a8 RM |
186 | /* |
187 | * If bt_status_query message is not sent successfully, | |
188 | * then need_flush_btinfo should be set again. | |
189 | */ | |
190 | if (!ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, | |
191 | wait_done, true)) { | |
bbefb871 | 192 | if (query_btinfo) |
4f6bd1a8 | 193 | mci->need_flush_btinfo = true; |
bbefb871 | 194 | } |
4f6bd1a8 RM |
195 | |
196 | if (query_btinfo) | |
197 | mci->query_bt = false; | |
bbefb871 MSS |
198 | } |
199 | ||
a3f846f1 SM |
200 | static void ar9003_mci_send_coex_halt_bt_gpm(struct ath_hw *ah, bool halt, |
201 | bool wait_done) | |
bbefb871 | 202 | { |
bbefb871 MSS |
203 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
204 | u32 payload[4] = {0, 0, 0, 0}; | |
205 | ||
37cd9d78 SM |
206 | MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, |
207 | MCI_GPM_COEX_HALT_BT_GPM); | |
bbefb871 MSS |
208 | |
209 | if (halt) { | |
210 | mci->query_bt = true; | |
211 | /* Send next unhalt no matter halt sent or not */ | |
212 | mci->unhalt_bt_gpm = true; | |
213 | mci->need_flush_btinfo = true; | |
214 | *(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) = | |
215 | MCI_GPM_COEX_BT_GPM_HALT; | |
216 | } else | |
217 | *(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) = | |
218 | MCI_GPM_COEX_BT_GPM_UNHALT; | |
219 | ||
220 | ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); | |
221 | } | |
222 | ||
bbefb871 MSS |
223 | static void ar9003_mci_prep_interface(struct ath_hw *ah) |
224 | { | |
225 | struct ath_common *common = ath9k_hw_common(ah); | |
226 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
227 | u32 saved_mci_int_en; | |
228 | u32 mci_timeout = 150; | |
229 | ||
230 | mci->bt_state = MCI_BT_SLEEP; | |
231 | saved_mci_int_en = REG_READ(ah, AR_MCI_INTERRUPT_EN); | |
232 | ||
233 | REG_WRITE(ah, AR_MCI_INTERRUPT_EN, 0); | |
234 | REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, | |
235 | REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_RAW)); | |
236 | REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, | |
237 | REG_READ(ah, AR_MCI_INTERRUPT_RAW)); | |
238 | ||
bbefb871 | 239 | ar9003_mci_remote_reset(ah, true); |
bbefb871 MSS |
240 | ar9003_mci_send_req_wake(ah, true); |
241 | ||
4f6bd1a8 RM |
242 | if (!ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, |
243 | AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING, 500)) | |
244 | goto clear_redunt; | |
bbefb871 | 245 | |
4f6bd1a8 | 246 | mci->bt_state = MCI_BT_AWAKE; |
bbefb871 | 247 | |
4f6bd1a8 RM |
248 | /* |
249 | * we don't need to send more remote_reset at this moment. | |
250 | * If BT receive first remote_reset, then BT HW will | |
251 | * be cleaned up and will be able to receive req_wake | |
252 | * and BT HW will respond sys_waking. | |
253 | * In this case, WLAN will receive BT's HW sys_waking. | |
254 | * Otherwise, if BT SW missed initial remote_reset, | |
255 | * that remote_reset will still clean up BT MCI RX, | |
256 | * and the req_wake will wake BT up, | |
257 | * and BT SW will respond this req_wake with a remote_reset and | |
258 | * sys_waking. In this case, WLAN will receive BT's SW | |
259 | * sys_waking. In either case, BT's RX is cleaned up. So we | |
260 | * don't need to reply BT's remote_reset now, if any. | |
261 | * Similarly, if in any case, WLAN can receive BT's sys_waking, | |
262 | * that means WLAN's RX is also fine. | |
263 | */ | |
264 | ar9003_mci_send_sys_waking(ah, true); | |
265 | udelay(10); | |
bbefb871 | 266 | |
4f6bd1a8 RM |
267 | /* |
268 | * Set BT priority interrupt value to be 0xff to | |
269 | * avoid having too many BT PRIORITY interrupts. | |
270 | */ | |
271 | REG_WRITE(ah, AR_MCI_BT_PRI0, 0xFFFFFFFF); | |
272 | REG_WRITE(ah, AR_MCI_BT_PRI1, 0xFFFFFFFF); | |
273 | REG_WRITE(ah, AR_MCI_BT_PRI2, 0xFFFFFFFF); | |
274 | REG_WRITE(ah, AR_MCI_BT_PRI3, 0xFFFFFFFF); | |
275 | REG_WRITE(ah, AR_MCI_BT_PRI, 0X000000FF); | |
bbefb871 | 276 | |
4f6bd1a8 RM |
277 | /* |
278 | * A contention reset will be received after send out | |
279 | * sys_waking. Also BT priority interrupt bits will be set. | |
280 | * Clear those bits before the next step. | |
281 | */ | |
bbefb871 | 282 | |
4f6bd1a8 RM |
283 | REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, |
284 | AR_MCI_INTERRUPT_RX_MSG_CONT_RST); | |
285 | REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_BT_PRI); | |
bbefb871 | 286 | |
4f6bd1a8 RM |
287 | if (mci->is_2g) { |
288 | ar9003_mci_send_lna_transfer(ah, true); | |
289 | udelay(5); | |
290 | } | |
bbefb871 | 291 | |
4f6bd1a8 RM |
292 | if ((mci->is_2g && !mci->update_2g5g)) { |
293 | if (ar9003_mci_wait_for_interrupt(ah, | |
294 | AR_MCI_INTERRUPT_RX_MSG_RAW, | |
295 | AR_MCI_INTERRUPT_RX_MSG_LNA_INFO, | |
296 | mci_timeout)) | |
297 | ath_dbg(common, MCI, | |
298 | "MCI WLAN has control over the LNA & BT obeys it\n"); | |
299 | else | |
300 | ath_dbg(common, MCI, | |
301 | "MCI BT didn't respond to LNA_TRANS\n"); | |
bbefb871 MSS |
302 | } |
303 | ||
4f6bd1a8 | 304 | clear_redunt: |
bbefb871 MSS |
305 | /* Clear the extra redundant SYS_WAKING from BT */ |
306 | if ((mci->bt_state == MCI_BT_AWAKE) && | |
4f6bd1a8 RM |
307 | (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, |
308 | AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) && | |
37cd9d78 SM |
309 | (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, |
310 | AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) == 0)) { | |
311 | REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, | |
312 | AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING); | |
313 | REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, | |
314 | AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE); | |
bbefb871 MSS |
315 | } |
316 | ||
317 | REG_WRITE(ah, AR_MCI_INTERRUPT_EN, saved_mci_int_en); | |
318 | } | |
319 | ||
d1ca8b8e SM |
320 | void ar9003_mci_set_full_sleep(struct ath_hw *ah) |
321 | { | |
d1ca8b8e SM |
322 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
323 | ||
b98ccec0 | 324 | if (ar9003_mci_state(ah, MCI_STATE_ENABLE) && |
d1ca8b8e SM |
325 | (mci->bt_state != MCI_BT_SLEEP) && |
326 | !mci->halted_bt_gpm) { | |
d1ca8b8e SM |
327 | ar9003_mci_send_coex_halt_bt_gpm(ah, true, true); |
328 | } | |
329 | ||
330 | mci->ready = false; | |
d1ca8b8e SM |
331 | } |
332 | ||
a3f846f1 | 333 | static void ar9003_mci_disable_interrupt(struct ath_hw *ah) |
bbefb871 MSS |
334 | { |
335 | REG_WRITE(ah, AR_MCI_INTERRUPT_EN, 0); | |
336 | REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0); | |
337 | } | |
338 | ||
a3f846f1 | 339 | static void ar9003_mci_enable_interrupt(struct ath_hw *ah) |
bbefb871 | 340 | { |
bbefb871 MSS |
341 | REG_WRITE(ah, AR_MCI_INTERRUPT_EN, AR_MCI_INTERRUPT_DEFAULT); |
342 | REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, | |
343 | AR_MCI_INTERRUPT_RX_MSG_DEFAULT); | |
344 | } | |
345 | ||
a3f846f1 | 346 | static bool ar9003_mci_check_int(struct ath_hw *ah, u32 ints) |
bbefb871 MSS |
347 | { |
348 | u32 intr; | |
349 | ||
350 | intr = REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_RAW); | |
351 | return ((intr & ints) == ints); | |
352 | } | |
353 | ||
354 | void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr, | |
355 | u32 *rx_msg_intr) | |
356 | { | |
357 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
8a309305 | 358 | |
bbefb871 MSS |
359 | *raw_intr = mci->raw_intr; |
360 | *rx_msg_intr = mci->rx_msg_intr; | |
361 | ||
362 | /* Clean int bits after the values are read. */ | |
363 | mci->raw_intr = 0; | |
364 | mci->rx_msg_intr = 0; | |
365 | } | |
366 | EXPORT_SYMBOL(ar9003_mci_get_interrupt); | |
367 | ||
5a1e2735 SM |
368 | void ar9003_mci_get_isr(struct ath_hw *ah, enum ath9k_int *masked) |
369 | { | |
370 | struct ath_common *common = ath9k_hw_common(ah); | |
371 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
372 | u32 raw_intr, rx_msg_intr; | |
373 | ||
374 | rx_msg_intr = REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_RAW); | |
375 | raw_intr = REG_READ(ah, AR_MCI_INTERRUPT_RAW); | |
376 | ||
377 | if ((raw_intr == 0xdeadbeef) || (rx_msg_intr == 0xdeadbeef)) { | |
378 | ath_dbg(common, MCI, | |
379 | "MCI gets 0xdeadbeef during int processing\n"); | |
380 | } else { | |
381 | mci->rx_msg_intr |= rx_msg_intr; | |
382 | mci->raw_intr |= raw_intr; | |
383 | *masked |= ATH9K_INT_MCI; | |
384 | ||
385 | if (rx_msg_intr & AR_MCI_INTERRUPT_RX_MSG_CONT_INFO) | |
386 | mci->cont_status = REG_READ(ah, AR_MCI_CONT_STATUS); | |
387 | ||
388 | REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, rx_msg_intr); | |
389 | REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, raw_intr); | |
390 | } | |
391 | } | |
392 | ||
a3f846f1 | 393 | static void ar9003_mci_2g5g_changed(struct ath_hw *ah, bool is_2g) |
bbefb871 MSS |
394 | { |
395 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
396 | ||
397 | if (!mci->update_2g5g && | |
398 | (mci->is_2g != is_2g)) | |
399 | mci->update_2g5g = true; | |
400 | ||
401 | mci->is_2g = is_2g; | |
402 | } | |
403 | ||
404 | static bool ar9003_mci_is_gpm_valid(struct ath_hw *ah, u32 msg_index) | |
405 | { | |
bbefb871 MSS |
406 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
407 | u32 *payload; | |
408 | u32 recv_type, offset; | |
409 | ||
410 | if (msg_index == MCI_GPM_INVALID) | |
411 | return false; | |
412 | ||
413 | offset = msg_index << 4; | |
414 | ||
415 | payload = (u32 *)(mci->gpm_buf + offset); | |
416 | recv_type = MCI_GPM_TYPE(payload); | |
417 | ||
37cd9d78 | 418 | if (recv_type == MCI_GPM_RSVD_PATTERN) |
bbefb871 | 419 | return false; |
bbefb871 MSS |
420 | |
421 | return true; | |
422 | } | |
423 | ||
424 | static void ar9003_mci_observation_set_up(struct ath_hw *ah) | |
425 | { | |
426 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
bbefb871 | 427 | |
37cd9d78 SM |
428 | if (mci->config & ATH_MCI_CONFIG_MCI_OBS_MCI) { |
429 | ath9k_hw_cfg_output(ah, 3, AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_DATA); | |
bbefb871 MSS |
430 | ath9k_hw_cfg_output(ah, 2, AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK); |
431 | ath9k_hw_cfg_output(ah, 1, AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA); | |
432 | ath9k_hw_cfg_output(ah, 0, AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK); | |
bbefb871 | 433 | } else if (mci->config & ATH_MCI_CONFIG_MCI_OBS_TXRX) { |
bbefb871 MSS |
434 | ath9k_hw_cfg_output(ah, 3, AR_GPIO_OUTPUT_MUX_AS_WL_IN_TX); |
435 | ath9k_hw_cfg_output(ah, 2, AR_GPIO_OUTPUT_MUX_AS_WL_IN_RX); | |
436 | ath9k_hw_cfg_output(ah, 1, AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX); | |
437 | ath9k_hw_cfg_output(ah, 0, AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX); | |
438 | ath9k_hw_cfg_output(ah, 5, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | |
bbefb871 | 439 | } else if (mci->config & ATH_MCI_CONFIG_MCI_OBS_BT) { |
bbefb871 MSS |
440 | ath9k_hw_cfg_output(ah, 3, AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX); |
441 | ath9k_hw_cfg_output(ah, 2, AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX); | |
442 | ath9k_hw_cfg_output(ah, 1, AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA); | |
443 | ath9k_hw_cfg_output(ah, 0, AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK); | |
bbefb871 MSS |
444 | } else |
445 | return; | |
446 | ||
447 | REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); | |
448 | ||
0cc4cdeb SM |
449 | REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL, AR_GLB_DS_JTAG_DISABLE, 1); |
450 | REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL, AR_GLB_WLAN_UART_INTF_EN, 0); | |
451 | REG_SET_BIT(ah, AR_GLB_GPIO_CONTROL, ATH_MCI_CONFIG_MCI_OBS_GPIO); | |
bbefb871 MSS |
452 | |
453 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_GPIO_OBS_SEL, 0); | |
454 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_MAC_BB_OBS_SEL, 1); | |
455 | REG_WRITE(ah, AR_OBS, 0x4b); | |
456 | REG_RMW_FIELD(ah, AR_DIAG_SW, AR_DIAG_OBS_PT_SEL1, 0x03); | |
457 | REG_RMW_FIELD(ah, AR_DIAG_SW, AR_DIAG_OBS_PT_SEL2, 0x01); | |
458 | REG_RMW_FIELD(ah, AR_MACMISC, AR_MACMISC_MISC_OBS_BUS_LSB, 0x02); | |
459 | REG_RMW_FIELD(ah, AR_MACMISC, AR_MACMISC_MISC_OBS_BUS_MSB, 0x03); | |
460 | REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS, | |
461 | AR_PHY_TEST_CTL_DEBUGPORT_SEL, 0x07); | |
462 | } | |
463 | ||
464 | static bool ar9003_mci_send_coex_bt_flags(struct ath_hw *ah, bool wait_done, | |
37cd9d78 | 465 | u8 opcode, u32 bt_flags) |
bbefb871 | 466 | { |
bbefb871 MSS |
467 | u32 pld[4] = {0, 0, 0, 0}; |
468 | ||
37cd9d78 SM |
469 | MCI_GPM_SET_TYPE_OPCODE(pld, MCI_GPM_COEX_AGENT, |
470 | MCI_GPM_COEX_BT_UPDATE_FLAGS); | |
bbefb871 MSS |
471 | |
472 | *(((u8 *)pld) + MCI_GPM_COEX_B_BT_FLAGS_OP) = opcode; | |
473 | *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 0) = bt_flags & 0xFF; | |
474 | *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 1) = (bt_flags >> 8) & 0xFF; | |
475 | *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 2) = (bt_flags >> 16) & 0xFF; | |
476 | *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 3) = (bt_flags >> 24) & 0xFF; | |
477 | ||
bbefb871 | 478 | return ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, |
37cd9d78 | 479 | wait_done, true); |
bbefb871 MSS |
480 | } |
481 | ||
a3f846f1 SM |
482 | static void ar9003_mci_sync_bt_state(struct ath_hw *ah) |
483 | { | |
a3f846f1 SM |
484 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
485 | u32 cur_bt_state; | |
486 | ||
b98ccec0 | 487 | cur_bt_state = ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP); |
a3f846f1 | 488 | |
37cd9d78 | 489 | if (mci->bt_state != cur_bt_state) |
a3f846f1 | 490 | mci->bt_state = cur_bt_state; |
a3f846f1 SM |
491 | |
492 | if (mci->bt_state != MCI_BT_SLEEP) { | |
493 | ||
494 | ar9003_mci_send_coex_version_query(ah, true); | |
495 | ar9003_mci_send_coex_wlan_channels(ah, true); | |
496 | ||
37cd9d78 | 497 | if (mci->unhalt_bt_gpm == true) |
a3f846f1 | 498 | ar9003_mci_send_coex_halt_bt_gpm(ah, false, true); |
a3f846f1 SM |
499 | } |
500 | } | |
501 | ||
528e5d36 SM |
502 | void ar9003_mci_check_bt(struct ath_hw *ah) |
503 | { | |
504 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; | |
505 | ||
506 | if (!mci_hw->ready) | |
507 | return; | |
508 | ||
509 | /* | |
510 | * check BT state again to make | |
511 | * sure it's not changed. | |
512 | */ | |
513 | ar9003_mci_sync_bt_state(ah); | |
514 | ar9003_mci_2g5g_switch(ah, true); | |
515 | ||
516 | if ((mci_hw->bt_state == MCI_BT_AWAKE) && | |
517 | (mci_hw->query_bt == true)) { | |
518 | mci_hw->need_flush_btinfo = true; | |
519 | } | |
520 | } | |
521 | ||
a3f846f1 SM |
522 | static void ar9003_mci_process_gpm_extra(struct ath_hw *ah, u8 gpm_type, |
523 | u8 gpm_opcode, u32 *p_gpm) | |
524 | { | |
525 | struct ath_common *common = ath9k_hw_common(ah); | |
526 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
527 | u8 *p_data = (u8 *) p_gpm; | |
528 | ||
529 | if (gpm_type != MCI_GPM_COEX_AGENT) | |
530 | return; | |
531 | ||
532 | switch (gpm_opcode) { | |
533 | case MCI_GPM_COEX_VERSION_QUERY: | |
534 | ath_dbg(common, MCI, "MCI Recv GPM COEX Version Query\n"); | |
535 | ar9003_mci_send_coex_version_response(ah, true); | |
536 | break; | |
537 | case MCI_GPM_COEX_VERSION_RESPONSE: | |
538 | ath_dbg(common, MCI, "MCI Recv GPM COEX Version Response\n"); | |
539 | mci->bt_ver_major = | |
540 | *(p_data + MCI_GPM_COEX_B_MAJOR_VERSION); | |
541 | mci->bt_ver_minor = | |
542 | *(p_data + MCI_GPM_COEX_B_MINOR_VERSION); | |
543 | mci->bt_version_known = true; | |
544 | ath_dbg(common, MCI, "MCI BT Coex version: %d.%d\n", | |
545 | mci->bt_ver_major, mci->bt_ver_minor); | |
546 | break; | |
547 | case MCI_GPM_COEX_STATUS_QUERY: | |
548 | ath_dbg(common, MCI, | |
549 | "MCI Recv GPM COEX Status Query = 0x%02X\n", | |
550 | *(p_data + MCI_GPM_COEX_B_WLAN_BITMAP)); | |
551 | mci->wlan_channels_update = true; | |
552 | ar9003_mci_send_coex_wlan_channels(ah, true); | |
553 | break; | |
554 | case MCI_GPM_COEX_BT_PROFILE_INFO: | |
555 | mci->query_bt = true; | |
556 | ath_dbg(common, MCI, "MCI Recv GPM COEX BT_Profile_Info\n"); | |
557 | break; | |
558 | case MCI_GPM_COEX_BT_STATUS_UPDATE: | |
559 | mci->query_bt = true; | |
560 | ath_dbg(common, MCI, | |
561 | "MCI Recv GPM COEX BT_Status_Update SEQ=%d (drop&query)\n", | |
562 | *(p_gpm + 3)); | |
563 | break; | |
564 | default: | |
565 | break; | |
566 | } | |
567 | } | |
568 | ||
569 | static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type, | |
570 | u8 gpm_opcode, int time_out) | |
571 | { | |
572 | struct ath_common *common = ath9k_hw_common(ah); | |
573 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
574 | u32 *p_gpm = NULL, mismatch = 0, more_data; | |
575 | u32 offset; | |
576 | u8 recv_type = 0, recv_opcode = 0; | |
577 | bool b_is_bt_cal_done = (gpm_type == MCI_GPM_BT_CAL_DONE); | |
578 | ||
a3f846f1 SM |
579 | more_data = time_out ? MCI_GPM_NOMORE : MCI_GPM_MORE; |
580 | ||
581 | while (time_out > 0) { | |
582 | if (p_gpm) { | |
583 | MCI_GPM_RECYCLE(p_gpm); | |
584 | p_gpm = NULL; | |
585 | } | |
586 | ||
587 | if (more_data != MCI_GPM_MORE) | |
588 | time_out = ar9003_mci_wait_for_interrupt(ah, | |
589 | AR_MCI_INTERRUPT_RX_MSG_RAW, | |
590 | AR_MCI_INTERRUPT_RX_MSG_GPM, | |
591 | time_out); | |
592 | ||
593 | if (!time_out) | |
594 | break; | |
595 | ||
506847ad | 596 | offset = ar9003_mci_get_next_gpm_offset(ah, false, &more_data); |
a3f846f1 SM |
597 | |
598 | if (offset == MCI_GPM_INVALID) | |
599 | continue; | |
600 | ||
601 | p_gpm = (u32 *) (mci->gpm_buf + offset); | |
602 | recv_type = MCI_GPM_TYPE(p_gpm); | |
603 | recv_opcode = MCI_GPM_OPCODE(p_gpm); | |
604 | ||
605 | if (MCI_GPM_IS_CAL_TYPE(recv_type)) { | |
a3f846f1 | 606 | if (recv_type == gpm_type) { |
a3f846f1 SM |
607 | if ((gpm_type == MCI_GPM_BT_CAL_DONE) && |
608 | !b_is_bt_cal_done) { | |
609 | gpm_type = MCI_GPM_BT_CAL_GRANT; | |
a3f846f1 SM |
610 | continue; |
611 | } | |
a3f846f1 SM |
612 | break; |
613 | } | |
4f6bd1a8 RM |
614 | } else if ((recv_type == gpm_type) && |
615 | (recv_opcode == gpm_opcode)) | |
a3f846f1 | 616 | break; |
a3f846f1 SM |
617 | |
618 | /* | |
619 | * check if it's cal_grant | |
620 | * | |
621 | * When we're waiting for cal_grant in reset routine, | |
622 | * it's possible that BT sends out cal_request at the | |
623 | * same time. Since BT's calibration doesn't happen | |
624 | * that often, we'll let BT completes calibration then | |
625 | * we continue to wait for cal_grant from BT. | |
626 | * Orginal: Wait BT_CAL_GRANT. | |
627 | * New: Receive BT_CAL_REQ -> send WLAN_CAL_GRANT->wait | |
628 | * BT_CAL_DONE -> Wait BT_CAL_GRANT. | |
629 | */ | |
630 | ||
631 | if ((gpm_type == MCI_GPM_BT_CAL_GRANT) && | |
632 | (recv_type == MCI_GPM_BT_CAL_REQ)) { | |
633 | ||
634 | u32 payload[4] = {0, 0, 0, 0}; | |
635 | ||
636 | gpm_type = MCI_GPM_BT_CAL_DONE; | |
a3f846f1 | 637 | MCI_GPM_SET_CAL_TYPE(payload, |
37cd9d78 | 638 | MCI_GPM_WLAN_CAL_GRANT); |
a3f846f1 SM |
639 | ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, |
640 | false, false); | |
a3f846f1 SM |
641 | continue; |
642 | } else { | |
643 | ath_dbg(common, MCI, "MCI GPM subtype not match 0x%x\n", | |
644 | *(p_gpm + 1)); | |
645 | mismatch++; | |
646 | ar9003_mci_process_gpm_extra(ah, recv_type, | |
37cd9d78 | 647 | recv_opcode, p_gpm); |
a3f846f1 SM |
648 | } |
649 | } | |
37cd9d78 | 650 | |
a3f846f1 SM |
651 | if (p_gpm) { |
652 | MCI_GPM_RECYCLE(p_gpm); | |
653 | p_gpm = NULL; | |
654 | } | |
655 | ||
37cd9d78 | 656 | if (time_out <= 0) |
a3f846f1 | 657 | time_out = 0; |
a3f846f1 SM |
658 | |
659 | while (more_data == MCI_GPM_MORE) { | |
506847ad | 660 | offset = ar9003_mci_get_next_gpm_offset(ah, false, &more_data); |
a3f846f1 SM |
661 | if (offset == MCI_GPM_INVALID) |
662 | break; | |
663 | ||
664 | p_gpm = (u32 *) (mci->gpm_buf + offset); | |
665 | recv_type = MCI_GPM_TYPE(p_gpm); | |
666 | recv_opcode = MCI_GPM_OPCODE(p_gpm); | |
667 | ||
668 | if (!MCI_GPM_IS_CAL_TYPE(recv_type)) | |
669 | ar9003_mci_process_gpm_extra(ah, recv_type, | |
670 | recv_opcode, p_gpm); | |
671 | ||
672 | MCI_GPM_RECYCLE(p_gpm); | |
673 | } | |
674 | ||
675 | return time_out; | |
676 | } | |
677 | ||
528e5d36 SM |
678 | bool ar9003_mci_start_reset(struct ath_hw *ah, struct ath9k_channel *chan) |
679 | { | |
680 | struct ath_common *common = ath9k_hw_common(ah); | |
681 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; | |
682 | u32 payload[4] = {0, 0, 0, 0}; | |
683 | ||
684 | ar9003_mci_2g5g_changed(ah, IS_CHAN_2GHZ(chan)); | |
685 | ||
686 | if (mci_hw->bt_state != MCI_BT_CAL_START) | |
687 | return false; | |
688 | ||
528e5d36 SM |
689 | mci_hw->bt_state = MCI_BT_CAL; |
690 | ||
691 | /* | |
692 | * MCI FIX: disable mci interrupt here. This is to avoid | |
693 | * SW_MSG_DONE or RX_MSG bits to trigger MCI_INT and | |
694 | * lead to mci_intr reentry. | |
695 | */ | |
528e5d36 SM |
696 | ar9003_mci_disable_interrupt(ah); |
697 | ||
528e5d36 SM |
698 | MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_GRANT); |
699 | ar9003_mci_send_message(ah, MCI_GPM, 0, payload, | |
700 | 16, true, false); | |
701 | ||
528e5d36 SM |
702 | /* Wait BT calibration to be completed for 25ms */ |
703 | ||
704 | if (ar9003_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_DONE, | |
705 | 0, 25000)) | |
37cd9d78 | 706 | ath_dbg(common, MCI, "MCI BT_CAL_DONE received\n"); |
528e5d36 SM |
707 | else |
708 | ath_dbg(common, MCI, | |
37cd9d78 | 709 | "MCI BT_CAL_DONE not received\n"); |
528e5d36 SM |
710 | |
711 | mci_hw->bt_state = MCI_BT_AWAKE; | |
712 | /* MCI FIX: enable mci interrupt here */ | |
713 | ar9003_mci_enable_interrupt(ah); | |
714 | ||
715 | return true; | |
716 | } | |
717 | ||
718 | int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, | |
719 | struct ath9k_hw_cal_data *caldata) | |
720 | { | |
528e5d36 SM |
721 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; |
722 | ||
723 | if (!mci_hw->ready) | |
724 | return 0; | |
725 | ||
726 | if (!IS_CHAN_2GHZ(chan) || (mci_hw->bt_state != MCI_BT_SLEEP)) | |
727 | goto exit; | |
728 | ||
4f6bd1a8 RM |
729 | if (!ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET) && |
730 | !ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)) | |
731 | goto exit; | |
528e5d36 | 732 | |
4f6bd1a8 RM |
733 | /* |
734 | * BT is sleeping. Check if BT wakes up during | |
735 | * WLAN calibration. If BT wakes up during | |
736 | * WLAN calibration, need to go through all | |
737 | * message exchanges again and recal. | |
738 | */ | |
739 | REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, | |
740 | (AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET | | |
741 | AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)); | |
528e5d36 | 742 | |
4f6bd1a8 RM |
743 | ar9003_mci_remote_reset(ah, true); |
744 | ar9003_mci_send_sys_waking(ah, true); | |
745 | udelay(1); | |
528e5d36 | 746 | |
4f6bd1a8 RM |
747 | if (IS_CHAN_2GHZ(chan)) |
748 | ar9003_mci_send_lna_transfer(ah, true); | |
528e5d36 | 749 | |
4f6bd1a8 | 750 | mci_hw->bt_state = MCI_BT_AWAKE; |
528e5d36 | 751 | |
b55f6bb7 BS |
752 | REG_CLR_BIT(ah, AR_PHY_TIMING4, |
753 | 1 << AR_PHY_TIMING_CONTROL4_DO_GAIN_DC_IQ_CAL_SHIFT); | |
754 | ||
4f6bd1a8 RM |
755 | if (caldata) { |
756 | caldata->done_txiqcal_once = false; | |
757 | caldata->done_txclcal_once = false; | |
758 | caldata->rtt_done = false; | |
759 | } | |
528e5d36 | 760 | |
4f6bd1a8 RM |
761 | if (!ath9k_hw_init_cal(ah, chan)) |
762 | return -EIO; | |
528e5d36 | 763 | |
b55f6bb7 BS |
764 | REG_SET_BIT(ah, AR_PHY_TIMING4, |
765 | 1 << AR_PHY_TIMING_CONTROL4_DO_GAIN_DC_IQ_CAL_SHIFT); | |
766 | ||
528e5d36 SM |
767 | exit: |
768 | ar9003_mci_enable_interrupt(ah); | |
769 | return 0; | |
770 | } | |
771 | ||
a3f846f1 SM |
772 | static void ar9003_mci_mute_bt(struct ath_hw *ah) |
773 | { | |
a3f846f1 SM |
774 | /* disable all MCI messages */ |
775 | REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE, 0xffff0000); | |
a3f846f1 SM |
776 | REG_SET_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); |
777 | ||
778 | /* wait pending HW messages to flush out */ | |
779 | udelay(10); | |
780 | ||
781 | /* | |
782 | * Send LNA_TAKE and SYS_SLEEPING when | |
783 | * 1. reset not after resuming from full sleep | |
784 | * 2. before reset MCI RX, to quiet BT and avoid MCI RX misalignment | |
785 | */ | |
a3f846f1 SM |
786 | ar9003_mci_send_lna_take(ah, true); |
787 | ||
788 | udelay(5); | |
789 | ||
a3f846f1 SM |
790 | ar9003_mci_send_sys_sleeping(ah, true); |
791 | } | |
792 | ||
4f851df7 SM |
793 | static void ar9003_mci_osla_setup(struct ath_hw *ah, bool enable) |
794 | { | |
795 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
796 | u32 thresh; | |
797 | ||
4f6bd1a8 | 798 | if (!enable) { |
4f851df7 SM |
799 | REG_CLR_BIT(ah, AR_BTCOEX_CTRL, |
800 | AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); | |
4f6bd1a8 | 801 | return; |
4f851df7 | 802 | } |
4f6bd1a8 RM |
803 | REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, AR_MCI_SCHD_TABLE_2_HW_BASED, 1); |
804 | REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, | |
805 | AR_MCI_SCHD_TABLE_2_MEM_BASED, 1); | |
806 | ||
4c6231a4 RM |
807 | if (AR_SREV_9565(ah)) |
808 | REG_RMW_FIELD(ah, AR_MCI_MISC, AR_MCI_MISC_HW_FIX_EN, 1); | |
809 | ||
4f6bd1a8 RM |
810 | if (!(mci->config & ATH_MCI_CONFIG_DISABLE_AGGR_THRESH)) { |
811 | thresh = MS(mci->config, ATH_MCI_CONFIG_AGGR_THRESH); | |
812 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
813 | AR_BTCOEX_CTRL_AGGR_THRESH, thresh); | |
814 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
815 | AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 1); | |
816 | } else | |
817 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
818 | AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 0); | |
819 | ||
820 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
821 | AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN, 1); | |
4f851df7 SM |
822 | } |
823 | ||
69c6ac60 SM |
824 | int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, |
825 | bool is_full_sleep) | |
bbefb871 MSS |
826 | { |
827 | struct ath_common *common = ath9k_hw_common(ah); | |
828 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
e82cb03f | 829 | u32 regval, i; |
bbefb871 | 830 | |
4f851df7 | 831 | ath_dbg(common, MCI, "MCI Reset (full_sleep = %d, is_2g = %d)\n", |
bbefb871 MSS |
832 | is_full_sleep, is_2g); |
833 | ||
bbefb871 | 834 | if (!mci->gpm_addr && !mci->sched_addr) { |
69c6ac60 SM |
835 | ath_err(common, "MCI GPM and schedule buffers are not allocated\n"); |
836 | return -ENOMEM; | |
bbefb871 MSS |
837 | } |
838 | ||
839 | if (REG_READ(ah, AR_BTCOEX_CTRL) == 0xdeadbeef) { | |
69c6ac60 SM |
840 | ath_err(common, "BTCOEX control register is dead\n"); |
841 | return -EINVAL; | |
bbefb871 MSS |
842 | } |
843 | ||
844 | /* Program MCI DMA related registers */ | |
845 | REG_WRITE(ah, AR_MCI_GPM_0, mci->gpm_addr); | |
846 | REG_WRITE(ah, AR_MCI_GPM_1, mci->gpm_len); | |
847 | REG_WRITE(ah, AR_MCI_SCHD_TABLE_0, mci->sched_addr); | |
848 | ||
849 | /* | |
850 | * To avoid MCI state machine be affected by incoming remote MCI msgs, | |
851 | * MCI mode will be enabled later, right before reset the MCI TX and RX. | |
852 | */ | |
853 | ||
854 | regval = SM(1, AR_BTCOEX_CTRL_AR9462_MODE) | | |
855 | SM(1, AR_BTCOEX_CTRL_WBTIMER_EN) | | |
856 | SM(1, AR_BTCOEX_CTRL_PA_SHARED) | | |
857 | SM(1, AR_BTCOEX_CTRL_LNA_SHARED) | | |
bbefb871 MSS |
858 | SM(0, AR_BTCOEX_CTRL_1_CHAIN_ACK) | |
859 | SM(0, AR_BTCOEX_CTRL_1_CHAIN_BCN) | | |
860 | SM(0, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); | |
d9575dad BS |
861 | if (AR_SREV_9565(ah)) { |
862 | regval |= SM(1, AR_BTCOEX_CTRL_NUM_ANTENNAS) | | |
863 | SM(1, AR_BTCOEX_CTRL_RX_CHAIN_MASK); | |
864 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, | |
865 | AR_BTCOEX_CTRL2_TX_CHAIN_MASK, 0x1); | |
866 | } else { | |
867 | regval |= SM(2, AR_BTCOEX_CTRL_NUM_ANTENNAS) | | |
868 | SM(3, AR_BTCOEX_CTRL_RX_CHAIN_MASK); | |
869 | } | |
bbefb871 | 870 | |
bbefb871 MSS |
871 | REG_WRITE(ah, AR_BTCOEX_CTRL, regval); |
872 | ||
4f851df7 SM |
873 | if (is_2g && !(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) |
874 | ar9003_mci_osla_setup(ah, true); | |
875 | else | |
876 | ar9003_mci_osla_setup(ah, false); | |
877 | ||
878 | REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, | |
879 | AR_BTCOEX_CTRL_SPDT_ENABLE); | |
880 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL3, | |
881 | AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT, 20); | |
bbefb871 | 882 | |
e75d4ed6 | 883 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_RX_DEWEIGHT, 0); |
bbefb871 MSS |
884 | REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); |
885 | ||
e75d4ed6 RM |
886 | /* Set the time out to 3.125ms (5 BT slots) */ |
887 | REG_RMW_FIELD(ah, AR_BTCOEX_WL_LNA, AR_BTCOEX_WL_LNA_TIMEOUT, 0x3D090); | |
888 | ||
e82cb03f RM |
889 | /* concurrent tx priority */ |
890 | if (mci->config & ATH_MCI_CONFIG_CONCUR_TX) { | |
891 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, | |
892 | AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE, 0); | |
893 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, | |
894 | AR_BTCOEX_CTRL2_TXPWR_THRESH, 0x7f); | |
895 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
896 | AR_BTCOEX_CTRL_REDUCE_TXPWR, 0); | |
897 | for (i = 0; i < 8; i++) | |
898 | REG_WRITE(ah, AR_BTCOEX_MAX_TXPWR(i), 0x7f7f7f7f); | |
899 | } | |
900 | ||
4f851df7 SM |
901 | regval = MS(mci->config, ATH_MCI_CONFIG_CLK_DIV); |
902 | REG_RMW_FIELD(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_CLK_DIV, regval); | |
bbefb871 MSS |
903 | REG_SET_BIT(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_MCI_MODE_EN); |
904 | ||
905 | /* Resetting the Rx and Tx paths of MCI */ | |
906 | regval = REG_READ(ah, AR_MCI_COMMAND2); | |
907 | regval |= SM(1, AR_MCI_COMMAND2_RESET_TX); | |
908 | REG_WRITE(ah, AR_MCI_COMMAND2, regval); | |
909 | ||
910 | udelay(1); | |
911 | ||
912 | regval &= ~SM(1, AR_MCI_COMMAND2_RESET_TX); | |
913 | REG_WRITE(ah, AR_MCI_COMMAND2, regval); | |
914 | ||
915 | if (is_full_sleep) { | |
916 | ar9003_mci_mute_bt(ah); | |
917 | udelay(100); | |
918 | } | |
919 | ||
3863495b | 920 | /* Check pending GPM msg before MCI Reset Rx */ |
506847ad | 921 | ar9003_mci_check_gpm_offset(ah); |
3863495b | 922 | |
bbefb871 MSS |
923 | regval |= SM(1, AR_MCI_COMMAND2_RESET_RX); |
924 | REG_WRITE(ah, AR_MCI_COMMAND2, regval); | |
925 | udelay(1); | |
926 | regval &= ~SM(1, AR_MCI_COMMAND2_RESET_RX); | |
927 | REG_WRITE(ah, AR_MCI_COMMAND2, regval); | |
928 | ||
506847ad | 929 | ar9003_mci_get_next_gpm_offset(ah, true, NULL); |
4f851df7 | 930 | |
bbefb871 MSS |
931 | REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE, |
932 | (SM(0xe801, AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR) | | |
933 | SM(0x0000, AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM))); | |
934 | ||
935 | REG_CLR_BIT(ah, AR_MCI_TX_CTRL, | |
4f851df7 | 936 | AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); |
bbefb871 | 937 | |
4f851df7 | 938 | ar9003_mci_observation_set_up(ah); |
bbefb871 MSS |
939 | |
940 | mci->ready = true; | |
941 | ar9003_mci_prep_interface(ah); | |
942 | ||
e9f9fd8c RM |
943 | if (AR_SREV_9565(ah)) |
944 | REG_RMW_FIELD(ah, AR_MCI_DBG_CNT_CTRL, | |
945 | AR_MCI_DBG_CNT_CTRL_ENABLE, 0); | |
bbefb871 MSS |
946 | if (en_int) |
947 | ar9003_mci_enable_interrupt(ah); | |
69c6ac60 SM |
948 | |
949 | return 0; | |
bbefb871 MSS |
950 | } |
951 | ||
528e5d36 SM |
952 | void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep) |
953 | { | |
954 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; | |
955 | ||
956 | ar9003_mci_disable_interrupt(ah); | |
957 | ||
958 | if (mci_hw->ready && !save_fullsleep) { | |
959 | ar9003_mci_mute_bt(ah); | |
960 | udelay(20); | |
961 | REG_WRITE(ah, AR_BTCOEX_CTRL, 0); | |
962 | } | |
963 | ||
964 | mci_hw->bt_state = MCI_BT_SLEEP; | |
965 | mci_hw->ready = false; | |
966 | } | |
967 | ||
bbefb871 MSS |
968 | static void ar9003_mci_send_2g5g_status(struct ath_hw *ah, bool wait_done) |
969 | { | |
bbefb871 MSS |
970 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
971 | u32 new_flags, to_set, to_clear; | |
972 | ||
4f6bd1a8 RM |
973 | if (!mci->update_2g5g || (mci->bt_state == MCI_BT_SLEEP)) |
974 | return; | |
bbefb871 | 975 | |
4f6bd1a8 RM |
976 | if (mci->is_2g) { |
977 | new_flags = MCI_2G_FLAGS; | |
978 | to_clear = MCI_2G_FLAGS_CLEAR_MASK; | |
979 | to_set = MCI_2G_FLAGS_SET_MASK; | |
980 | } else { | |
981 | new_flags = MCI_5G_FLAGS; | |
982 | to_clear = MCI_5G_FLAGS_CLEAR_MASK; | |
983 | to_set = MCI_5G_FLAGS_SET_MASK; | |
984 | } | |
985 | ||
986 | if (to_clear) | |
987 | ar9003_mci_send_coex_bt_flags(ah, wait_done, | |
37cd9d78 SM |
988 | MCI_GPM_COEX_BT_FLAGS_CLEAR, |
989 | to_clear); | |
4f6bd1a8 RM |
990 | if (to_set) |
991 | ar9003_mci_send_coex_bt_flags(ah, wait_done, | |
37cd9d78 SM |
992 | MCI_GPM_COEX_BT_FLAGS_SET, |
993 | to_set); | |
bbefb871 MSS |
994 | } |
995 | ||
996 | static void ar9003_mci_queue_unsent_gpm(struct ath_hw *ah, u8 header, | |
997 | u32 *payload, bool queue) | |
998 | { | |
bbefb871 MSS |
999 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
1000 | u8 type, opcode; | |
1001 | ||
bbefb871 MSS |
1002 | /* check if the message is to be queued */ |
1003 | if (header != MCI_GPM) | |
1004 | return; | |
1005 | ||
1006 | type = MCI_GPM_TYPE(payload); | |
1007 | opcode = MCI_GPM_OPCODE(payload); | |
1008 | ||
1009 | if (type != MCI_GPM_COEX_AGENT) | |
1010 | return; | |
1011 | ||
1012 | switch (opcode) { | |
1013 | case MCI_GPM_COEX_BT_UPDATE_FLAGS: | |
bbefb871 | 1014 | if (*(((u8 *)payload) + MCI_GPM_COEX_B_BT_FLAGS_OP) == |
c91ec465 | 1015 | MCI_GPM_COEX_BT_FLAGS_READ) |
bbefb871 MSS |
1016 | break; |
1017 | ||
1018 | mci->update_2g5g = queue; | |
1019 | ||
bbefb871 | 1020 | break; |
bbefb871 | 1021 | case MCI_GPM_COEX_WLAN_CHANNELS: |
bbefb871 | 1022 | mci->wlan_channels_update = queue; |
bbefb871 | 1023 | break; |
bbefb871 | 1024 | case MCI_GPM_COEX_HALT_BT_GPM: |
bbefb871 | 1025 | if (*(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) == |
37cd9d78 | 1026 | MCI_GPM_COEX_BT_GPM_UNHALT) { |
bbefb871 MSS |
1027 | mci->unhalt_bt_gpm = queue; |
1028 | ||
37cd9d78 | 1029 | if (!queue) |
bbefb871 | 1030 | mci->halted_bt_gpm = false; |
bbefb871 MSS |
1031 | } |
1032 | ||
1033 | if (*(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) == | |
1034 | MCI_GPM_COEX_BT_GPM_HALT) { | |
1035 | ||
1036 | mci->halted_bt_gpm = !queue; | |
bbefb871 MSS |
1037 | } |
1038 | ||
1039 | break; | |
1040 | default: | |
1041 | break; | |
1042 | } | |
1043 | } | |
1044 | ||
1bde95fa | 1045 | void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool force) |
bbefb871 | 1046 | { |
bbefb871 MSS |
1047 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
1048 | ||
1bde95fa | 1049 | if (!mci->update_2g5g && !force) |
4f6bd1a8 | 1050 | return; |
bbefb871 | 1051 | |
4f6bd1a8 | 1052 | if (mci->is_2g) { |
83bfea42 RM |
1053 | ar9003_mci_send_2g5g_status(ah, true); |
1054 | ar9003_mci_send_lna_transfer(ah, true); | |
1055 | udelay(5); | |
4ff6a9d2 RM |
1056 | |
1057 | REG_CLR_BIT(ah, AR_MCI_TX_CTRL, | |
4f6bd1a8 RM |
1058 | AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); |
1059 | REG_CLR_BIT(ah, AR_PHY_GLB_CONTROL, | |
1060 | AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); | |
1061 | ||
1062 | if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) | |
1bde95fa | 1063 | ar9003_mci_osla_setup(ah, true); |
7d47884f RM |
1064 | |
1065 | if (AR_SREV_9462(ah)) | |
1066 | REG_WRITE(ah, AR_SELFGEN_MASK, 0x02); | |
4f6bd1a8 | 1067 | } else { |
83bfea42 RM |
1068 | ar9003_mci_send_lna_take(ah, true); |
1069 | udelay(5); | |
4ff6a9d2 | 1070 | |
4f6bd1a8 RM |
1071 | REG_SET_BIT(ah, AR_MCI_TX_CTRL, |
1072 | AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); | |
1073 | REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, | |
1074 | AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); | |
4f6bd1a8 | 1075 | |
1bde95fa | 1076 | ar9003_mci_osla_setup(ah, false); |
83bfea42 | 1077 | ar9003_mci_send_2g5g_status(ah, true); |
bbefb871 MSS |
1078 | } |
1079 | } | |
1080 | ||
1081 | bool ar9003_mci_send_message(struct ath_hw *ah, u8 header, u32 flag, | |
1082 | u32 *payload, u8 len, bool wait_done, | |
1083 | bool check_bt) | |
1084 | { | |
1085 | struct ath_common *common = ath9k_hw_common(ah); | |
1086 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1087 | bool msg_sent = false; | |
1088 | u32 regval; | |
1089 | u32 saved_mci_int_en; | |
1090 | int i; | |
1091 | ||
1092 | saved_mci_int_en = REG_READ(ah, AR_MCI_INTERRUPT_EN); | |
1093 | regval = REG_READ(ah, AR_BTCOEX_CTRL); | |
1094 | ||
1095 | if ((regval == 0xdeadbeef) || !(regval & AR_BTCOEX_CTRL_MCI_MODE_EN)) { | |
d2182b69 JP |
1096 | ath_dbg(common, MCI, |
1097 | "MCI Not sending 0x%x. MCI is not enabled. full_sleep = %d\n", | |
37cd9d78 | 1098 | header, (ah->power_mode == ATH9K_PM_FULL_SLEEP) ? 1 : 0); |
bbefb871 MSS |
1099 | ar9003_mci_queue_unsent_gpm(ah, header, payload, true); |
1100 | return false; | |
bbefb871 | 1101 | } else if (check_bt && (mci->bt_state == MCI_BT_SLEEP)) { |
d2182b69 JP |
1102 | ath_dbg(common, MCI, |
1103 | "MCI Don't send message 0x%x. BT is in sleep state\n", | |
1104 | header); | |
bbefb871 MSS |
1105 | ar9003_mci_queue_unsent_gpm(ah, header, payload, true); |
1106 | return false; | |
1107 | } | |
1108 | ||
1109 | if (wait_done) | |
1110 | REG_WRITE(ah, AR_MCI_INTERRUPT_EN, 0); | |
1111 | ||
1112 | /* Need to clear SW_MSG_DONE raw bit before wait */ | |
1113 | ||
1114 | REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, | |
1115 | (AR_MCI_INTERRUPT_SW_MSG_DONE | | |
1116 | AR_MCI_INTERRUPT_MSG_FAIL_MASK)); | |
1117 | ||
1118 | if (payload) { | |
1119 | for (i = 0; (i * 4) < len; i++) | |
1120 | REG_WRITE(ah, (AR_MCI_TX_PAYLOAD0 + i * 4), | |
1121 | *(payload + i)); | |
1122 | } | |
1123 | ||
1124 | REG_WRITE(ah, AR_MCI_COMMAND0, | |
1125 | (SM((flag & MCI_FLAG_DISABLE_TIMESTAMP), | |
1126 | AR_MCI_COMMAND0_DISABLE_TIMESTAMP) | | |
1127 | SM(len, AR_MCI_COMMAND0_LEN) | | |
1128 | SM(header, AR_MCI_COMMAND0_HEADER))); | |
1129 | ||
1130 | if (wait_done && | |
1131 | !(ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RAW, | |
37cd9d78 | 1132 | AR_MCI_INTERRUPT_SW_MSG_DONE, 500))) |
bbefb871 MSS |
1133 | ar9003_mci_queue_unsent_gpm(ah, header, payload, true); |
1134 | else { | |
1135 | ar9003_mci_queue_unsent_gpm(ah, header, payload, false); | |
1136 | msg_sent = true; | |
1137 | } | |
1138 | ||
1139 | if (wait_done) | |
1140 | REG_WRITE(ah, AR_MCI_INTERRUPT_EN, saved_mci_int_en); | |
1141 | ||
1142 | return msg_sent; | |
1143 | } | |
1144 | EXPORT_SYMBOL(ar9003_mci_send_message); | |
1145 | ||
f2f408ef SM |
1146 | void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable) |
1147 | { | |
1148 | struct ath_common *common = ath9k_hw_common(ah); | |
1149 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; | |
1150 | u32 pld[4] = {0, 0, 0, 0}; | |
1151 | ||
1152 | if ((mci_hw->bt_state != MCI_BT_AWAKE) || | |
1153 | (mci_hw->config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) | |
1154 | return; | |
1155 | ||
f2f408ef SM |
1156 | MCI_GPM_SET_CAL_TYPE(pld, MCI_GPM_WLAN_CAL_REQ); |
1157 | pld[MCI_GPM_WLAN_CAL_W_SEQUENCE] = mci_hw->wlan_cal_seq++; | |
1158 | ||
1159 | ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, true, false); | |
1160 | ||
f2f408ef | 1161 | if (ar9003_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_GRANT, 0, 50000)) { |
37cd9d78 | 1162 | ath_dbg(common, MCI, "MCI BT_CAL_GRANT received\n"); |
f2f408ef | 1163 | } else { |
2fd5d35b | 1164 | *is_reusable = false; |
37cd9d78 | 1165 | ath_dbg(common, MCI, "MCI BT_CAL_GRANT not received\n"); |
f2f408ef SM |
1166 | } |
1167 | } | |
1168 | ||
1169 | void ar9003_mci_init_cal_done(struct ath_hw *ah) | |
1170 | { | |
f2f408ef SM |
1171 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; |
1172 | u32 pld[4] = {0, 0, 0, 0}; | |
1173 | ||
1174 | if ((mci_hw->bt_state != MCI_BT_AWAKE) || | |
1175 | (mci_hw->config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) | |
1176 | return; | |
1177 | ||
f2f408ef SM |
1178 | MCI_GPM_SET_CAL_TYPE(pld, MCI_GPM_WLAN_CAL_DONE); |
1179 | pld[MCI_GPM_WLAN_CAL_W_SEQUENCE] = mci_hw->wlan_cal_done++; | |
1180 | ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, true, false); | |
1181 | } | |
1182 | ||
69c6ac60 SM |
1183 | int ar9003_mci_setup(struct ath_hw *ah, u32 gpm_addr, void *gpm_buf, |
1184 | u16 len, u32 sched_addr) | |
bbefb871 MSS |
1185 | { |
1186 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
bbefb871 MSS |
1187 | |
1188 | mci->gpm_addr = gpm_addr; | |
1189 | mci->gpm_buf = gpm_buf; | |
1190 | mci->gpm_len = len; | |
1191 | mci->sched_addr = sched_addr; | |
bbefb871 | 1192 | |
69c6ac60 | 1193 | return ar9003_mci_reset(ah, true, true, true); |
bbefb871 MSS |
1194 | } |
1195 | EXPORT_SYMBOL(ar9003_mci_setup); | |
1196 | ||
1197 | void ar9003_mci_cleanup(struct ath_hw *ah) | |
1198 | { | |
bbefb871 MSS |
1199 | /* Turn off MCI and Jupiter mode. */ |
1200 | REG_WRITE(ah, AR_BTCOEX_CTRL, 0x00); | |
bbefb871 MSS |
1201 | ar9003_mci_disable_interrupt(ah); |
1202 | } | |
1203 | EXPORT_SYMBOL(ar9003_mci_cleanup); | |
1204 | ||
b98ccec0 | 1205 | u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type) |
bbefb871 | 1206 | { |
bbefb871 | 1207 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
2097fdd7 | 1208 | u32 value = 0, tsf; |
bbefb871 MSS |
1209 | u8 query_type; |
1210 | ||
1211 | switch (state_type) { | |
1212 | case MCI_STATE_ENABLE: | |
1213 | if (mci->ready) { | |
bbefb871 MSS |
1214 | value = REG_READ(ah, AR_BTCOEX_CTRL); |
1215 | ||
1216 | if ((value == 0xdeadbeef) || (value == 0xffffffff)) | |
1217 | value = 0; | |
1218 | } | |
1219 | value &= AR_BTCOEX_CTRL_MCI_MODE_EN; | |
4f6bd1a8 | 1220 | break; |
bbefb871 MSS |
1221 | case MCI_STATE_LAST_SCHD_MSG_OFFSET: |
1222 | value = MS(REG_READ(ah, AR_MCI_RX_STATUS), | |
1223 | AR_MCI_RX_LAST_SCHD_MSG_INDEX); | |
1224 | /* Make it in bytes */ | |
1225 | value <<= 4; | |
1226 | break; | |
bbefb871 MSS |
1227 | case MCI_STATE_REMOTE_SLEEP: |
1228 | value = MS(REG_READ(ah, AR_MCI_RX_STATUS), | |
1229 | AR_MCI_RX_REMOTE_SLEEP) ? | |
1230 | MCI_BT_SLEEP : MCI_BT_AWAKE; | |
1231 | break; | |
bbefb871 MSS |
1232 | case MCI_STATE_SET_BT_AWAKE: |
1233 | mci->bt_state = MCI_BT_AWAKE; | |
1234 | ar9003_mci_send_coex_version_query(ah, true); | |
1235 | ar9003_mci_send_coex_wlan_channels(ah, true); | |
1236 | ||
37cd9d78 | 1237 | if (mci->unhalt_bt_gpm) |
bbefb871 | 1238 | ar9003_mci_send_coex_halt_bt_gpm(ah, false, true); |
bbefb871 | 1239 | |
1bde95fa | 1240 | ar9003_mci_2g5g_switch(ah, false); |
bbefb871 | 1241 | break; |
bbefb871 MSS |
1242 | case MCI_STATE_RESET_REQ_WAKE: |
1243 | ar9003_mci_reset_req_wakeup(ah); | |
1244 | mci->update_2g5g = true; | |
1245 | ||
0cc4cdeb | 1246 | if (mci->config & ATH_MCI_CONFIG_MCI_OBS_MASK) { |
bbefb871 MSS |
1247 | /* Check if we still have control of the GPIOs */ |
1248 | if ((REG_READ(ah, AR_GLB_GPIO_CONTROL) & | |
37cd9d78 SM |
1249 | ATH_MCI_CONFIG_MCI_OBS_GPIO) != |
1250 | ATH_MCI_CONFIG_MCI_OBS_GPIO) { | |
bbefb871 MSS |
1251 | ar9003_mci_observation_set_up(ah); |
1252 | } | |
1253 | } | |
1254 | break; | |
bbefb871 MSS |
1255 | case MCI_STATE_SEND_WLAN_COEX_VERSION: |
1256 | ar9003_mci_send_coex_version_response(ah, true); | |
1257 | break; | |
bbefb871 MSS |
1258 | case MCI_STATE_SEND_VERSION_QUERY: |
1259 | ar9003_mci_send_coex_version_query(ah, true); | |
1260 | break; | |
bbefb871 | 1261 | case MCI_STATE_SEND_STATUS_QUERY: |
c91ec465 | 1262 | query_type = MCI_GPM_COEX_QUERY_BT_TOPOLOGY; |
bbefb871 MSS |
1263 | ar9003_mci_send_coex_bt_status_query(ah, true, query_type); |
1264 | break; | |
bbefb871 | 1265 | case MCI_STATE_RECOVER_RX: |
2097fdd7 RM |
1266 | tsf = ath9k_hw_gettsf32(ah); |
1267 | if ((tsf - mci->last_recovery) <= MCI_RECOVERY_DUR_TSF) { | |
1268 | ath_dbg(ath9k_hw_common(ah), MCI, | |
1269 | "(MCI) ignore Rx recovery\n"); | |
1270 | break; | |
1271 | } | |
1272 | ath_dbg(ath9k_hw_common(ah), MCI, "(MCI) RECOVER RX\n"); | |
1273 | mci->last_recovery = tsf; | |
bbefb871 MSS |
1274 | ar9003_mci_prep_interface(ah); |
1275 | mci->query_bt = true; | |
1276 | mci->need_flush_btinfo = true; | |
1277 | ar9003_mci_send_coex_wlan_channels(ah, true); | |
1bde95fa | 1278 | ar9003_mci_2g5g_switch(ah, false); |
bbefb871 | 1279 | break; |
bbefb871 MSS |
1280 | case MCI_STATE_NEED_FTP_STOMP: |
1281 | value = !(mci->config & ATH_MCI_CONFIG_DISABLE_FTP_STOMP); | |
1282 | break; | |
d92bb98f RM |
1283 | case MCI_STATE_NEED_FLUSH_BT_INFO: |
1284 | value = (!mci->unhalt_bt_gpm && mci->need_flush_btinfo) ? 1 : 0; | |
1285 | mci->need_flush_btinfo = false; | |
1286 | break; | |
bbefb871 MSS |
1287 | default: |
1288 | break; | |
bbefb871 MSS |
1289 | } |
1290 | ||
1291 | return value; | |
1292 | } | |
1293 | EXPORT_SYMBOL(ar9003_mci_state); | |
99922a45 RM |
1294 | |
1295 | void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah) | |
1296 | { | |
1297 | struct ath_common *common = ath9k_hw_common(ah); | |
1298 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1299 | ||
1300 | ath_dbg(common, MCI, "Give LNA and SPDT control to BT\n"); | |
1301 | ||
4ff6a9d2 RM |
1302 | ar9003_mci_send_lna_take(ah, true); |
1303 | udelay(50); | |
1304 | ||
99922a45 RM |
1305 | REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); |
1306 | mci->is_2g = false; | |
1307 | mci->update_2g5g = true; | |
1308 | ar9003_mci_send_2g5g_status(ah, true); | |
1309 | ||
1310 | /* Force another 2g5g update at next scanning */ | |
1311 | mci->update_2g5g = true; | |
1312 | } | |
9dd9b0dc RM |
1313 | |
1314 | void ar9003_mci_set_power_awake(struct ath_hw *ah) | |
1315 | { | |
1316 | u32 btcoex_ctrl2, diag_sw; | |
1317 | int i; | |
1318 | u8 lna_ctrl, bt_sleep; | |
1319 | ||
1320 | for (i = 0; i < AH_WAIT_TIMEOUT; i++) { | |
1321 | btcoex_ctrl2 = REG_READ(ah, AR_BTCOEX_CTRL2); | |
1322 | if (btcoex_ctrl2 != 0xdeadbeef) | |
1323 | break; | |
1324 | udelay(AH_TIME_QUANTUM); | |
1325 | } | |
1326 | REG_WRITE(ah, AR_BTCOEX_CTRL2, (btcoex_ctrl2 | BIT(23))); | |
1327 | ||
1328 | for (i = 0; i < AH_WAIT_TIMEOUT; i++) { | |
1329 | diag_sw = REG_READ(ah, AR_DIAG_SW); | |
1330 | if (diag_sw != 0xdeadbeef) | |
1331 | break; | |
1332 | udelay(AH_TIME_QUANTUM); | |
1333 | } | |
1334 | REG_WRITE(ah, AR_DIAG_SW, (diag_sw | BIT(27) | BIT(19) | BIT(18))); | |
1335 | lna_ctrl = REG_READ(ah, AR_OBS_BUS_CTRL) & 0x3; | |
a50d1fd4 | 1336 | bt_sleep = MS(REG_READ(ah, AR_MCI_RX_STATUS), AR_MCI_RX_REMOTE_SLEEP); |
9dd9b0dc RM |
1337 | |
1338 | REG_WRITE(ah, AR_BTCOEX_CTRL2, btcoex_ctrl2); | |
1339 | REG_WRITE(ah, AR_DIAG_SW, diag_sw); | |
1340 | ||
1341 | if (bt_sleep && (lna_ctrl == 2)) { | |
1342 | REG_SET_BIT(ah, AR_BTCOEX_RC, 0x1); | |
1343 | REG_CLR_BIT(ah, AR_BTCOEX_RC, 0x1); | |
1344 | udelay(50); | |
1345 | } | |
1346 | } | |
506847ad RM |
1347 | |
1348 | void ar9003_mci_check_gpm_offset(struct ath_hw *ah) | |
1349 | { | |
1350 | struct ath_common *common = ath9k_hw_common(ah); | |
1351 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1352 | u32 offset; | |
1353 | ||
1354 | /* | |
1355 | * This should only be called before "MAC Warm Reset" or "MCI Reset Rx". | |
1356 | */ | |
1357 | offset = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); | |
1358 | if (mci->gpm_idx == offset) | |
1359 | return; | |
1360 | ath_dbg(common, MCI, "GPM cached write pointer mismatch %d %d\n", | |
1361 | mci->gpm_idx, offset); | |
1362 | mci->query_bt = true; | |
1363 | mci->need_flush_btinfo = true; | |
1364 | mci->gpm_idx = 0; | |
1365 | } | |
1366 | ||
1367 | u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, bool first, u32 *more) | |
1368 | { | |
1369 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1370 | u32 offset, more_gpm = 0, gpm_ptr; | |
1371 | ||
1372 | if (first) { | |
1373 | gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); | |
90be994c MSS |
1374 | |
1375 | if (gpm_ptr >= mci->gpm_len) | |
1376 | gpm_ptr = 0; | |
1377 | ||
506847ad RM |
1378 | mci->gpm_idx = gpm_ptr; |
1379 | return gpm_ptr; | |
1380 | } | |
1381 | ||
1382 | /* | |
1383 | * This could be useful to avoid new GPM message interrupt which | |
1384 | * may lead to spurious interrupt after power sleep, or multiple | |
1385 | * entry of ath_mci_intr(). | |
1386 | * Adding empty GPM check by returning HAL_MCI_GPM_INVALID can | |
1387 | * alleviate this effect, but clearing GPM RX interrupt bit is | |
1388 | * safe, because whether this is called from hw or driver code | |
1389 | * there must be an interrupt bit set/triggered initially | |
1390 | */ | |
1391 | REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, | |
1392 | AR_MCI_INTERRUPT_RX_MSG_GPM); | |
1393 | ||
1394 | gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); | |
1395 | offset = gpm_ptr; | |
1396 | ||
1397 | if (!offset) | |
1398 | offset = mci->gpm_len - 1; | |
1399 | else if (offset >= mci->gpm_len) { | |
1400 | if (offset != 0xFFFF) | |
1401 | offset = 0; | |
1402 | } else { | |
1403 | offset--; | |
1404 | } | |
1405 | ||
1406 | if ((offset == 0xFFFF) || (gpm_ptr == mci->gpm_idx)) { | |
1407 | offset = MCI_GPM_INVALID; | |
1408 | more_gpm = MCI_GPM_NOMORE; | |
1409 | goto out; | |
1410 | } | |
1411 | for (;;) { | |
1412 | u32 temp_index; | |
1413 | ||
1414 | /* skip reserved GPM if any */ | |
1415 | ||
1416 | if (offset != mci->gpm_idx) | |
1417 | more_gpm = MCI_GPM_MORE; | |
1418 | else | |
1419 | more_gpm = MCI_GPM_NOMORE; | |
1420 | ||
1421 | temp_index = mci->gpm_idx; | |
90be994c MSS |
1422 | |
1423 | if (temp_index >= mci->gpm_len) | |
1424 | temp_index = 0; | |
1425 | ||
506847ad RM |
1426 | mci->gpm_idx++; |
1427 | ||
1428 | if (mci->gpm_idx >= mci->gpm_len) | |
1429 | mci->gpm_idx = 0; | |
1430 | ||
1431 | if (ar9003_mci_is_gpm_valid(ah, temp_index)) { | |
1432 | offset = temp_index; | |
1433 | break; | |
1434 | } | |
1435 | ||
1436 | if (more_gpm == MCI_GPM_NOMORE) { | |
1437 | offset = MCI_GPM_INVALID; | |
1438 | break; | |
1439 | } | |
1440 | } | |
1441 | ||
1442 | if (offset != MCI_GPM_INVALID) | |
1443 | offset <<= 4; | |
1444 | out: | |
1445 | if (more) | |
1446 | *more = more_gpm; | |
1447 | ||
1448 | return offset; | |
1449 | } | |
1450 | EXPORT_SYMBOL(ar9003_mci_get_next_gpm_offset); | |
e1763d3f RM |
1451 | |
1452 | void ar9003_mci_set_bt_version(struct ath_hw *ah, u8 major, u8 minor) | |
1453 | { | |
1454 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1455 | ||
1456 | mci->bt_ver_major = major; | |
1457 | mci->bt_ver_minor = minor; | |
1458 | mci->bt_version_known = true; | |
1459 | ath_dbg(ath9k_hw_common(ah), MCI, "MCI BT version set: %d.%d\n", | |
1460 | mci->bt_ver_major, mci->bt_ver_minor); | |
1461 | } | |
1462 | EXPORT_SYMBOL(ar9003_mci_set_bt_version); | |
2d340ac8 RM |
1463 | |
1464 | void ar9003_mci_send_wlan_channels(struct ath_hw *ah) | |
1465 | { | |
1466 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1467 | ||
1468 | mci->wlan_channels_update = true; | |
1469 | ar9003_mci_send_coex_wlan_channels(ah, true); | |
1470 | } | |
1471 | EXPORT_SYMBOL(ar9003_mci_send_wlan_channels); | |
e82cb03f RM |
1472 | |
1473 | u16 ar9003_mci_get_max_txpower(struct ath_hw *ah, u8 ctlmode) | |
1474 | { | |
1475 | if (!ah->btcoex_hw.mci.concur_tx) | |
1476 | goto out; | |
1477 | ||
1478 | if (ctlmode == CTL_2GHT20) | |
1479 | return ATH_BTCOEX_HT20_MAX_TXPOWER; | |
1480 | else if (ctlmode == CTL_2GHT40) | |
1481 | return ATH_BTCOEX_HT40_MAX_TXPOWER; | |
1482 | ||
1483 | out: | |
1484 | return -1; | |
1485 | } |