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