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 | } | |
83ad49a9 | 717 | EXPORT_SYMBOL(ar9003_mci_start_reset); |
528e5d36 SM |
718 | |
719 | int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, | |
720 | struct ath9k_hw_cal_data *caldata) | |
721 | { | |
528e5d36 SM |
722 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; |
723 | ||
724 | if (!mci_hw->ready) | |
725 | return 0; | |
726 | ||
727 | if (!IS_CHAN_2GHZ(chan) || (mci_hw->bt_state != MCI_BT_SLEEP)) | |
728 | goto exit; | |
729 | ||
4f6bd1a8 RM |
730 | if (!ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET) && |
731 | !ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)) | |
732 | goto exit; | |
528e5d36 | 733 | |
4f6bd1a8 RM |
734 | /* |
735 | * BT is sleeping. Check if BT wakes up during | |
736 | * WLAN calibration. If BT wakes up during | |
737 | * WLAN calibration, need to go through all | |
738 | * message exchanges again and recal. | |
739 | */ | |
740 | REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, | |
741 | (AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET | | |
742 | AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)); | |
528e5d36 | 743 | |
4f6bd1a8 RM |
744 | ar9003_mci_remote_reset(ah, true); |
745 | ar9003_mci_send_sys_waking(ah, true); | |
746 | udelay(1); | |
528e5d36 | 747 | |
4f6bd1a8 RM |
748 | if (IS_CHAN_2GHZ(chan)) |
749 | ar9003_mci_send_lna_transfer(ah, true); | |
528e5d36 | 750 | |
4f6bd1a8 | 751 | mci_hw->bt_state = MCI_BT_AWAKE; |
528e5d36 | 752 | |
4f6bd1a8 RM |
753 | if (caldata) { |
754 | caldata->done_txiqcal_once = false; | |
755 | caldata->done_txclcal_once = false; | |
756 | caldata->rtt_done = false; | |
757 | } | |
528e5d36 | 758 | |
4f6bd1a8 RM |
759 | if (!ath9k_hw_init_cal(ah, chan)) |
760 | return -EIO; | |
528e5d36 | 761 | |
528e5d36 SM |
762 | exit: |
763 | ar9003_mci_enable_interrupt(ah); | |
764 | return 0; | |
765 | } | |
766 | ||
a3f846f1 SM |
767 | static void ar9003_mci_mute_bt(struct ath_hw *ah) |
768 | { | |
a3f846f1 SM |
769 | /* disable all MCI messages */ |
770 | REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE, 0xffff0000); | |
a3f846f1 SM |
771 | REG_SET_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); |
772 | ||
773 | /* wait pending HW messages to flush out */ | |
774 | udelay(10); | |
775 | ||
776 | /* | |
777 | * Send LNA_TAKE and SYS_SLEEPING when | |
778 | * 1. reset not after resuming from full sleep | |
779 | * 2. before reset MCI RX, to quiet BT and avoid MCI RX misalignment | |
780 | */ | |
a3f846f1 SM |
781 | ar9003_mci_send_lna_take(ah, true); |
782 | ||
783 | udelay(5); | |
784 | ||
a3f846f1 SM |
785 | ar9003_mci_send_sys_sleeping(ah, true); |
786 | } | |
787 | ||
4f851df7 SM |
788 | static void ar9003_mci_osla_setup(struct ath_hw *ah, bool enable) |
789 | { | |
790 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
791 | u32 thresh; | |
792 | ||
4f6bd1a8 | 793 | if (!enable) { |
4f851df7 SM |
794 | REG_CLR_BIT(ah, AR_BTCOEX_CTRL, |
795 | AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); | |
4f6bd1a8 | 796 | return; |
4f851df7 | 797 | } |
4f6bd1a8 RM |
798 | REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, AR_MCI_SCHD_TABLE_2_HW_BASED, 1); |
799 | REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, | |
800 | AR_MCI_SCHD_TABLE_2_MEM_BASED, 1); | |
801 | ||
802 | if (!(mci->config & ATH_MCI_CONFIG_DISABLE_AGGR_THRESH)) { | |
803 | thresh = MS(mci->config, ATH_MCI_CONFIG_AGGR_THRESH); | |
804 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
805 | AR_BTCOEX_CTRL_AGGR_THRESH, thresh); | |
806 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
807 | AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 1); | |
808 | } else | |
809 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
810 | AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 0); | |
811 | ||
812 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
813 | AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN, 1); | |
4f851df7 SM |
814 | } |
815 | ||
69c6ac60 SM |
816 | int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, |
817 | bool is_full_sleep) | |
bbefb871 MSS |
818 | { |
819 | struct ath_common *common = ath9k_hw_common(ah); | |
820 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
4f851df7 | 821 | u32 regval; |
bbefb871 | 822 | |
4f851df7 | 823 | ath_dbg(common, MCI, "MCI Reset (full_sleep = %d, is_2g = %d)\n", |
bbefb871 MSS |
824 | is_full_sleep, is_2g); |
825 | ||
bbefb871 | 826 | if (!mci->gpm_addr && !mci->sched_addr) { |
69c6ac60 SM |
827 | ath_err(common, "MCI GPM and schedule buffers are not allocated\n"); |
828 | return -ENOMEM; | |
bbefb871 MSS |
829 | } |
830 | ||
831 | if (REG_READ(ah, AR_BTCOEX_CTRL) == 0xdeadbeef) { | |
69c6ac60 SM |
832 | ath_err(common, "BTCOEX control register is dead\n"); |
833 | return -EINVAL; | |
bbefb871 MSS |
834 | } |
835 | ||
836 | /* Program MCI DMA related registers */ | |
837 | REG_WRITE(ah, AR_MCI_GPM_0, mci->gpm_addr); | |
838 | REG_WRITE(ah, AR_MCI_GPM_1, mci->gpm_len); | |
839 | REG_WRITE(ah, AR_MCI_SCHD_TABLE_0, mci->sched_addr); | |
840 | ||
841 | /* | |
842 | * To avoid MCI state machine be affected by incoming remote MCI msgs, | |
843 | * MCI mode will be enabled later, right before reset the MCI TX and RX. | |
844 | */ | |
845 | ||
846 | regval = SM(1, AR_BTCOEX_CTRL_AR9462_MODE) | | |
847 | SM(1, AR_BTCOEX_CTRL_WBTIMER_EN) | | |
848 | SM(1, AR_BTCOEX_CTRL_PA_SHARED) | | |
849 | SM(1, AR_BTCOEX_CTRL_LNA_SHARED) | | |
850 | SM(2, AR_BTCOEX_CTRL_NUM_ANTENNAS) | | |
851 | SM(3, AR_BTCOEX_CTRL_RX_CHAIN_MASK) | | |
852 | SM(0, AR_BTCOEX_CTRL_1_CHAIN_ACK) | | |
853 | SM(0, AR_BTCOEX_CTRL_1_CHAIN_BCN) | | |
854 | SM(0, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); | |
855 | ||
bbefb871 MSS |
856 | REG_WRITE(ah, AR_BTCOEX_CTRL, regval); |
857 | ||
4f851df7 SM |
858 | if (is_2g && !(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) |
859 | ar9003_mci_osla_setup(ah, true); | |
860 | else | |
861 | ar9003_mci_osla_setup(ah, false); | |
862 | ||
863 | REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, | |
864 | AR_BTCOEX_CTRL_SPDT_ENABLE); | |
865 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL3, | |
866 | AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT, 20); | |
bbefb871 MSS |
867 | |
868 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_RX_DEWEIGHT, 1); | |
869 | REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); | |
870 | ||
4f851df7 SM |
871 | regval = MS(mci->config, ATH_MCI_CONFIG_CLK_DIV); |
872 | REG_RMW_FIELD(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_CLK_DIV, regval); | |
bbefb871 MSS |
873 | REG_SET_BIT(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_MCI_MODE_EN); |
874 | ||
875 | /* Resetting the Rx and Tx paths of MCI */ | |
876 | regval = REG_READ(ah, AR_MCI_COMMAND2); | |
877 | regval |= SM(1, AR_MCI_COMMAND2_RESET_TX); | |
878 | REG_WRITE(ah, AR_MCI_COMMAND2, regval); | |
879 | ||
880 | udelay(1); | |
881 | ||
882 | regval &= ~SM(1, AR_MCI_COMMAND2_RESET_TX); | |
883 | REG_WRITE(ah, AR_MCI_COMMAND2, regval); | |
884 | ||
885 | if (is_full_sleep) { | |
886 | ar9003_mci_mute_bt(ah); | |
887 | udelay(100); | |
888 | } | |
889 | ||
3863495b | 890 | /* Check pending GPM msg before MCI Reset Rx */ |
506847ad | 891 | ar9003_mci_check_gpm_offset(ah); |
3863495b | 892 | |
bbefb871 MSS |
893 | regval |= SM(1, AR_MCI_COMMAND2_RESET_RX); |
894 | REG_WRITE(ah, AR_MCI_COMMAND2, regval); | |
895 | udelay(1); | |
896 | regval &= ~SM(1, AR_MCI_COMMAND2_RESET_RX); | |
897 | REG_WRITE(ah, AR_MCI_COMMAND2, regval); | |
898 | ||
506847ad | 899 | ar9003_mci_get_next_gpm_offset(ah, true, NULL); |
4f851df7 | 900 | |
bbefb871 MSS |
901 | REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE, |
902 | (SM(0xe801, AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR) | | |
903 | SM(0x0000, AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM))); | |
904 | ||
905 | REG_CLR_BIT(ah, AR_MCI_TX_CTRL, | |
4f851df7 | 906 | AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); |
bbefb871 | 907 | |
4f851df7 | 908 | ar9003_mci_observation_set_up(ah); |
bbefb871 MSS |
909 | |
910 | mci->ready = true; | |
911 | ar9003_mci_prep_interface(ah); | |
912 | ||
913 | if (en_int) | |
914 | ar9003_mci_enable_interrupt(ah); | |
69c6ac60 SM |
915 | |
916 | return 0; | |
bbefb871 MSS |
917 | } |
918 | ||
528e5d36 SM |
919 | void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep) |
920 | { | |
921 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; | |
922 | ||
923 | ar9003_mci_disable_interrupt(ah); | |
924 | ||
925 | if (mci_hw->ready && !save_fullsleep) { | |
926 | ar9003_mci_mute_bt(ah); | |
927 | udelay(20); | |
928 | REG_WRITE(ah, AR_BTCOEX_CTRL, 0); | |
929 | } | |
930 | ||
931 | mci_hw->bt_state = MCI_BT_SLEEP; | |
932 | mci_hw->ready = false; | |
933 | } | |
934 | ||
bbefb871 MSS |
935 | static void ar9003_mci_send_2g5g_status(struct ath_hw *ah, bool wait_done) |
936 | { | |
bbefb871 MSS |
937 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
938 | u32 new_flags, to_set, to_clear; | |
939 | ||
4f6bd1a8 RM |
940 | if (!mci->update_2g5g || (mci->bt_state == MCI_BT_SLEEP)) |
941 | return; | |
bbefb871 | 942 | |
4f6bd1a8 RM |
943 | if (mci->is_2g) { |
944 | new_flags = MCI_2G_FLAGS; | |
945 | to_clear = MCI_2G_FLAGS_CLEAR_MASK; | |
946 | to_set = MCI_2G_FLAGS_SET_MASK; | |
947 | } else { | |
948 | new_flags = MCI_5G_FLAGS; | |
949 | to_clear = MCI_5G_FLAGS_CLEAR_MASK; | |
950 | to_set = MCI_5G_FLAGS_SET_MASK; | |
951 | } | |
952 | ||
953 | if (to_clear) | |
954 | ar9003_mci_send_coex_bt_flags(ah, wait_done, | |
37cd9d78 SM |
955 | MCI_GPM_COEX_BT_FLAGS_CLEAR, |
956 | to_clear); | |
4f6bd1a8 RM |
957 | if (to_set) |
958 | ar9003_mci_send_coex_bt_flags(ah, wait_done, | |
37cd9d78 SM |
959 | MCI_GPM_COEX_BT_FLAGS_SET, |
960 | to_set); | |
bbefb871 MSS |
961 | } |
962 | ||
963 | static void ar9003_mci_queue_unsent_gpm(struct ath_hw *ah, u8 header, | |
964 | u32 *payload, bool queue) | |
965 | { | |
bbefb871 MSS |
966 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
967 | u8 type, opcode; | |
968 | ||
bbefb871 MSS |
969 | /* check if the message is to be queued */ |
970 | if (header != MCI_GPM) | |
971 | return; | |
972 | ||
973 | type = MCI_GPM_TYPE(payload); | |
974 | opcode = MCI_GPM_OPCODE(payload); | |
975 | ||
976 | if (type != MCI_GPM_COEX_AGENT) | |
977 | return; | |
978 | ||
979 | switch (opcode) { | |
980 | case MCI_GPM_COEX_BT_UPDATE_FLAGS: | |
bbefb871 | 981 | if (*(((u8 *)payload) + MCI_GPM_COEX_B_BT_FLAGS_OP) == |
c91ec465 | 982 | MCI_GPM_COEX_BT_FLAGS_READ) |
bbefb871 MSS |
983 | break; |
984 | ||
985 | mci->update_2g5g = queue; | |
986 | ||
bbefb871 | 987 | break; |
bbefb871 | 988 | case MCI_GPM_COEX_WLAN_CHANNELS: |
bbefb871 | 989 | mci->wlan_channels_update = queue; |
bbefb871 | 990 | break; |
bbefb871 | 991 | case MCI_GPM_COEX_HALT_BT_GPM: |
bbefb871 | 992 | if (*(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) == |
37cd9d78 | 993 | MCI_GPM_COEX_BT_GPM_UNHALT) { |
bbefb871 MSS |
994 | mci->unhalt_bt_gpm = queue; |
995 | ||
37cd9d78 | 996 | if (!queue) |
bbefb871 | 997 | mci->halted_bt_gpm = false; |
bbefb871 MSS |
998 | } |
999 | ||
1000 | if (*(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) == | |
1001 | MCI_GPM_COEX_BT_GPM_HALT) { | |
1002 | ||
1003 | mci->halted_bt_gpm = !queue; | |
bbefb871 MSS |
1004 | } |
1005 | ||
1006 | break; | |
1007 | default: | |
1008 | break; | |
1009 | } | |
1010 | } | |
1011 | ||
1bde95fa | 1012 | void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool force) |
bbefb871 | 1013 | { |
bbefb871 MSS |
1014 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
1015 | ||
1bde95fa | 1016 | if (!mci->update_2g5g && !force) |
4f6bd1a8 | 1017 | return; |
bbefb871 | 1018 | |
4f6bd1a8 | 1019 | if (mci->is_2g) { |
83bfea42 RM |
1020 | ar9003_mci_send_2g5g_status(ah, true); |
1021 | ar9003_mci_send_lna_transfer(ah, true); | |
1022 | udelay(5); | |
4ff6a9d2 RM |
1023 | |
1024 | REG_CLR_BIT(ah, AR_MCI_TX_CTRL, | |
4f6bd1a8 RM |
1025 | AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); |
1026 | REG_CLR_BIT(ah, AR_PHY_GLB_CONTROL, | |
1027 | AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); | |
1028 | ||
1029 | if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) | |
1bde95fa | 1030 | ar9003_mci_osla_setup(ah, true); |
bfbee427 | 1031 | REG_WRITE(ah, AR_SELFGEN_MASK, 0x02); |
4f6bd1a8 | 1032 | } else { |
83bfea42 RM |
1033 | ar9003_mci_send_lna_take(ah, true); |
1034 | udelay(5); | |
4ff6a9d2 | 1035 | |
4f6bd1a8 RM |
1036 | REG_SET_BIT(ah, AR_MCI_TX_CTRL, |
1037 | AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); | |
1038 | REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, | |
1039 | AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); | |
4f6bd1a8 | 1040 | |
1bde95fa | 1041 | ar9003_mci_osla_setup(ah, false); |
83bfea42 | 1042 | ar9003_mci_send_2g5g_status(ah, true); |
bbefb871 MSS |
1043 | } |
1044 | } | |
1045 | ||
1046 | bool ar9003_mci_send_message(struct ath_hw *ah, u8 header, u32 flag, | |
1047 | u32 *payload, u8 len, bool wait_done, | |
1048 | bool check_bt) | |
1049 | { | |
1050 | struct ath_common *common = ath9k_hw_common(ah); | |
1051 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1052 | bool msg_sent = false; | |
1053 | u32 regval; | |
1054 | u32 saved_mci_int_en; | |
1055 | int i; | |
1056 | ||
1057 | saved_mci_int_en = REG_READ(ah, AR_MCI_INTERRUPT_EN); | |
1058 | regval = REG_READ(ah, AR_BTCOEX_CTRL); | |
1059 | ||
1060 | if ((regval == 0xdeadbeef) || !(regval & AR_BTCOEX_CTRL_MCI_MODE_EN)) { | |
d2182b69 JP |
1061 | ath_dbg(common, MCI, |
1062 | "MCI Not sending 0x%x. MCI is not enabled. full_sleep = %d\n", | |
37cd9d78 | 1063 | header, (ah->power_mode == ATH9K_PM_FULL_SLEEP) ? 1 : 0); |
bbefb871 MSS |
1064 | ar9003_mci_queue_unsent_gpm(ah, header, payload, true); |
1065 | return false; | |
bbefb871 | 1066 | } else if (check_bt && (mci->bt_state == MCI_BT_SLEEP)) { |
d2182b69 JP |
1067 | ath_dbg(common, MCI, |
1068 | "MCI Don't send message 0x%x. BT is in sleep state\n", | |
1069 | header); | |
bbefb871 MSS |
1070 | ar9003_mci_queue_unsent_gpm(ah, header, payload, true); |
1071 | return false; | |
1072 | } | |
1073 | ||
1074 | if (wait_done) | |
1075 | REG_WRITE(ah, AR_MCI_INTERRUPT_EN, 0); | |
1076 | ||
1077 | /* Need to clear SW_MSG_DONE raw bit before wait */ | |
1078 | ||
1079 | REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, | |
1080 | (AR_MCI_INTERRUPT_SW_MSG_DONE | | |
1081 | AR_MCI_INTERRUPT_MSG_FAIL_MASK)); | |
1082 | ||
1083 | if (payload) { | |
1084 | for (i = 0; (i * 4) < len; i++) | |
1085 | REG_WRITE(ah, (AR_MCI_TX_PAYLOAD0 + i * 4), | |
1086 | *(payload + i)); | |
1087 | } | |
1088 | ||
1089 | REG_WRITE(ah, AR_MCI_COMMAND0, | |
1090 | (SM((flag & MCI_FLAG_DISABLE_TIMESTAMP), | |
1091 | AR_MCI_COMMAND0_DISABLE_TIMESTAMP) | | |
1092 | SM(len, AR_MCI_COMMAND0_LEN) | | |
1093 | SM(header, AR_MCI_COMMAND0_HEADER))); | |
1094 | ||
1095 | if (wait_done && | |
1096 | !(ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RAW, | |
37cd9d78 | 1097 | AR_MCI_INTERRUPT_SW_MSG_DONE, 500))) |
bbefb871 MSS |
1098 | ar9003_mci_queue_unsent_gpm(ah, header, payload, true); |
1099 | else { | |
1100 | ar9003_mci_queue_unsent_gpm(ah, header, payload, false); | |
1101 | msg_sent = true; | |
1102 | } | |
1103 | ||
1104 | if (wait_done) | |
1105 | REG_WRITE(ah, AR_MCI_INTERRUPT_EN, saved_mci_int_en); | |
1106 | ||
1107 | return msg_sent; | |
1108 | } | |
1109 | EXPORT_SYMBOL(ar9003_mci_send_message); | |
1110 | ||
f2f408ef SM |
1111 | void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable) |
1112 | { | |
1113 | struct ath_common *common = ath9k_hw_common(ah); | |
1114 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; | |
1115 | u32 pld[4] = {0, 0, 0, 0}; | |
1116 | ||
1117 | if ((mci_hw->bt_state != MCI_BT_AWAKE) || | |
1118 | (mci_hw->config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) | |
1119 | return; | |
1120 | ||
f2f408ef SM |
1121 | MCI_GPM_SET_CAL_TYPE(pld, MCI_GPM_WLAN_CAL_REQ); |
1122 | pld[MCI_GPM_WLAN_CAL_W_SEQUENCE] = mci_hw->wlan_cal_seq++; | |
1123 | ||
1124 | ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, true, false); | |
1125 | ||
f2f408ef | 1126 | if (ar9003_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_GRANT, 0, 50000)) { |
37cd9d78 | 1127 | ath_dbg(common, MCI, "MCI BT_CAL_GRANT received\n"); |
f2f408ef | 1128 | } else { |
2fd5d35b | 1129 | *is_reusable = false; |
37cd9d78 | 1130 | ath_dbg(common, MCI, "MCI BT_CAL_GRANT not received\n"); |
f2f408ef SM |
1131 | } |
1132 | } | |
1133 | ||
1134 | void ar9003_mci_init_cal_done(struct ath_hw *ah) | |
1135 | { | |
f2f408ef SM |
1136 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; |
1137 | u32 pld[4] = {0, 0, 0, 0}; | |
1138 | ||
1139 | if ((mci_hw->bt_state != MCI_BT_AWAKE) || | |
1140 | (mci_hw->config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) | |
1141 | return; | |
1142 | ||
f2f408ef SM |
1143 | MCI_GPM_SET_CAL_TYPE(pld, MCI_GPM_WLAN_CAL_DONE); |
1144 | pld[MCI_GPM_WLAN_CAL_W_SEQUENCE] = mci_hw->wlan_cal_done++; | |
1145 | ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, true, false); | |
1146 | } | |
1147 | ||
69c6ac60 SM |
1148 | int ar9003_mci_setup(struct ath_hw *ah, u32 gpm_addr, void *gpm_buf, |
1149 | u16 len, u32 sched_addr) | |
bbefb871 MSS |
1150 | { |
1151 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
bbefb871 MSS |
1152 | |
1153 | mci->gpm_addr = gpm_addr; | |
1154 | mci->gpm_buf = gpm_buf; | |
1155 | mci->gpm_len = len; | |
1156 | mci->sched_addr = sched_addr; | |
bbefb871 | 1157 | |
69c6ac60 | 1158 | return ar9003_mci_reset(ah, true, true, true); |
bbefb871 MSS |
1159 | } |
1160 | EXPORT_SYMBOL(ar9003_mci_setup); | |
1161 | ||
1162 | void ar9003_mci_cleanup(struct ath_hw *ah) | |
1163 | { | |
bbefb871 MSS |
1164 | /* Turn off MCI and Jupiter mode. */ |
1165 | REG_WRITE(ah, AR_BTCOEX_CTRL, 0x00); | |
bbefb871 MSS |
1166 | ar9003_mci_disable_interrupt(ah); |
1167 | } | |
1168 | EXPORT_SYMBOL(ar9003_mci_cleanup); | |
1169 | ||
b98ccec0 | 1170 | u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type) |
bbefb871 | 1171 | { |
bbefb871 | 1172 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
506847ad | 1173 | u32 value = 0; |
bbefb871 MSS |
1174 | u8 query_type; |
1175 | ||
1176 | switch (state_type) { | |
1177 | case MCI_STATE_ENABLE: | |
1178 | if (mci->ready) { | |
bbefb871 MSS |
1179 | value = REG_READ(ah, AR_BTCOEX_CTRL); |
1180 | ||
1181 | if ((value == 0xdeadbeef) || (value == 0xffffffff)) | |
1182 | value = 0; | |
1183 | } | |
1184 | value &= AR_BTCOEX_CTRL_MCI_MODE_EN; | |
4f6bd1a8 | 1185 | break; |
bbefb871 MSS |
1186 | case MCI_STATE_LAST_SCHD_MSG_OFFSET: |
1187 | value = MS(REG_READ(ah, AR_MCI_RX_STATUS), | |
1188 | AR_MCI_RX_LAST_SCHD_MSG_INDEX); | |
1189 | /* Make it in bytes */ | |
1190 | value <<= 4; | |
1191 | break; | |
bbefb871 MSS |
1192 | case MCI_STATE_REMOTE_SLEEP: |
1193 | value = MS(REG_READ(ah, AR_MCI_RX_STATUS), | |
1194 | AR_MCI_RX_REMOTE_SLEEP) ? | |
1195 | MCI_BT_SLEEP : MCI_BT_AWAKE; | |
1196 | break; | |
bbefb871 MSS |
1197 | case MCI_STATE_SET_BT_AWAKE: |
1198 | mci->bt_state = MCI_BT_AWAKE; | |
1199 | ar9003_mci_send_coex_version_query(ah, true); | |
1200 | ar9003_mci_send_coex_wlan_channels(ah, true); | |
1201 | ||
37cd9d78 | 1202 | if (mci->unhalt_bt_gpm) |
bbefb871 | 1203 | ar9003_mci_send_coex_halt_bt_gpm(ah, false, true); |
bbefb871 | 1204 | |
1bde95fa | 1205 | ar9003_mci_2g5g_switch(ah, false); |
bbefb871 | 1206 | break; |
bbefb871 MSS |
1207 | case MCI_STATE_RESET_REQ_WAKE: |
1208 | ar9003_mci_reset_req_wakeup(ah); | |
1209 | mci->update_2g5g = true; | |
1210 | ||
0cc4cdeb | 1211 | if (mci->config & ATH_MCI_CONFIG_MCI_OBS_MASK) { |
bbefb871 MSS |
1212 | /* Check if we still have control of the GPIOs */ |
1213 | if ((REG_READ(ah, AR_GLB_GPIO_CONTROL) & | |
37cd9d78 SM |
1214 | ATH_MCI_CONFIG_MCI_OBS_GPIO) != |
1215 | ATH_MCI_CONFIG_MCI_OBS_GPIO) { | |
bbefb871 MSS |
1216 | ar9003_mci_observation_set_up(ah); |
1217 | } | |
1218 | } | |
1219 | break; | |
bbefb871 MSS |
1220 | case MCI_STATE_SEND_WLAN_COEX_VERSION: |
1221 | ar9003_mci_send_coex_version_response(ah, true); | |
1222 | break; | |
bbefb871 MSS |
1223 | case MCI_STATE_SEND_VERSION_QUERY: |
1224 | ar9003_mci_send_coex_version_query(ah, true); | |
1225 | break; | |
bbefb871 | 1226 | case MCI_STATE_SEND_STATUS_QUERY: |
c91ec465 | 1227 | query_type = MCI_GPM_COEX_QUERY_BT_TOPOLOGY; |
bbefb871 MSS |
1228 | ar9003_mci_send_coex_bt_status_query(ah, true, query_type); |
1229 | break; | |
bbefb871 | 1230 | case MCI_STATE_RECOVER_RX: |
bbefb871 MSS |
1231 | ar9003_mci_prep_interface(ah); |
1232 | mci->query_bt = true; | |
1233 | mci->need_flush_btinfo = true; | |
1234 | ar9003_mci_send_coex_wlan_channels(ah, true); | |
1bde95fa | 1235 | ar9003_mci_2g5g_switch(ah, false); |
bbefb871 | 1236 | break; |
bbefb871 MSS |
1237 | case MCI_STATE_NEED_FTP_STOMP: |
1238 | value = !(mci->config & ATH_MCI_CONFIG_DISABLE_FTP_STOMP); | |
1239 | break; | |
d92bb98f RM |
1240 | case MCI_STATE_NEED_FLUSH_BT_INFO: |
1241 | value = (!mci->unhalt_bt_gpm && mci->need_flush_btinfo) ? 1 : 0; | |
1242 | mci->need_flush_btinfo = false; | |
1243 | break; | |
bbefb871 MSS |
1244 | default: |
1245 | break; | |
bbefb871 MSS |
1246 | } |
1247 | ||
1248 | return value; | |
1249 | } | |
1250 | EXPORT_SYMBOL(ar9003_mci_state); | |
99922a45 RM |
1251 | |
1252 | void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah) | |
1253 | { | |
1254 | struct ath_common *common = ath9k_hw_common(ah); | |
1255 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1256 | ||
1257 | ath_dbg(common, MCI, "Give LNA and SPDT control to BT\n"); | |
1258 | ||
4ff6a9d2 RM |
1259 | ar9003_mci_send_lna_take(ah, true); |
1260 | udelay(50); | |
1261 | ||
99922a45 RM |
1262 | REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); |
1263 | mci->is_2g = false; | |
1264 | mci->update_2g5g = true; | |
1265 | ar9003_mci_send_2g5g_status(ah, true); | |
1266 | ||
1267 | /* Force another 2g5g update at next scanning */ | |
1268 | mci->update_2g5g = true; | |
1269 | } | |
9dd9b0dc RM |
1270 | |
1271 | void ar9003_mci_set_power_awake(struct ath_hw *ah) | |
1272 | { | |
1273 | u32 btcoex_ctrl2, diag_sw; | |
1274 | int i; | |
1275 | u8 lna_ctrl, bt_sleep; | |
1276 | ||
1277 | for (i = 0; i < AH_WAIT_TIMEOUT; i++) { | |
1278 | btcoex_ctrl2 = REG_READ(ah, AR_BTCOEX_CTRL2); | |
1279 | if (btcoex_ctrl2 != 0xdeadbeef) | |
1280 | break; | |
1281 | udelay(AH_TIME_QUANTUM); | |
1282 | } | |
1283 | REG_WRITE(ah, AR_BTCOEX_CTRL2, (btcoex_ctrl2 | BIT(23))); | |
1284 | ||
1285 | for (i = 0; i < AH_WAIT_TIMEOUT; i++) { | |
1286 | diag_sw = REG_READ(ah, AR_DIAG_SW); | |
1287 | if (diag_sw != 0xdeadbeef) | |
1288 | break; | |
1289 | udelay(AH_TIME_QUANTUM); | |
1290 | } | |
1291 | REG_WRITE(ah, AR_DIAG_SW, (diag_sw | BIT(27) | BIT(19) | BIT(18))); | |
1292 | lna_ctrl = REG_READ(ah, AR_OBS_BUS_CTRL) & 0x3; | |
a50d1fd4 | 1293 | bt_sleep = MS(REG_READ(ah, AR_MCI_RX_STATUS), AR_MCI_RX_REMOTE_SLEEP); |
9dd9b0dc RM |
1294 | |
1295 | REG_WRITE(ah, AR_BTCOEX_CTRL2, btcoex_ctrl2); | |
1296 | REG_WRITE(ah, AR_DIAG_SW, diag_sw); | |
1297 | ||
1298 | if (bt_sleep && (lna_ctrl == 2)) { | |
1299 | REG_SET_BIT(ah, AR_BTCOEX_RC, 0x1); | |
1300 | REG_CLR_BIT(ah, AR_BTCOEX_RC, 0x1); | |
1301 | udelay(50); | |
1302 | } | |
1303 | } | |
506847ad RM |
1304 | |
1305 | void ar9003_mci_check_gpm_offset(struct ath_hw *ah) | |
1306 | { | |
1307 | struct ath_common *common = ath9k_hw_common(ah); | |
1308 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1309 | u32 offset; | |
1310 | ||
1311 | /* | |
1312 | * This should only be called before "MAC Warm Reset" or "MCI Reset Rx". | |
1313 | */ | |
1314 | offset = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); | |
1315 | if (mci->gpm_idx == offset) | |
1316 | return; | |
1317 | ath_dbg(common, MCI, "GPM cached write pointer mismatch %d %d\n", | |
1318 | mci->gpm_idx, offset); | |
1319 | mci->query_bt = true; | |
1320 | mci->need_flush_btinfo = true; | |
1321 | mci->gpm_idx = 0; | |
1322 | } | |
1323 | ||
1324 | u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, bool first, u32 *more) | |
1325 | { | |
1326 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1327 | u32 offset, more_gpm = 0, gpm_ptr; | |
1328 | ||
1329 | if (first) { | |
1330 | gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); | |
90be994c MSS |
1331 | |
1332 | if (gpm_ptr >= mci->gpm_len) | |
1333 | gpm_ptr = 0; | |
1334 | ||
506847ad RM |
1335 | mci->gpm_idx = gpm_ptr; |
1336 | return gpm_ptr; | |
1337 | } | |
1338 | ||
1339 | /* | |
1340 | * This could be useful to avoid new GPM message interrupt which | |
1341 | * may lead to spurious interrupt after power sleep, or multiple | |
1342 | * entry of ath_mci_intr(). | |
1343 | * Adding empty GPM check by returning HAL_MCI_GPM_INVALID can | |
1344 | * alleviate this effect, but clearing GPM RX interrupt bit is | |
1345 | * safe, because whether this is called from hw or driver code | |
1346 | * there must be an interrupt bit set/triggered initially | |
1347 | */ | |
1348 | REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, | |
1349 | AR_MCI_INTERRUPT_RX_MSG_GPM); | |
1350 | ||
1351 | gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); | |
1352 | offset = gpm_ptr; | |
1353 | ||
1354 | if (!offset) | |
1355 | offset = mci->gpm_len - 1; | |
1356 | else if (offset >= mci->gpm_len) { | |
1357 | if (offset != 0xFFFF) | |
1358 | offset = 0; | |
1359 | } else { | |
1360 | offset--; | |
1361 | } | |
1362 | ||
1363 | if ((offset == 0xFFFF) || (gpm_ptr == mci->gpm_idx)) { | |
1364 | offset = MCI_GPM_INVALID; | |
1365 | more_gpm = MCI_GPM_NOMORE; | |
1366 | goto out; | |
1367 | } | |
1368 | for (;;) { | |
1369 | u32 temp_index; | |
1370 | ||
1371 | /* skip reserved GPM if any */ | |
1372 | ||
1373 | if (offset != mci->gpm_idx) | |
1374 | more_gpm = MCI_GPM_MORE; | |
1375 | else | |
1376 | more_gpm = MCI_GPM_NOMORE; | |
1377 | ||
1378 | temp_index = mci->gpm_idx; | |
90be994c MSS |
1379 | |
1380 | if (temp_index >= mci->gpm_len) | |
1381 | temp_index = 0; | |
1382 | ||
506847ad RM |
1383 | mci->gpm_idx++; |
1384 | ||
1385 | if (mci->gpm_idx >= mci->gpm_len) | |
1386 | mci->gpm_idx = 0; | |
1387 | ||
1388 | if (ar9003_mci_is_gpm_valid(ah, temp_index)) { | |
1389 | offset = temp_index; | |
1390 | break; | |
1391 | } | |
1392 | ||
1393 | if (more_gpm == MCI_GPM_NOMORE) { | |
1394 | offset = MCI_GPM_INVALID; | |
1395 | break; | |
1396 | } | |
1397 | } | |
1398 | ||
1399 | if (offset != MCI_GPM_INVALID) | |
1400 | offset <<= 4; | |
1401 | out: | |
1402 | if (more) | |
1403 | *more = more_gpm; | |
1404 | ||
1405 | return offset; | |
1406 | } | |
1407 | EXPORT_SYMBOL(ar9003_mci_get_next_gpm_offset); | |
e1763d3f RM |
1408 | |
1409 | void ar9003_mci_set_bt_version(struct ath_hw *ah, u8 major, u8 minor) | |
1410 | { | |
1411 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1412 | ||
1413 | mci->bt_ver_major = major; | |
1414 | mci->bt_ver_minor = minor; | |
1415 | mci->bt_version_known = true; | |
1416 | ath_dbg(ath9k_hw_common(ah), MCI, "MCI BT version set: %d.%d\n", | |
1417 | mci->bt_ver_major, mci->bt_ver_minor); | |
1418 | } | |
1419 | EXPORT_SYMBOL(ar9003_mci_set_bt_version); | |
2d340ac8 RM |
1420 | |
1421 | void ar9003_mci_send_wlan_channels(struct ath_hw *ah) | |
1422 | { | |
1423 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1424 | ||
1425 | mci->wlan_channels_update = true; | |
1426 | ar9003_mci_send_coex_wlan_channels(ah, true); | |
1427 | } | |
1428 | EXPORT_SYMBOL(ar9003_mci_send_wlan_channels); |