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 | 429 | if (mci->config & ATH_MCI_CONFIG_MCI_OBS_MCI) { |
b2d70d49 MP |
430 | ath9k_hw_gpio_request_out(ah, 3, NULL, |
431 | AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_DATA); | |
432 | ath9k_hw_gpio_request_out(ah, 2, NULL, | |
433 | AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK); | |
434 | ath9k_hw_gpio_request_out(ah, 1, NULL, | |
435 | AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA); | |
436 | ath9k_hw_gpio_request_out(ah, 0, NULL, | |
437 | AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK); | |
bbefb871 | 438 | } else if (mci->config & ATH_MCI_CONFIG_MCI_OBS_TXRX) { |
b2d70d49 MP |
439 | ath9k_hw_gpio_request_out(ah, 3, NULL, |
440 | AR_GPIO_OUTPUT_MUX_AS_WL_IN_TX); | |
441 | ath9k_hw_gpio_request_out(ah, 2, NULL, | |
442 | AR_GPIO_OUTPUT_MUX_AS_WL_IN_RX); | |
443 | ath9k_hw_gpio_request_out(ah, 1, NULL, | |
444 | AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX); | |
445 | ath9k_hw_gpio_request_out(ah, 0, NULL, | |
446 | AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX); | |
447 | ath9k_hw_gpio_request_out(ah, 5, NULL, | |
448 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | |
bbefb871 | 449 | } else if (mci->config & ATH_MCI_CONFIG_MCI_OBS_BT) { |
b2d70d49 MP |
450 | ath9k_hw_gpio_request_out(ah, 3, NULL, |
451 | AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX); | |
452 | ath9k_hw_gpio_request_out(ah, 2, NULL, | |
453 | AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX); | |
454 | ath9k_hw_gpio_request_out(ah, 1, NULL, | |
455 | AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA); | |
456 | ath9k_hw_gpio_request_out(ah, 0, NULL, | |
457 | AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK); | |
bbefb871 MSS |
458 | } else |
459 | return; | |
460 | ||
461 | REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); | |
462 | ||
0cc4cdeb SM |
463 | REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL, AR_GLB_DS_JTAG_DISABLE, 1); |
464 | REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL, AR_GLB_WLAN_UART_INTF_EN, 0); | |
465 | REG_SET_BIT(ah, AR_GLB_GPIO_CONTROL, ATH_MCI_CONFIG_MCI_OBS_GPIO); | |
bbefb871 MSS |
466 | |
467 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_GPIO_OBS_SEL, 0); | |
468 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_MAC_BB_OBS_SEL, 1); | |
469 | REG_WRITE(ah, AR_OBS, 0x4b); | |
470 | REG_RMW_FIELD(ah, AR_DIAG_SW, AR_DIAG_OBS_PT_SEL1, 0x03); | |
471 | REG_RMW_FIELD(ah, AR_DIAG_SW, AR_DIAG_OBS_PT_SEL2, 0x01); | |
472 | REG_RMW_FIELD(ah, AR_MACMISC, AR_MACMISC_MISC_OBS_BUS_LSB, 0x02); | |
473 | REG_RMW_FIELD(ah, AR_MACMISC, AR_MACMISC_MISC_OBS_BUS_MSB, 0x03); | |
474 | REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS, | |
475 | AR_PHY_TEST_CTL_DEBUGPORT_SEL, 0x07); | |
476 | } | |
477 | ||
478 | static bool ar9003_mci_send_coex_bt_flags(struct ath_hw *ah, bool wait_done, | |
37cd9d78 | 479 | u8 opcode, u32 bt_flags) |
bbefb871 | 480 | { |
bbefb871 MSS |
481 | u32 pld[4] = {0, 0, 0, 0}; |
482 | ||
37cd9d78 SM |
483 | MCI_GPM_SET_TYPE_OPCODE(pld, MCI_GPM_COEX_AGENT, |
484 | MCI_GPM_COEX_BT_UPDATE_FLAGS); | |
bbefb871 MSS |
485 | |
486 | *(((u8 *)pld) + MCI_GPM_COEX_B_BT_FLAGS_OP) = opcode; | |
487 | *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 0) = bt_flags & 0xFF; | |
488 | *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 1) = (bt_flags >> 8) & 0xFF; | |
489 | *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 2) = (bt_flags >> 16) & 0xFF; | |
490 | *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 3) = (bt_flags >> 24) & 0xFF; | |
491 | ||
bbefb871 | 492 | return ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, |
37cd9d78 | 493 | wait_done, true); |
bbefb871 MSS |
494 | } |
495 | ||
a3f846f1 SM |
496 | static void ar9003_mci_sync_bt_state(struct ath_hw *ah) |
497 | { | |
a3f846f1 SM |
498 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
499 | u32 cur_bt_state; | |
500 | ||
b98ccec0 | 501 | cur_bt_state = ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP); |
a3f846f1 | 502 | |
37cd9d78 | 503 | if (mci->bt_state != cur_bt_state) |
a3f846f1 | 504 | mci->bt_state = cur_bt_state; |
a3f846f1 SM |
505 | |
506 | if (mci->bt_state != MCI_BT_SLEEP) { | |
507 | ||
508 | ar9003_mci_send_coex_version_query(ah, true); | |
509 | ar9003_mci_send_coex_wlan_channels(ah, true); | |
510 | ||
37cd9d78 | 511 | if (mci->unhalt_bt_gpm == true) |
a3f846f1 | 512 | ar9003_mci_send_coex_halt_bt_gpm(ah, false, true); |
a3f846f1 SM |
513 | } |
514 | } | |
515 | ||
528e5d36 SM |
516 | void ar9003_mci_check_bt(struct ath_hw *ah) |
517 | { | |
518 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; | |
519 | ||
520 | if (!mci_hw->ready) | |
521 | return; | |
522 | ||
523 | /* | |
524 | * check BT state again to make | |
525 | * sure it's not changed. | |
526 | */ | |
527 | ar9003_mci_sync_bt_state(ah); | |
528 | ar9003_mci_2g5g_switch(ah, true); | |
529 | ||
530 | if ((mci_hw->bt_state == MCI_BT_AWAKE) && | |
531 | (mci_hw->query_bt == true)) { | |
532 | mci_hw->need_flush_btinfo = true; | |
533 | } | |
534 | } | |
535 | ||
a3f846f1 SM |
536 | static void ar9003_mci_process_gpm_extra(struct ath_hw *ah, u8 gpm_type, |
537 | u8 gpm_opcode, u32 *p_gpm) | |
538 | { | |
539 | struct ath_common *common = ath9k_hw_common(ah); | |
540 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
541 | u8 *p_data = (u8 *) p_gpm; | |
542 | ||
543 | if (gpm_type != MCI_GPM_COEX_AGENT) | |
544 | return; | |
545 | ||
546 | switch (gpm_opcode) { | |
547 | case MCI_GPM_COEX_VERSION_QUERY: | |
548 | ath_dbg(common, MCI, "MCI Recv GPM COEX Version Query\n"); | |
549 | ar9003_mci_send_coex_version_response(ah, true); | |
550 | break; | |
551 | case MCI_GPM_COEX_VERSION_RESPONSE: | |
552 | ath_dbg(common, MCI, "MCI Recv GPM COEX Version Response\n"); | |
553 | mci->bt_ver_major = | |
554 | *(p_data + MCI_GPM_COEX_B_MAJOR_VERSION); | |
555 | mci->bt_ver_minor = | |
556 | *(p_data + MCI_GPM_COEX_B_MINOR_VERSION); | |
557 | mci->bt_version_known = true; | |
558 | ath_dbg(common, MCI, "MCI BT Coex version: %d.%d\n", | |
559 | mci->bt_ver_major, mci->bt_ver_minor); | |
560 | break; | |
561 | case MCI_GPM_COEX_STATUS_QUERY: | |
562 | ath_dbg(common, MCI, | |
563 | "MCI Recv GPM COEX Status Query = 0x%02X\n", | |
564 | *(p_data + MCI_GPM_COEX_B_WLAN_BITMAP)); | |
565 | mci->wlan_channels_update = true; | |
566 | ar9003_mci_send_coex_wlan_channels(ah, true); | |
567 | break; | |
568 | case MCI_GPM_COEX_BT_PROFILE_INFO: | |
569 | mci->query_bt = true; | |
570 | ath_dbg(common, MCI, "MCI Recv GPM COEX BT_Profile_Info\n"); | |
571 | break; | |
572 | case MCI_GPM_COEX_BT_STATUS_UPDATE: | |
573 | mci->query_bt = true; | |
574 | ath_dbg(common, MCI, | |
575 | "MCI Recv GPM COEX BT_Status_Update SEQ=%d (drop&query)\n", | |
576 | *(p_gpm + 3)); | |
577 | break; | |
578 | default: | |
579 | break; | |
580 | } | |
581 | } | |
582 | ||
583 | static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type, | |
584 | u8 gpm_opcode, int time_out) | |
585 | { | |
586 | struct ath_common *common = ath9k_hw_common(ah); | |
587 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
588 | u32 *p_gpm = NULL, mismatch = 0, more_data; | |
589 | u32 offset; | |
590 | u8 recv_type = 0, recv_opcode = 0; | |
591 | bool b_is_bt_cal_done = (gpm_type == MCI_GPM_BT_CAL_DONE); | |
592 | ||
a3f846f1 SM |
593 | more_data = time_out ? MCI_GPM_NOMORE : MCI_GPM_MORE; |
594 | ||
595 | while (time_out > 0) { | |
596 | if (p_gpm) { | |
597 | MCI_GPM_RECYCLE(p_gpm); | |
598 | p_gpm = NULL; | |
599 | } | |
600 | ||
601 | if (more_data != MCI_GPM_MORE) | |
602 | time_out = ar9003_mci_wait_for_interrupt(ah, | |
603 | AR_MCI_INTERRUPT_RX_MSG_RAW, | |
604 | AR_MCI_INTERRUPT_RX_MSG_GPM, | |
605 | time_out); | |
606 | ||
607 | if (!time_out) | |
608 | break; | |
609 | ||
ad1dc638 | 610 | offset = ar9003_mci_get_next_gpm_offset(ah, &more_data); |
a3f846f1 SM |
611 | |
612 | if (offset == MCI_GPM_INVALID) | |
613 | continue; | |
614 | ||
615 | p_gpm = (u32 *) (mci->gpm_buf + offset); | |
616 | recv_type = MCI_GPM_TYPE(p_gpm); | |
617 | recv_opcode = MCI_GPM_OPCODE(p_gpm); | |
618 | ||
619 | if (MCI_GPM_IS_CAL_TYPE(recv_type)) { | |
a3f846f1 | 620 | if (recv_type == gpm_type) { |
a3f846f1 SM |
621 | if ((gpm_type == MCI_GPM_BT_CAL_DONE) && |
622 | !b_is_bt_cal_done) { | |
623 | gpm_type = MCI_GPM_BT_CAL_GRANT; | |
a3f846f1 SM |
624 | continue; |
625 | } | |
a3f846f1 SM |
626 | break; |
627 | } | |
4f6bd1a8 RM |
628 | } else if ((recv_type == gpm_type) && |
629 | (recv_opcode == gpm_opcode)) | |
a3f846f1 | 630 | break; |
a3f846f1 SM |
631 | |
632 | /* | |
633 | * check if it's cal_grant | |
634 | * | |
635 | * When we're waiting for cal_grant in reset routine, | |
636 | * it's possible that BT sends out cal_request at the | |
637 | * same time. Since BT's calibration doesn't happen | |
638 | * that often, we'll let BT completes calibration then | |
639 | * we continue to wait for cal_grant from BT. | |
640 | * Orginal: Wait BT_CAL_GRANT. | |
641 | * New: Receive BT_CAL_REQ -> send WLAN_CAL_GRANT->wait | |
642 | * BT_CAL_DONE -> Wait BT_CAL_GRANT. | |
643 | */ | |
644 | ||
645 | if ((gpm_type == MCI_GPM_BT_CAL_GRANT) && | |
646 | (recv_type == MCI_GPM_BT_CAL_REQ)) { | |
647 | ||
648 | u32 payload[4] = {0, 0, 0, 0}; | |
649 | ||
650 | gpm_type = MCI_GPM_BT_CAL_DONE; | |
a3f846f1 | 651 | MCI_GPM_SET_CAL_TYPE(payload, |
37cd9d78 | 652 | MCI_GPM_WLAN_CAL_GRANT); |
a3f846f1 SM |
653 | ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, |
654 | false, false); | |
a3f846f1 SM |
655 | continue; |
656 | } else { | |
657 | ath_dbg(common, MCI, "MCI GPM subtype not match 0x%x\n", | |
658 | *(p_gpm + 1)); | |
659 | mismatch++; | |
660 | ar9003_mci_process_gpm_extra(ah, recv_type, | |
37cd9d78 | 661 | recv_opcode, p_gpm); |
a3f846f1 SM |
662 | } |
663 | } | |
37cd9d78 | 664 | |
a3f846f1 SM |
665 | if (p_gpm) { |
666 | MCI_GPM_RECYCLE(p_gpm); | |
667 | p_gpm = NULL; | |
668 | } | |
669 | ||
37cd9d78 | 670 | if (time_out <= 0) |
a3f846f1 | 671 | time_out = 0; |
a3f846f1 SM |
672 | |
673 | while (more_data == MCI_GPM_MORE) { | |
ad1dc638 | 674 | offset = ar9003_mci_get_next_gpm_offset(ah, &more_data); |
a3f846f1 SM |
675 | if (offset == MCI_GPM_INVALID) |
676 | break; | |
677 | ||
678 | p_gpm = (u32 *) (mci->gpm_buf + offset); | |
679 | recv_type = MCI_GPM_TYPE(p_gpm); | |
680 | recv_opcode = MCI_GPM_OPCODE(p_gpm); | |
681 | ||
682 | if (!MCI_GPM_IS_CAL_TYPE(recv_type)) | |
683 | ar9003_mci_process_gpm_extra(ah, recv_type, | |
684 | recv_opcode, p_gpm); | |
685 | ||
686 | MCI_GPM_RECYCLE(p_gpm); | |
687 | } | |
688 | ||
689 | return time_out; | |
690 | } | |
691 | ||
528e5d36 SM |
692 | bool ar9003_mci_start_reset(struct ath_hw *ah, struct ath9k_channel *chan) |
693 | { | |
694 | struct ath_common *common = ath9k_hw_common(ah); | |
695 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; | |
696 | u32 payload[4] = {0, 0, 0, 0}; | |
697 | ||
698 | ar9003_mci_2g5g_changed(ah, IS_CHAN_2GHZ(chan)); | |
699 | ||
700 | if (mci_hw->bt_state != MCI_BT_CAL_START) | |
701 | return false; | |
702 | ||
528e5d36 SM |
703 | mci_hw->bt_state = MCI_BT_CAL; |
704 | ||
705 | /* | |
706 | * MCI FIX: disable mci interrupt here. This is to avoid | |
707 | * SW_MSG_DONE or RX_MSG bits to trigger MCI_INT and | |
708 | * lead to mci_intr reentry. | |
709 | */ | |
528e5d36 SM |
710 | ar9003_mci_disable_interrupt(ah); |
711 | ||
528e5d36 SM |
712 | MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_GRANT); |
713 | ar9003_mci_send_message(ah, MCI_GPM, 0, payload, | |
714 | 16, true, false); | |
715 | ||
528e5d36 SM |
716 | /* Wait BT calibration to be completed for 25ms */ |
717 | ||
718 | if (ar9003_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_DONE, | |
719 | 0, 25000)) | |
37cd9d78 | 720 | ath_dbg(common, MCI, "MCI BT_CAL_DONE received\n"); |
528e5d36 SM |
721 | else |
722 | ath_dbg(common, MCI, | |
37cd9d78 | 723 | "MCI BT_CAL_DONE not received\n"); |
528e5d36 SM |
724 | |
725 | mci_hw->bt_state = MCI_BT_AWAKE; | |
726 | /* MCI FIX: enable mci interrupt here */ | |
727 | ar9003_mci_enable_interrupt(ah); | |
728 | ||
729 | return true; | |
730 | } | |
731 | ||
732 | int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, | |
733 | struct ath9k_hw_cal_data *caldata) | |
734 | { | |
528e5d36 SM |
735 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; |
736 | ||
737 | if (!mci_hw->ready) | |
738 | return 0; | |
739 | ||
740 | if (!IS_CHAN_2GHZ(chan) || (mci_hw->bt_state != MCI_BT_SLEEP)) | |
741 | goto exit; | |
742 | ||
4f6bd1a8 RM |
743 | if (!ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET) && |
744 | !ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)) | |
745 | goto exit; | |
528e5d36 | 746 | |
4f6bd1a8 RM |
747 | /* |
748 | * BT is sleeping. Check if BT wakes up during | |
749 | * WLAN calibration. If BT wakes up during | |
750 | * WLAN calibration, need to go through all | |
751 | * message exchanges again and recal. | |
752 | */ | |
753 | REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, | |
754 | (AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET | | |
755 | AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)); | |
528e5d36 | 756 | |
4f6bd1a8 RM |
757 | ar9003_mci_remote_reset(ah, true); |
758 | ar9003_mci_send_sys_waking(ah, true); | |
759 | udelay(1); | |
528e5d36 | 760 | |
4f6bd1a8 RM |
761 | if (IS_CHAN_2GHZ(chan)) |
762 | ar9003_mci_send_lna_transfer(ah, true); | |
528e5d36 | 763 | |
4f6bd1a8 | 764 | mci_hw->bt_state = MCI_BT_AWAKE; |
528e5d36 | 765 | |
b55f6bb7 BS |
766 | REG_CLR_BIT(ah, AR_PHY_TIMING4, |
767 | 1 << AR_PHY_TIMING_CONTROL4_DO_GAIN_DC_IQ_CAL_SHIFT); | |
768 | ||
4f6bd1a8 | 769 | if (caldata) { |
4b9b42bf SM |
770 | clear_bit(TXIQCAL_DONE, &caldata->cal_flags); |
771 | clear_bit(TXCLCAL_DONE, &caldata->cal_flags); | |
772 | clear_bit(RTT_DONE, &caldata->cal_flags); | |
4f6bd1a8 | 773 | } |
528e5d36 | 774 | |
4f6bd1a8 RM |
775 | if (!ath9k_hw_init_cal(ah, chan)) |
776 | return -EIO; | |
528e5d36 | 777 | |
b55f6bb7 BS |
778 | REG_SET_BIT(ah, AR_PHY_TIMING4, |
779 | 1 << AR_PHY_TIMING_CONTROL4_DO_GAIN_DC_IQ_CAL_SHIFT); | |
780 | ||
528e5d36 SM |
781 | exit: |
782 | ar9003_mci_enable_interrupt(ah); | |
783 | return 0; | |
784 | } | |
785 | ||
a3f846f1 SM |
786 | static void ar9003_mci_mute_bt(struct ath_hw *ah) |
787 | { | |
2f890cab SM |
788 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
789 | ||
a3f846f1 SM |
790 | /* disable all MCI messages */ |
791 | REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE, 0xffff0000); | |
2f890cab SM |
792 | REG_WRITE(ah, AR_BTCOEX_WL_WEIGHTS0, 0xffffffff); |
793 | REG_WRITE(ah, AR_BTCOEX_WL_WEIGHTS1, 0xffffffff); | |
794 | REG_WRITE(ah, AR_BTCOEX_WL_WEIGHTS2, 0xffffffff); | |
795 | REG_WRITE(ah, AR_BTCOEX_WL_WEIGHTS3, 0xffffffff); | |
a3f846f1 SM |
796 | REG_SET_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); |
797 | ||
798 | /* wait pending HW messages to flush out */ | |
799 | udelay(10); | |
800 | ||
801 | /* | |
802 | * Send LNA_TAKE and SYS_SLEEPING when | |
803 | * 1. reset not after resuming from full sleep | |
804 | * 2. before reset MCI RX, to quiet BT and avoid MCI RX misalignment | |
805 | */ | |
2f890cab SM |
806 | if (MCI_ANT_ARCH_PA_LNA_SHARED(mci)) { |
807 | ar9003_mci_send_lna_take(ah, true); | |
808 | udelay(5); | |
809 | } | |
a3f846f1 | 810 | |
a3f846f1 SM |
811 | ar9003_mci_send_sys_sleeping(ah, true); |
812 | } | |
813 | ||
4f851df7 SM |
814 | static void ar9003_mci_osla_setup(struct ath_hw *ah, bool enable) |
815 | { | |
816 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
817 | u32 thresh; | |
818 | ||
4f6bd1a8 | 819 | if (!enable) { |
4f851df7 SM |
820 | REG_CLR_BIT(ah, AR_BTCOEX_CTRL, |
821 | AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); | |
4f6bd1a8 | 822 | return; |
4f851df7 | 823 | } |
4f6bd1a8 RM |
824 | REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, AR_MCI_SCHD_TABLE_2_HW_BASED, 1); |
825 | REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, | |
826 | AR_MCI_SCHD_TABLE_2_MEM_BASED, 1); | |
827 | ||
4c6231a4 RM |
828 | if (AR_SREV_9565(ah)) |
829 | REG_RMW_FIELD(ah, AR_MCI_MISC, AR_MCI_MISC_HW_FIX_EN, 1); | |
830 | ||
4f6bd1a8 RM |
831 | if (!(mci->config & ATH_MCI_CONFIG_DISABLE_AGGR_THRESH)) { |
832 | thresh = MS(mci->config, ATH_MCI_CONFIG_AGGR_THRESH); | |
833 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
834 | AR_BTCOEX_CTRL_AGGR_THRESH, thresh); | |
835 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
836 | AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 1); | |
837 | } else | |
838 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
839 | AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 0); | |
840 | ||
841 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
842 | AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN, 1); | |
4f851df7 SM |
843 | } |
844 | ||
4d9f7c68 SM |
845 | static void ar9003_mci_stat_setup(struct ath_hw *ah) |
846 | { | |
847 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
848 | ||
849 | if (!AR_SREV_9565(ah)) | |
850 | return; | |
851 | ||
852 | if (mci->config & ATH_MCI_CONFIG_MCI_STAT_DBG) { | |
853 | REG_RMW_FIELD(ah, AR_MCI_DBG_CNT_CTRL, | |
854 | AR_MCI_DBG_CNT_CTRL_ENABLE, 1); | |
855 | REG_RMW_FIELD(ah, AR_MCI_DBG_CNT_CTRL, | |
856 | AR_MCI_DBG_CNT_CTRL_BT_LINKID, | |
857 | MCI_STAT_ALL_BT_LINKID); | |
858 | } else { | |
859 | REG_RMW_FIELD(ah, AR_MCI_DBG_CNT_CTRL, | |
860 | AR_MCI_DBG_CNT_CTRL_ENABLE, 0); | |
861 | } | |
862 | } | |
863 | ||
e18e164e SM |
864 | static void ar9003_mci_set_btcoex_ctrl_9565_1ANT(struct ath_hw *ah) |
865 | { | |
866 | u32 regval; | |
867 | ||
868 | regval = SM(1, AR_BTCOEX_CTRL_AR9462_MODE) | | |
869 | SM(1, AR_BTCOEX_CTRL_WBTIMER_EN) | | |
870 | SM(1, AR_BTCOEX_CTRL_PA_SHARED) | | |
871 | SM(1, AR_BTCOEX_CTRL_LNA_SHARED) | | |
872 | SM(1, AR_BTCOEX_CTRL_NUM_ANTENNAS) | | |
873 | SM(1, AR_BTCOEX_CTRL_RX_CHAIN_MASK) | | |
874 | SM(0, AR_BTCOEX_CTRL_1_CHAIN_ACK) | | |
875 | SM(0, AR_BTCOEX_CTRL_1_CHAIN_BCN) | | |
876 | SM(0, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); | |
877 | ||
878 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, | |
879 | AR_BTCOEX_CTRL2_TX_CHAIN_MASK, 0x1); | |
880 | REG_WRITE(ah, AR_BTCOEX_CTRL, regval); | |
881 | } | |
882 | ||
883 | static void ar9003_mci_set_btcoex_ctrl_9565_2ANT(struct ath_hw *ah) | |
884 | { | |
885 | u32 regval; | |
886 | ||
887 | regval = SM(1, AR_BTCOEX_CTRL_AR9462_MODE) | | |
888 | SM(1, AR_BTCOEX_CTRL_WBTIMER_EN) | | |
889 | SM(0, AR_BTCOEX_CTRL_PA_SHARED) | | |
890 | SM(0, AR_BTCOEX_CTRL_LNA_SHARED) | | |
891 | SM(2, AR_BTCOEX_CTRL_NUM_ANTENNAS) | | |
892 | SM(1, AR_BTCOEX_CTRL_RX_CHAIN_MASK) | | |
893 | SM(0, AR_BTCOEX_CTRL_1_CHAIN_ACK) | | |
894 | SM(0, AR_BTCOEX_CTRL_1_CHAIN_BCN) | | |
895 | SM(0, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); | |
896 | ||
897 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, | |
898 | AR_BTCOEX_CTRL2_TX_CHAIN_MASK, 0x0); | |
899 | REG_WRITE(ah, AR_BTCOEX_CTRL, regval); | |
900 | } | |
901 | ||
902 | static void ar9003_mci_set_btcoex_ctrl_9462(struct ath_hw *ah) | |
903 | { | |
904 | u32 regval; | |
905 | ||
906 | regval = SM(1, AR_BTCOEX_CTRL_AR9462_MODE) | | |
907 | SM(1, AR_BTCOEX_CTRL_WBTIMER_EN) | | |
908 | SM(1, AR_BTCOEX_CTRL_PA_SHARED) | | |
909 | SM(1, AR_BTCOEX_CTRL_LNA_SHARED) | | |
910 | SM(2, AR_BTCOEX_CTRL_NUM_ANTENNAS) | | |
911 | SM(3, AR_BTCOEX_CTRL_RX_CHAIN_MASK) | | |
912 | SM(0, AR_BTCOEX_CTRL_1_CHAIN_ACK) | | |
913 | SM(0, AR_BTCOEX_CTRL_1_CHAIN_BCN) | | |
914 | SM(0, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); | |
915 | ||
916 | REG_WRITE(ah, AR_BTCOEX_CTRL, regval); | |
917 | } | |
918 | ||
69c6ac60 SM |
919 | int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, |
920 | bool is_full_sleep) | |
bbefb871 MSS |
921 | { |
922 | struct ath_common *common = ath9k_hw_common(ah); | |
923 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
e82cb03f | 924 | u32 regval, i; |
bbefb871 | 925 | |
4f851df7 | 926 | ath_dbg(common, MCI, "MCI Reset (full_sleep = %d, is_2g = %d)\n", |
bbefb871 MSS |
927 | is_full_sleep, is_2g); |
928 | ||
bbefb871 | 929 | if (REG_READ(ah, AR_BTCOEX_CTRL) == 0xdeadbeef) { |
69c6ac60 SM |
930 | ath_err(common, "BTCOEX control register is dead\n"); |
931 | return -EINVAL; | |
bbefb871 MSS |
932 | } |
933 | ||
934 | /* Program MCI DMA related registers */ | |
935 | REG_WRITE(ah, AR_MCI_GPM_0, mci->gpm_addr); | |
936 | REG_WRITE(ah, AR_MCI_GPM_1, mci->gpm_len); | |
937 | REG_WRITE(ah, AR_MCI_SCHD_TABLE_0, mci->sched_addr); | |
938 | ||
939 | /* | |
940 | * To avoid MCI state machine be affected by incoming remote MCI msgs, | |
941 | * MCI mode will be enabled later, right before reset the MCI TX and RX. | |
942 | */ | |
d9575dad | 943 | if (AR_SREV_9565(ah)) { |
e18e164e SM |
944 | u8 ant = MS(mci->config, ATH_MCI_CONFIG_ANT_ARCH); |
945 | ||
946 | if (ant == ATH_MCI_ANT_ARCH_1_ANT_PA_LNA_SHARED) | |
947 | ar9003_mci_set_btcoex_ctrl_9565_1ANT(ah); | |
948 | else | |
949 | ar9003_mci_set_btcoex_ctrl_9565_2ANT(ah); | |
d9575dad | 950 | } else { |
e18e164e | 951 | ar9003_mci_set_btcoex_ctrl_9462(ah); |
d9575dad | 952 | } |
bbefb871 | 953 | |
4f851df7 SM |
954 | if (is_2g && !(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) |
955 | ar9003_mci_osla_setup(ah, true); | |
956 | else | |
957 | ar9003_mci_osla_setup(ah, false); | |
958 | ||
959 | REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, | |
960 | AR_BTCOEX_CTRL_SPDT_ENABLE); | |
961 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL3, | |
962 | AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT, 20); | |
bbefb871 | 963 | |
e75d4ed6 | 964 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_RX_DEWEIGHT, 0); |
bbefb871 MSS |
965 | REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); |
966 | ||
e75d4ed6 RM |
967 | /* Set the time out to 3.125ms (5 BT slots) */ |
968 | REG_RMW_FIELD(ah, AR_BTCOEX_WL_LNA, AR_BTCOEX_WL_LNA_TIMEOUT, 0x3D090); | |
969 | ||
e82cb03f RM |
970 | /* concurrent tx priority */ |
971 | if (mci->config & ATH_MCI_CONFIG_CONCUR_TX) { | |
972 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, | |
973 | AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE, 0); | |
974 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, | |
975 | AR_BTCOEX_CTRL2_TXPWR_THRESH, 0x7f); | |
976 | REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, | |
977 | AR_BTCOEX_CTRL_REDUCE_TXPWR, 0); | |
978 | for (i = 0; i < 8; i++) | |
979 | REG_WRITE(ah, AR_BTCOEX_MAX_TXPWR(i), 0x7f7f7f7f); | |
980 | } | |
981 | ||
4f851df7 SM |
982 | regval = MS(mci->config, ATH_MCI_CONFIG_CLK_DIV); |
983 | REG_RMW_FIELD(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_CLK_DIV, regval); | |
bbefb871 MSS |
984 | REG_SET_BIT(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_MCI_MODE_EN); |
985 | ||
986 | /* Resetting the Rx and Tx paths of MCI */ | |
987 | regval = REG_READ(ah, AR_MCI_COMMAND2); | |
988 | regval |= SM(1, AR_MCI_COMMAND2_RESET_TX); | |
989 | REG_WRITE(ah, AR_MCI_COMMAND2, regval); | |
990 | ||
991 | udelay(1); | |
992 | ||
993 | regval &= ~SM(1, AR_MCI_COMMAND2_RESET_TX); | |
994 | REG_WRITE(ah, AR_MCI_COMMAND2, regval); | |
995 | ||
996 | if (is_full_sleep) { | |
997 | ar9003_mci_mute_bt(ah); | |
998 | udelay(100); | |
999 | } | |
1000 | ||
3863495b | 1001 | /* Check pending GPM msg before MCI Reset Rx */ |
506847ad | 1002 | ar9003_mci_check_gpm_offset(ah); |
3863495b | 1003 | |
bbefb871 MSS |
1004 | regval |= SM(1, AR_MCI_COMMAND2_RESET_RX); |
1005 | REG_WRITE(ah, AR_MCI_COMMAND2, regval); | |
1006 | udelay(1); | |
1007 | regval &= ~SM(1, AR_MCI_COMMAND2_RESET_RX); | |
1008 | REG_WRITE(ah, AR_MCI_COMMAND2, regval); | |
1009 | ||
ad1dc638 SM |
1010 | /* Init GPM offset after MCI Reset Rx */ |
1011 | ar9003_mci_state(ah, MCI_STATE_INIT_GPM_OFFSET); | |
4f851df7 | 1012 | |
bbefb871 MSS |
1013 | REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE, |
1014 | (SM(0xe801, AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR) | | |
1015 | SM(0x0000, AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM))); | |
1016 | ||
d808ecd8 SM |
1017 | if (MCI_ANT_ARCH_PA_LNA_SHARED(mci)) |
1018 | REG_CLR_BIT(ah, AR_MCI_TX_CTRL, | |
1019 | AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); | |
1020 | else | |
1021 | REG_SET_BIT(ah, AR_MCI_TX_CTRL, | |
1022 | AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); | |
bbefb871 | 1023 | |
4f851df7 | 1024 | ar9003_mci_observation_set_up(ah); |
bbefb871 MSS |
1025 | |
1026 | mci->ready = true; | |
1027 | ar9003_mci_prep_interface(ah); | |
4d9f7c68 | 1028 | ar9003_mci_stat_setup(ah); |
bbefb871 MSS |
1029 | |
1030 | if (en_int) | |
1031 | ar9003_mci_enable_interrupt(ah); | |
69c6ac60 | 1032 | |
7644317b SM |
1033 | if (ath9k_hw_is_aic_enabled(ah)) |
1034 | ar9003_aic_start_normal(ah); | |
1035 | ||
69c6ac60 | 1036 | return 0; |
bbefb871 MSS |
1037 | } |
1038 | ||
528e5d36 SM |
1039 | void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep) |
1040 | { | |
1041 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; | |
1042 | ||
1043 | ar9003_mci_disable_interrupt(ah); | |
1044 | ||
1045 | if (mci_hw->ready && !save_fullsleep) { | |
1046 | ar9003_mci_mute_bt(ah); | |
1047 | udelay(20); | |
1048 | REG_WRITE(ah, AR_BTCOEX_CTRL, 0); | |
1049 | } | |
1050 | ||
1051 | mci_hw->bt_state = MCI_BT_SLEEP; | |
1052 | mci_hw->ready = false; | |
1053 | } | |
1054 | ||
bbefb871 MSS |
1055 | static void ar9003_mci_send_2g5g_status(struct ath_hw *ah, bool wait_done) |
1056 | { | |
bbefb871 MSS |
1057 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
1058 | u32 new_flags, to_set, to_clear; | |
1059 | ||
4f6bd1a8 RM |
1060 | if (!mci->update_2g5g || (mci->bt_state == MCI_BT_SLEEP)) |
1061 | return; | |
bbefb871 | 1062 | |
4f6bd1a8 RM |
1063 | if (mci->is_2g) { |
1064 | new_flags = MCI_2G_FLAGS; | |
1065 | to_clear = MCI_2G_FLAGS_CLEAR_MASK; | |
1066 | to_set = MCI_2G_FLAGS_SET_MASK; | |
1067 | } else { | |
1068 | new_flags = MCI_5G_FLAGS; | |
1069 | to_clear = MCI_5G_FLAGS_CLEAR_MASK; | |
1070 | to_set = MCI_5G_FLAGS_SET_MASK; | |
1071 | } | |
1072 | ||
1073 | if (to_clear) | |
1074 | ar9003_mci_send_coex_bt_flags(ah, wait_done, | |
37cd9d78 SM |
1075 | MCI_GPM_COEX_BT_FLAGS_CLEAR, |
1076 | to_clear); | |
4f6bd1a8 RM |
1077 | if (to_set) |
1078 | ar9003_mci_send_coex_bt_flags(ah, wait_done, | |
37cd9d78 SM |
1079 | MCI_GPM_COEX_BT_FLAGS_SET, |
1080 | to_set); | |
bbefb871 MSS |
1081 | } |
1082 | ||
1083 | static void ar9003_mci_queue_unsent_gpm(struct ath_hw *ah, u8 header, | |
1084 | u32 *payload, bool queue) | |
1085 | { | |
bbefb871 MSS |
1086 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
1087 | u8 type, opcode; | |
1088 | ||
bbefb871 MSS |
1089 | /* check if the message is to be queued */ |
1090 | if (header != MCI_GPM) | |
1091 | return; | |
1092 | ||
1093 | type = MCI_GPM_TYPE(payload); | |
1094 | opcode = MCI_GPM_OPCODE(payload); | |
1095 | ||
1096 | if (type != MCI_GPM_COEX_AGENT) | |
1097 | return; | |
1098 | ||
1099 | switch (opcode) { | |
1100 | case MCI_GPM_COEX_BT_UPDATE_FLAGS: | |
bbefb871 | 1101 | if (*(((u8 *)payload) + MCI_GPM_COEX_B_BT_FLAGS_OP) == |
c91ec465 | 1102 | MCI_GPM_COEX_BT_FLAGS_READ) |
bbefb871 MSS |
1103 | break; |
1104 | ||
1105 | mci->update_2g5g = queue; | |
1106 | ||
bbefb871 | 1107 | break; |
bbefb871 | 1108 | case MCI_GPM_COEX_WLAN_CHANNELS: |
bbefb871 | 1109 | mci->wlan_channels_update = queue; |
bbefb871 | 1110 | break; |
bbefb871 | 1111 | case MCI_GPM_COEX_HALT_BT_GPM: |
bbefb871 | 1112 | if (*(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) == |
37cd9d78 | 1113 | MCI_GPM_COEX_BT_GPM_UNHALT) { |
bbefb871 MSS |
1114 | mci->unhalt_bt_gpm = queue; |
1115 | ||
37cd9d78 | 1116 | if (!queue) |
bbefb871 | 1117 | mci->halted_bt_gpm = false; |
bbefb871 MSS |
1118 | } |
1119 | ||
1120 | if (*(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) == | |
1121 | MCI_GPM_COEX_BT_GPM_HALT) { | |
1122 | ||
1123 | mci->halted_bt_gpm = !queue; | |
bbefb871 MSS |
1124 | } |
1125 | ||
1126 | break; | |
1127 | default: | |
1128 | break; | |
1129 | } | |
1130 | } | |
1131 | ||
1bde95fa | 1132 | void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool force) |
bbefb871 | 1133 | { |
bbefb871 MSS |
1134 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
1135 | ||
1bde95fa | 1136 | if (!mci->update_2g5g && !force) |
4f6bd1a8 | 1137 | return; |
bbefb871 | 1138 | |
4f6bd1a8 | 1139 | if (mci->is_2g) { |
83bfea42 RM |
1140 | ar9003_mci_send_2g5g_status(ah, true); |
1141 | ar9003_mci_send_lna_transfer(ah, true); | |
1142 | udelay(5); | |
4ff6a9d2 RM |
1143 | |
1144 | REG_CLR_BIT(ah, AR_MCI_TX_CTRL, | |
4f6bd1a8 RM |
1145 | AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); |
1146 | REG_CLR_BIT(ah, AR_PHY_GLB_CONTROL, | |
1147 | AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); | |
1148 | ||
1149 | if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) | |
1bde95fa | 1150 | ar9003_mci_osla_setup(ah, true); |
7d47884f RM |
1151 | |
1152 | if (AR_SREV_9462(ah)) | |
1153 | REG_WRITE(ah, AR_SELFGEN_MASK, 0x02); | |
4f6bd1a8 | 1154 | } else { |
83bfea42 RM |
1155 | ar9003_mci_send_lna_take(ah, true); |
1156 | udelay(5); | |
4ff6a9d2 | 1157 | |
4f6bd1a8 RM |
1158 | REG_SET_BIT(ah, AR_MCI_TX_CTRL, |
1159 | AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); | |
1160 | REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, | |
1161 | AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); | |
4f6bd1a8 | 1162 | |
1bde95fa | 1163 | ar9003_mci_osla_setup(ah, false); |
83bfea42 | 1164 | ar9003_mci_send_2g5g_status(ah, true); |
bbefb871 MSS |
1165 | } |
1166 | } | |
1167 | ||
1168 | bool ar9003_mci_send_message(struct ath_hw *ah, u8 header, u32 flag, | |
1169 | u32 *payload, u8 len, bool wait_done, | |
1170 | bool check_bt) | |
1171 | { | |
1172 | struct ath_common *common = ath9k_hw_common(ah); | |
1173 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1174 | bool msg_sent = false; | |
1175 | u32 regval; | |
1176 | u32 saved_mci_int_en; | |
1177 | int i; | |
1178 | ||
1179 | saved_mci_int_en = REG_READ(ah, AR_MCI_INTERRUPT_EN); | |
1180 | regval = REG_READ(ah, AR_BTCOEX_CTRL); | |
1181 | ||
1182 | if ((regval == 0xdeadbeef) || !(regval & AR_BTCOEX_CTRL_MCI_MODE_EN)) { | |
d2182b69 JP |
1183 | ath_dbg(common, MCI, |
1184 | "MCI Not sending 0x%x. MCI is not enabled. full_sleep = %d\n", | |
37cd9d78 | 1185 | header, (ah->power_mode == ATH9K_PM_FULL_SLEEP) ? 1 : 0); |
bbefb871 MSS |
1186 | ar9003_mci_queue_unsent_gpm(ah, header, payload, true); |
1187 | return false; | |
bbefb871 | 1188 | } else if (check_bt && (mci->bt_state == MCI_BT_SLEEP)) { |
d2182b69 JP |
1189 | ath_dbg(common, MCI, |
1190 | "MCI Don't send message 0x%x. BT is in sleep state\n", | |
1191 | header); | |
bbefb871 MSS |
1192 | ar9003_mci_queue_unsent_gpm(ah, header, payload, true); |
1193 | return false; | |
1194 | } | |
1195 | ||
1196 | if (wait_done) | |
1197 | REG_WRITE(ah, AR_MCI_INTERRUPT_EN, 0); | |
1198 | ||
1199 | /* Need to clear SW_MSG_DONE raw bit before wait */ | |
1200 | ||
1201 | REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, | |
1202 | (AR_MCI_INTERRUPT_SW_MSG_DONE | | |
1203 | AR_MCI_INTERRUPT_MSG_FAIL_MASK)); | |
1204 | ||
1205 | if (payload) { | |
1206 | for (i = 0; (i * 4) < len; i++) | |
1207 | REG_WRITE(ah, (AR_MCI_TX_PAYLOAD0 + i * 4), | |
1208 | *(payload + i)); | |
1209 | } | |
1210 | ||
1211 | REG_WRITE(ah, AR_MCI_COMMAND0, | |
1212 | (SM((flag & MCI_FLAG_DISABLE_TIMESTAMP), | |
1213 | AR_MCI_COMMAND0_DISABLE_TIMESTAMP) | | |
1214 | SM(len, AR_MCI_COMMAND0_LEN) | | |
1215 | SM(header, AR_MCI_COMMAND0_HEADER))); | |
1216 | ||
1217 | if (wait_done && | |
1218 | !(ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RAW, | |
37cd9d78 | 1219 | AR_MCI_INTERRUPT_SW_MSG_DONE, 500))) |
bbefb871 MSS |
1220 | ar9003_mci_queue_unsent_gpm(ah, header, payload, true); |
1221 | else { | |
1222 | ar9003_mci_queue_unsent_gpm(ah, header, payload, false); | |
1223 | msg_sent = true; | |
1224 | } | |
1225 | ||
1226 | if (wait_done) | |
1227 | REG_WRITE(ah, AR_MCI_INTERRUPT_EN, saved_mci_int_en); | |
1228 | ||
1229 | return msg_sent; | |
1230 | } | |
1231 | EXPORT_SYMBOL(ar9003_mci_send_message); | |
1232 | ||
f2f408ef SM |
1233 | void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable) |
1234 | { | |
1235 | struct ath_common *common = ath9k_hw_common(ah); | |
1236 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; | |
1237 | u32 pld[4] = {0, 0, 0, 0}; | |
1238 | ||
1239 | if ((mci_hw->bt_state != MCI_BT_AWAKE) || | |
1240 | (mci_hw->config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) | |
1241 | return; | |
1242 | ||
f2f408ef SM |
1243 | MCI_GPM_SET_CAL_TYPE(pld, MCI_GPM_WLAN_CAL_REQ); |
1244 | pld[MCI_GPM_WLAN_CAL_W_SEQUENCE] = mci_hw->wlan_cal_seq++; | |
1245 | ||
1246 | ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, true, false); | |
1247 | ||
f2f408ef | 1248 | if (ar9003_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_GRANT, 0, 50000)) { |
37cd9d78 | 1249 | ath_dbg(common, MCI, "MCI BT_CAL_GRANT received\n"); |
f2f408ef | 1250 | } else { |
2fd5d35b | 1251 | *is_reusable = false; |
37cd9d78 | 1252 | ath_dbg(common, MCI, "MCI BT_CAL_GRANT not received\n"); |
f2f408ef SM |
1253 | } |
1254 | } | |
1255 | ||
1256 | void ar9003_mci_init_cal_done(struct ath_hw *ah) | |
1257 | { | |
f2f408ef SM |
1258 | struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; |
1259 | u32 pld[4] = {0, 0, 0, 0}; | |
1260 | ||
1261 | if ((mci_hw->bt_state != MCI_BT_AWAKE) || | |
1262 | (mci_hw->config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) | |
1263 | return; | |
1264 | ||
f2f408ef SM |
1265 | MCI_GPM_SET_CAL_TYPE(pld, MCI_GPM_WLAN_CAL_DONE); |
1266 | pld[MCI_GPM_WLAN_CAL_W_SEQUENCE] = mci_hw->wlan_cal_done++; | |
1267 | ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, true, false); | |
1268 | } | |
1269 | ||
69c6ac60 SM |
1270 | int ar9003_mci_setup(struct ath_hw *ah, u32 gpm_addr, void *gpm_buf, |
1271 | u16 len, u32 sched_addr) | |
bbefb871 MSS |
1272 | { |
1273 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
bbefb871 MSS |
1274 | |
1275 | mci->gpm_addr = gpm_addr; | |
1276 | mci->gpm_buf = gpm_buf; | |
1277 | mci->gpm_len = len; | |
1278 | mci->sched_addr = sched_addr; | |
bbefb871 | 1279 | |
69c6ac60 | 1280 | return ar9003_mci_reset(ah, true, true, true); |
bbefb871 MSS |
1281 | } |
1282 | EXPORT_SYMBOL(ar9003_mci_setup); | |
1283 | ||
1284 | void ar9003_mci_cleanup(struct ath_hw *ah) | |
1285 | { | |
bbefb871 MSS |
1286 | /* Turn off MCI and Jupiter mode. */ |
1287 | REG_WRITE(ah, AR_BTCOEX_CTRL, 0x00); | |
bbefb871 MSS |
1288 | ar9003_mci_disable_interrupt(ah); |
1289 | } | |
1290 | EXPORT_SYMBOL(ar9003_mci_cleanup); | |
1291 | ||
b98ccec0 | 1292 | u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type) |
bbefb871 | 1293 | { |
bbefb871 | 1294 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; |
2097fdd7 | 1295 | u32 value = 0, tsf; |
bbefb871 MSS |
1296 | u8 query_type; |
1297 | ||
1298 | switch (state_type) { | |
1299 | case MCI_STATE_ENABLE: | |
1300 | if (mci->ready) { | |
bbefb871 MSS |
1301 | value = REG_READ(ah, AR_BTCOEX_CTRL); |
1302 | ||
1303 | if ((value == 0xdeadbeef) || (value == 0xffffffff)) | |
1304 | value = 0; | |
1305 | } | |
1306 | value &= AR_BTCOEX_CTRL_MCI_MODE_EN; | |
4f6bd1a8 | 1307 | break; |
ad1dc638 SM |
1308 | case MCI_STATE_INIT_GPM_OFFSET: |
1309 | value = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); | |
1310 | ||
1311 | if (value < mci->gpm_len) | |
1312 | mci->gpm_idx = value; | |
1313 | else | |
1314 | mci->gpm_idx = 0; | |
1315 | break; | |
bbefb871 MSS |
1316 | case MCI_STATE_LAST_SCHD_MSG_OFFSET: |
1317 | value = MS(REG_READ(ah, AR_MCI_RX_STATUS), | |
1318 | AR_MCI_RX_LAST_SCHD_MSG_INDEX); | |
1319 | /* Make it in bytes */ | |
1320 | value <<= 4; | |
1321 | break; | |
bbefb871 MSS |
1322 | case MCI_STATE_REMOTE_SLEEP: |
1323 | value = MS(REG_READ(ah, AR_MCI_RX_STATUS), | |
1324 | AR_MCI_RX_REMOTE_SLEEP) ? | |
1325 | MCI_BT_SLEEP : MCI_BT_AWAKE; | |
1326 | break; | |
bbefb871 MSS |
1327 | case MCI_STATE_SET_BT_AWAKE: |
1328 | mci->bt_state = MCI_BT_AWAKE; | |
1329 | ar9003_mci_send_coex_version_query(ah, true); | |
1330 | ar9003_mci_send_coex_wlan_channels(ah, true); | |
1331 | ||
37cd9d78 | 1332 | if (mci->unhalt_bt_gpm) |
bbefb871 | 1333 | ar9003_mci_send_coex_halt_bt_gpm(ah, false, true); |
bbefb871 | 1334 | |
1bde95fa | 1335 | ar9003_mci_2g5g_switch(ah, false); |
bbefb871 | 1336 | break; |
bbefb871 MSS |
1337 | case MCI_STATE_RESET_REQ_WAKE: |
1338 | ar9003_mci_reset_req_wakeup(ah); | |
1339 | mci->update_2g5g = true; | |
1340 | ||
0cc4cdeb | 1341 | if (mci->config & ATH_MCI_CONFIG_MCI_OBS_MASK) { |
bbefb871 MSS |
1342 | /* Check if we still have control of the GPIOs */ |
1343 | if ((REG_READ(ah, AR_GLB_GPIO_CONTROL) & | |
37cd9d78 SM |
1344 | ATH_MCI_CONFIG_MCI_OBS_GPIO) != |
1345 | ATH_MCI_CONFIG_MCI_OBS_GPIO) { | |
bbefb871 MSS |
1346 | ar9003_mci_observation_set_up(ah); |
1347 | } | |
1348 | } | |
1349 | break; | |
bbefb871 MSS |
1350 | case MCI_STATE_SEND_WLAN_COEX_VERSION: |
1351 | ar9003_mci_send_coex_version_response(ah, true); | |
1352 | break; | |
bbefb871 MSS |
1353 | case MCI_STATE_SEND_VERSION_QUERY: |
1354 | ar9003_mci_send_coex_version_query(ah, true); | |
1355 | break; | |
bbefb871 | 1356 | case MCI_STATE_SEND_STATUS_QUERY: |
c91ec465 | 1357 | query_type = MCI_GPM_COEX_QUERY_BT_TOPOLOGY; |
bbefb871 MSS |
1358 | ar9003_mci_send_coex_bt_status_query(ah, true, query_type); |
1359 | break; | |
bbefb871 | 1360 | case MCI_STATE_RECOVER_RX: |
2097fdd7 RM |
1361 | tsf = ath9k_hw_gettsf32(ah); |
1362 | if ((tsf - mci->last_recovery) <= MCI_RECOVERY_DUR_TSF) { | |
1363 | ath_dbg(ath9k_hw_common(ah), MCI, | |
1364 | "(MCI) ignore Rx recovery\n"); | |
1365 | break; | |
1366 | } | |
1367 | ath_dbg(ath9k_hw_common(ah), MCI, "(MCI) RECOVER RX\n"); | |
1368 | mci->last_recovery = tsf; | |
bbefb871 MSS |
1369 | ar9003_mci_prep_interface(ah); |
1370 | mci->query_bt = true; | |
1371 | mci->need_flush_btinfo = true; | |
1372 | ar9003_mci_send_coex_wlan_channels(ah, true); | |
1bde95fa | 1373 | ar9003_mci_2g5g_switch(ah, false); |
bbefb871 | 1374 | break; |
bbefb871 MSS |
1375 | case MCI_STATE_NEED_FTP_STOMP: |
1376 | value = !(mci->config & ATH_MCI_CONFIG_DISABLE_FTP_STOMP); | |
1377 | break; | |
d92bb98f RM |
1378 | case MCI_STATE_NEED_FLUSH_BT_INFO: |
1379 | value = (!mci->unhalt_bt_gpm && mci->need_flush_btinfo) ? 1 : 0; | |
1380 | mci->need_flush_btinfo = false; | |
1381 | break; | |
60544603 SM |
1382 | case MCI_STATE_AIC_CAL: |
1383 | if (ath9k_hw_is_aic_enabled(ah)) | |
1384 | value = ar9003_aic_calibration(ah); | |
1385 | break; | |
f2ef792a SM |
1386 | case MCI_STATE_AIC_START: |
1387 | if (ath9k_hw_is_aic_enabled(ah)) | |
1388 | ar9003_aic_start_normal(ah); | |
1389 | break; | |
958b6827 SM |
1390 | case MCI_STATE_AIC_CAL_RESET: |
1391 | if (ath9k_hw_is_aic_enabled(ah)) | |
1392 | value = ar9003_aic_cal_reset(ah); | |
1393 | break; | |
b6ab9ae2 | 1394 | case MCI_STATE_AIC_CAL_SINGLE: |
208837ee SM |
1395 | if (ath9k_hw_is_aic_enabled(ah)) |
1396 | value = ar9003_aic_calibration_single(ah); | |
b6ab9ae2 | 1397 | break; |
bbefb871 MSS |
1398 | default: |
1399 | break; | |
bbefb871 MSS |
1400 | } |
1401 | ||
1402 | return value; | |
1403 | } | |
1404 | EXPORT_SYMBOL(ar9003_mci_state); | |
99922a45 RM |
1405 | |
1406 | void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah) | |
1407 | { | |
1408 | struct ath_common *common = ath9k_hw_common(ah); | |
1409 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1410 | ||
1411 | ath_dbg(common, MCI, "Give LNA and SPDT control to BT\n"); | |
1412 | ||
4ff6a9d2 RM |
1413 | ar9003_mci_send_lna_take(ah, true); |
1414 | udelay(50); | |
1415 | ||
99922a45 RM |
1416 | REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); |
1417 | mci->is_2g = false; | |
1418 | mci->update_2g5g = true; | |
1419 | ar9003_mci_send_2g5g_status(ah, true); | |
1420 | ||
1421 | /* Force another 2g5g update at next scanning */ | |
1422 | mci->update_2g5g = true; | |
1423 | } | |
9dd9b0dc RM |
1424 | |
1425 | void ar9003_mci_set_power_awake(struct ath_hw *ah) | |
1426 | { | |
1427 | u32 btcoex_ctrl2, diag_sw; | |
1428 | int i; | |
1429 | u8 lna_ctrl, bt_sleep; | |
1430 | ||
1431 | for (i = 0; i < AH_WAIT_TIMEOUT; i++) { | |
1432 | btcoex_ctrl2 = REG_READ(ah, AR_BTCOEX_CTRL2); | |
1433 | if (btcoex_ctrl2 != 0xdeadbeef) | |
1434 | break; | |
1435 | udelay(AH_TIME_QUANTUM); | |
1436 | } | |
1437 | REG_WRITE(ah, AR_BTCOEX_CTRL2, (btcoex_ctrl2 | BIT(23))); | |
1438 | ||
1439 | for (i = 0; i < AH_WAIT_TIMEOUT; i++) { | |
1440 | diag_sw = REG_READ(ah, AR_DIAG_SW); | |
1441 | if (diag_sw != 0xdeadbeef) | |
1442 | break; | |
1443 | udelay(AH_TIME_QUANTUM); | |
1444 | } | |
1445 | REG_WRITE(ah, AR_DIAG_SW, (diag_sw | BIT(27) | BIT(19) | BIT(18))); | |
1446 | lna_ctrl = REG_READ(ah, AR_OBS_BUS_CTRL) & 0x3; | |
a50d1fd4 | 1447 | bt_sleep = MS(REG_READ(ah, AR_MCI_RX_STATUS), AR_MCI_RX_REMOTE_SLEEP); |
9dd9b0dc RM |
1448 | |
1449 | REG_WRITE(ah, AR_BTCOEX_CTRL2, btcoex_ctrl2); | |
1450 | REG_WRITE(ah, AR_DIAG_SW, diag_sw); | |
1451 | ||
1452 | if (bt_sleep && (lna_ctrl == 2)) { | |
1453 | REG_SET_BIT(ah, AR_BTCOEX_RC, 0x1); | |
1454 | REG_CLR_BIT(ah, AR_BTCOEX_RC, 0x1); | |
1455 | udelay(50); | |
1456 | } | |
1457 | } | |
506847ad RM |
1458 | |
1459 | void ar9003_mci_check_gpm_offset(struct ath_hw *ah) | |
1460 | { | |
1461 | struct ath_common *common = ath9k_hw_common(ah); | |
1462 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1463 | u32 offset; | |
1464 | ||
1465 | /* | |
1466 | * This should only be called before "MAC Warm Reset" or "MCI Reset Rx". | |
1467 | */ | |
1468 | offset = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); | |
1469 | if (mci->gpm_idx == offset) | |
1470 | return; | |
1471 | ath_dbg(common, MCI, "GPM cached write pointer mismatch %d %d\n", | |
1472 | mci->gpm_idx, offset); | |
1473 | mci->query_bt = true; | |
1474 | mci->need_flush_btinfo = true; | |
1475 | mci->gpm_idx = 0; | |
1476 | } | |
1477 | ||
ad1dc638 | 1478 | u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, u32 *more) |
506847ad RM |
1479 | { |
1480 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1481 | u32 offset, more_gpm = 0, gpm_ptr; | |
1482 | ||
506847ad RM |
1483 | /* |
1484 | * This could be useful to avoid new GPM message interrupt which | |
1485 | * may lead to spurious interrupt after power sleep, or multiple | |
1486 | * entry of ath_mci_intr(). | |
1487 | * Adding empty GPM check by returning HAL_MCI_GPM_INVALID can | |
1488 | * alleviate this effect, but clearing GPM RX interrupt bit is | |
1489 | * safe, because whether this is called from hw or driver code | |
1490 | * there must be an interrupt bit set/triggered initially | |
1491 | */ | |
1492 | REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, | |
1493 | AR_MCI_INTERRUPT_RX_MSG_GPM); | |
1494 | ||
1495 | gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); | |
1496 | offset = gpm_ptr; | |
1497 | ||
1498 | if (!offset) | |
1499 | offset = mci->gpm_len - 1; | |
1500 | else if (offset >= mci->gpm_len) { | |
1501 | if (offset != 0xFFFF) | |
1502 | offset = 0; | |
1503 | } else { | |
1504 | offset--; | |
1505 | } | |
1506 | ||
1507 | if ((offset == 0xFFFF) || (gpm_ptr == mci->gpm_idx)) { | |
1508 | offset = MCI_GPM_INVALID; | |
1509 | more_gpm = MCI_GPM_NOMORE; | |
1510 | goto out; | |
1511 | } | |
1512 | for (;;) { | |
1513 | u32 temp_index; | |
1514 | ||
1515 | /* skip reserved GPM if any */ | |
1516 | ||
1517 | if (offset != mci->gpm_idx) | |
1518 | more_gpm = MCI_GPM_MORE; | |
1519 | else | |
1520 | more_gpm = MCI_GPM_NOMORE; | |
1521 | ||
1522 | temp_index = mci->gpm_idx; | |
90be994c MSS |
1523 | |
1524 | if (temp_index >= mci->gpm_len) | |
1525 | temp_index = 0; | |
1526 | ||
506847ad RM |
1527 | mci->gpm_idx++; |
1528 | ||
1529 | if (mci->gpm_idx >= mci->gpm_len) | |
1530 | mci->gpm_idx = 0; | |
1531 | ||
1532 | if (ar9003_mci_is_gpm_valid(ah, temp_index)) { | |
1533 | offset = temp_index; | |
1534 | break; | |
1535 | } | |
1536 | ||
1537 | if (more_gpm == MCI_GPM_NOMORE) { | |
1538 | offset = MCI_GPM_INVALID; | |
1539 | break; | |
1540 | } | |
1541 | } | |
1542 | ||
1543 | if (offset != MCI_GPM_INVALID) | |
1544 | offset <<= 4; | |
1545 | out: | |
1546 | if (more) | |
1547 | *more = more_gpm; | |
1548 | ||
1549 | return offset; | |
1550 | } | |
1551 | EXPORT_SYMBOL(ar9003_mci_get_next_gpm_offset); | |
e1763d3f RM |
1552 | |
1553 | void ar9003_mci_set_bt_version(struct ath_hw *ah, u8 major, u8 minor) | |
1554 | { | |
1555 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1556 | ||
1557 | mci->bt_ver_major = major; | |
1558 | mci->bt_ver_minor = minor; | |
1559 | mci->bt_version_known = true; | |
1560 | ath_dbg(ath9k_hw_common(ah), MCI, "MCI BT version set: %d.%d\n", | |
1561 | mci->bt_ver_major, mci->bt_ver_minor); | |
1562 | } | |
1563 | EXPORT_SYMBOL(ar9003_mci_set_bt_version); | |
2d340ac8 RM |
1564 | |
1565 | void ar9003_mci_send_wlan_channels(struct ath_hw *ah) | |
1566 | { | |
1567 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | |
1568 | ||
1569 | mci->wlan_channels_update = true; | |
1570 | ar9003_mci_send_coex_wlan_channels(ah, true); | |
1571 | } | |
1572 | EXPORT_SYMBOL(ar9003_mci_send_wlan_channels); | |
e82cb03f RM |
1573 | |
1574 | u16 ar9003_mci_get_max_txpower(struct ath_hw *ah, u8 ctlmode) | |
1575 | { | |
1576 | if (!ah->btcoex_hw.mci.concur_tx) | |
1577 | goto out; | |
1578 | ||
1579 | if (ctlmode == CTL_2GHT20) | |
1580 | return ATH_BTCOEX_HT20_MAX_TXPOWER; | |
1581 | else if (ctlmode == CTL_2GHT40) | |
1582 | return ATH_BTCOEX_HT40_MAX_TXPOWER; | |
1583 | ||
1584 | out: | |
1585 | return -1; | |
1586 | } |