diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/link.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/link.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c new file mode 100644 index 000000000000..7368b9630b99 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/link.c | |||
@@ -0,0 +1,497 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Qualcomm Atheros, 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 "ath9k.h" | ||
18 | |||
19 | /* | ||
20 | * TX polling - checks if the TX engine is stuck somewhere | ||
21 | * and issues a chip reset if so. | ||
22 | */ | ||
23 | void ath_tx_complete_poll_work(struct work_struct *work) | ||
24 | { | ||
25 | struct ath_softc *sc = container_of(work, struct ath_softc, | ||
26 | tx_complete_work.work); | ||
27 | struct ath_txq *txq; | ||
28 | int i; | ||
29 | bool needreset = false; | ||
30 | #ifdef CONFIG_ATH9K_DEBUGFS | ||
31 | sc->tx_complete_poll_work_seen++; | ||
32 | #endif | ||
33 | |||
34 | for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) | ||
35 | if (ATH_TXQ_SETUP(sc, i)) { | ||
36 | txq = &sc->tx.txq[i]; | ||
37 | ath_txq_lock(sc, txq); | ||
38 | if (txq->axq_depth) { | ||
39 | if (txq->axq_tx_inprogress) { | ||
40 | needreset = true; | ||
41 | ath_txq_unlock(sc, txq); | ||
42 | break; | ||
43 | } else { | ||
44 | txq->axq_tx_inprogress = true; | ||
45 | } | ||
46 | } | ||
47 | ath_txq_unlock_complete(sc, txq); | ||
48 | } | ||
49 | |||
50 | if (needreset) { | ||
51 | ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, | ||
52 | "tx hung, resetting the chip\n"); | ||
53 | RESET_STAT_INC(sc, RESET_TYPE_TX_HANG); | ||
54 | ieee80211_queue_work(sc->hw, &sc->hw_reset_work); | ||
55 | } | ||
56 | |||
57 | ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, | ||
58 | msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT)); | ||
59 | } | ||
60 | |||
61 | /* | ||
62 | * Checks if the BB/MAC is hung. | ||
63 | */ | ||
64 | void ath_hw_check(struct work_struct *work) | ||
65 | { | ||
66 | struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work); | ||
67 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); | ||
68 | unsigned long flags; | ||
69 | int busy; | ||
70 | u8 is_alive, nbeacon = 1; | ||
71 | |||
72 | ath9k_ps_wakeup(sc); | ||
73 | is_alive = ath9k_hw_check_alive(sc->sc_ah); | ||
74 | |||
75 | if (is_alive && !AR_SREV_9300(sc->sc_ah)) | ||
76 | goto out; | ||
77 | else if (!is_alive && AR_SREV_9300(sc->sc_ah)) { | ||
78 | ath_dbg(common, RESET, | ||
79 | "DCU stuck is detected. Schedule chip reset\n"); | ||
80 | RESET_STAT_INC(sc, RESET_TYPE_MAC_HANG); | ||
81 | goto sched_reset; | ||
82 | } | ||
83 | |||
84 | spin_lock_irqsave(&common->cc_lock, flags); | ||
85 | busy = ath_update_survey_stats(sc); | ||
86 | spin_unlock_irqrestore(&common->cc_lock, flags); | ||
87 | |||
88 | ath_dbg(common, RESET, "Possible baseband hang, busy=%d (try %d)\n", | ||
89 | busy, sc->hw_busy_count + 1); | ||
90 | if (busy >= 99) { | ||
91 | if (++sc->hw_busy_count >= 3) { | ||
92 | RESET_STAT_INC(sc, RESET_TYPE_BB_HANG); | ||
93 | goto sched_reset; | ||
94 | } | ||
95 | } else if (busy >= 0) { | ||
96 | sc->hw_busy_count = 0; | ||
97 | nbeacon = 3; | ||
98 | } | ||
99 | |||
100 | ath_start_rx_poll(sc, nbeacon); | ||
101 | goto out; | ||
102 | |||
103 | sched_reset: | ||
104 | ieee80211_queue_work(sc->hw, &sc->hw_reset_work); | ||
105 | out: | ||
106 | ath9k_ps_restore(sc); | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * PLL-WAR for AR9485. | ||
111 | */ | ||
112 | static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) | ||
113 | { | ||
114 | static int count; | ||
115 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); | ||
116 | |||
117 | if (pll_sqsum >= 0x40000) { | ||
118 | count++; | ||
119 | if (count == 3) { | ||
120 | /* Rx is hung for more than 500ms. Reset it */ | ||
121 | ath_dbg(common, RESET, "Possible RX hang, resetting\n"); | ||
122 | RESET_STAT_INC(sc, RESET_TYPE_PLL_HANG); | ||
123 | ieee80211_queue_work(sc->hw, &sc->hw_reset_work); | ||
124 | count = 0; | ||
125 | } | ||
126 | } else | ||
127 | count = 0; | ||
128 | } | ||
129 | |||
130 | void ath_hw_pll_work(struct work_struct *work) | ||
131 | { | ||
132 | struct ath_softc *sc = container_of(work, struct ath_softc, | ||
133 | hw_pll_work.work); | ||
134 | u32 pll_sqsum; | ||
135 | |||
136 | if (AR_SREV_9485(sc->sc_ah)) { | ||
137 | ath9k_ps_wakeup(sc); | ||
138 | pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); | ||
139 | ath9k_ps_restore(sc); | ||
140 | ath_hw_pll_rx_hang_check(sc, pll_sqsum); | ||
141 | ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * RX Polling - monitors baseband hangs. | ||
147 | */ | ||
148 | void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon) | ||
149 | { | ||
150 | if (!AR_SREV_9300(sc->sc_ah)) | ||
151 | return; | ||
152 | |||
153 | if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF)) | ||
154 | return; | ||
155 | |||
156 | mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies | ||
157 | (nbeacon * sc->cur_beacon_conf.beacon_interval)); | ||
158 | } | ||
159 | |||
160 | void ath_rx_poll(unsigned long data) | ||
161 | { | ||
162 | struct ath_softc *sc = (struct ath_softc *)data; | ||
163 | |||
164 | ieee80211_queue_work(sc->hw, &sc->hw_check_work); | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * PA Pre-distortion. | ||
169 | */ | ||
170 | static void ath_paprd_activate(struct ath_softc *sc) | ||
171 | { | ||
172 | struct ath_hw *ah = sc->sc_ah; | ||
173 | struct ath9k_hw_cal_data *caldata = ah->caldata; | ||
174 | int chain; | ||
175 | |||
176 | if (!caldata || !caldata->paprd_done) | ||
177 | return; | ||
178 | |||
179 | ath9k_ps_wakeup(sc); | ||
180 | ar9003_paprd_enable(ah, false); | ||
181 | for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { | ||
182 | if (!(ah->txchainmask & BIT(chain))) | ||
183 | continue; | ||
184 | |||
185 | ar9003_paprd_populate_single_table(ah, caldata, chain); | ||
186 | } | ||
187 | |||
188 | ar9003_paprd_enable(ah, true); | ||
189 | ath9k_ps_restore(sc); | ||
190 | } | ||
191 | |||
192 | static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain) | ||
193 | { | ||
194 | struct ieee80211_hw *hw = sc->hw; | ||
195 | struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | ||
196 | struct ath_hw *ah = sc->sc_ah; | ||
197 | struct ath_common *common = ath9k_hw_common(ah); | ||
198 | struct ath_tx_control txctl; | ||
199 | int time_left; | ||
200 | |||
201 | memset(&txctl, 0, sizeof(txctl)); | ||
202 | txctl.txq = sc->tx.txq_map[WME_AC_BE]; | ||
203 | |||
204 | memset(tx_info, 0, sizeof(*tx_info)); | ||
205 | tx_info->band = hw->conf.channel->band; | ||
206 | tx_info->flags |= IEEE80211_TX_CTL_NO_ACK; | ||
207 | tx_info->control.rates[0].idx = 0; | ||
208 | tx_info->control.rates[0].count = 1; | ||
209 | tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS; | ||
210 | tx_info->control.rates[1].idx = -1; | ||
211 | |||
212 | init_completion(&sc->paprd_complete); | ||
213 | txctl.paprd = BIT(chain); | ||
214 | |||
215 | if (ath_tx_start(hw, skb, &txctl) != 0) { | ||
216 | ath_dbg(common, CALIBRATE, "PAPRD TX failed\n"); | ||
217 | dev_kfree_skb_any(skb); | ||
218 | return false; | ||
219 | } | ||
220 | |||
221 | time_left = wait_for_completion_timeout(&sc->paprd_complete, | ||
222 | msecs_to_jiffies(ATH_PAPRD_TIMEOUT)); | ||
223 | |||
224 | if (!time_left) | ||
225 | ath_dbg(common, CALIBRATE, | ||
226 | "Timeout waiting for paprd training on TX chain %d\n", | ||
227 | chain); | ||
228 | |||
229 | return !!time_left; | ||
230 | } | ||
231 | |||
232 | void ath_paprd_calibrate(struct work_struct *work) | ||
233 | { | ||
234 | struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work); | ||
235 | struct ieee80211_hw *hw = sc->hw; | ||
236 | struct ath_hw *ah = sc->sc_ah; | ||
237 | struct ieee80211_hdr *hdr; | ||
238 | struct sk_buff *skb = NULL; | ||
239 | struct ath9k_hw_cal_data *caldata = ah->caldata; | ||
240 | struct ath_common *common = ath9k_hw_common(ah); | ||
241 | int ftype; | ||
242 | int chain_ok = 0; | ||
243 | int chain; | ||
244 | int len = 1800; | ||
245 | |||
246 | if (!caldata) | ||
247 | return; | ||
248 | |||
249 | ath9k_ps_wakeup(sc); | ||
250 | |||
251 | if (ar9003_paprd_init_table(ah) < 0) | ||
252 | goto fail_paprd; | ||
253 | |||
254 | skb = alloc_skb(len, GFP_KERNEL); | ||
255 | if (!skb) | ||
256 | goto fail_paprd; | ||
257 | |||
258 | skb_put(skb, len); | ||
259 | memset(skb->data, 0, len); | ||
260 | hdr = (struct ieee80211_hdr *)skb->data; | ||
261 | ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC; | ||
262 | hdr->frame_control = cpu_to_le16(ftype); | ||
263 | hdr->duration_id = cpu_to_le16(10); | ||
264 | memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN); | ||
265 | memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); | ||
266 | memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); | ||
267 | |||
268 | for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { | ||
269 | if (!(ah->txchainmask & BIT(chain))) | ||
270 | continue; | ||
271 | |||
272 | chain_ok = 0; | ||
273 | |||
274 | ath_dbg(common, CALIBRATE, | ||
275 | "Sending PAPRD frame for thermal measurement on chain %d\n", | ||
276 | chain); | ||
277 | if (!ath_paprd_send_frame(sc, skb, chain)) | ||
278 | goto fail_paprd; | ||
279 | |||
280 | ar9003_paprd_setup_gain_table(ah, chain); | ||
281 | |||
282 | ath_dbg(common, CALIBRATE, | ||
283 | "Sending PAPRD training frame on chain %d\n", chain); | ||
284 | if (!ath_paprd_send_frame(sc, skb, chain)) | ||
285 | goto fail_paprd; | ||
286 | |||
287 | if (!ar9003_paprd_is_done(ah)) { | ||
288 | ath_dbg(common, CALIBRATE, | ||
289 | "PAPRD not yet done on chain %d\n", chain); | ||
290 | break; | ||
291 | } | ||
292 | |||
293 | if (ar9003_paprd_create_curve(ah, caldata, chain)) { | ||
294 | ath_dbg(common, CALIBRATE, | ||
295 | "PAPRD create curve failed on chain %d\n", | ||
296 | chain); | ||
297 | break; | ||
298 | } | ||
299 | |||
300 | chain_ok = 1; | ||
301 | } | ||
302 | kfree_skb(skb); | ||
303 | |||
304 | if (chain_ok) { | ||
305 | caldata->paprd_done = true; | ||
306 | ath_paprd_activate(sc); | ||
307 | } | ||
308 | |||
309 | fail_paprd: | ||
310 | ath9k_ps_restore(sc); | ||
311 | } | ||
312 | |||
313 | /* | ||
314 | * ANI performs periodic noise floor calibration | ||
315 | * that is used to adjust and optimize the chip performance. This | ||
316 | * takes environmental changes (location, temperature) into account. | ||
317 | * When the task is complete, it reschedules itself depending on the | ||
318 | * appropriate interval that was calculated. | ||
319 | */ | ||
320 | void ath_ani_calibrate(unsigned long data) | ||
321 | { | ||
322 | struct ath_softc *sc = (struct ath_softc *)data; | ||
323 | struct ath_hw *ah = sc->sc_ah; | ||
324 | struct ath_common *common = ath9k_hw_common(ah); | ||
325 | bool longcal = false; | ||
326 | bool shortcal = false; | ||
327 | bool aniflag = false; | ||
328 | unsigned int timestamp = jiffies_to_msecs(jiffies); | ||
329 | u32 cal_interval, short_cal_interval, long_cal_interval; | ||
330 | unsigned long flags; | ||
331 | |||
332 | if (ah->caldata && ah->caldata->nfcal_interference) | ||
333 | long_cal_interval = ATH_LONG_CALINTERVAL_INT; | ||
334 | else | ||
335 | long_cal_interval = ATH_LONG_CALINTERVAL; | ||
336 | |||
337 | short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? | ||
338 | ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; | ||
339 | |||
340 | /* Only calibrate if awake */ | ||
341 | if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) | ||
342 | goto set_timer; | ||
343 | |||
344 | ath9k_ps_wakeup(sc); | ||
345 | |||
346 | /* Long calibration runs independently of short calibration. */ | ||
347 | if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) { | ||
348 | longcal = true; | ||
349 | common->ani.longcal_timer = timestamp; | ||
350 | } | ||
351 | |||
352 | /* Short calibration applies only while caldone is false */ | ||
353 | if (!common->ani.caldone) { | ||
354 | if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) { | ||
355 | shortcal = true; | ||
356 | common->ani.shortcal_timer = timestamp; | ||
357 | common->ani.resetcal_timer = timestamp; | ||
358 | } | ||
359 | } else { | ||
360 | if ((timestamp - common->ani.resetcal_timer) >= | ||
361 | ATH_RESTART_CALINTERVAL) { | ||
362 | common->ani.caldone = ath9k_hw_reset_calvalid(ah); | ||
363 | if (common->ani.caldone) | ||
364 | common->ani.resetcal_timer = timestamp; | ||
365 | } | ||
366 | } | ||
367 | |||
368 | /* Verify whether we must check ANI */ | ||
369 | if (sc->sc_ah->config.enable_ani | ||
370 | && (timestamp - common->ani.checkani_timer) >= | ||
371 | ah->config.ani_poll_interval) { | ||
372 | aniflag = true; | ||
373 | common->ani.checkani_timer = timestamp; | ||
374 | } | ||
375 | |||
376 | /* Call ANI routine if necessary */ | ||
377 | if (aniflag) { | ||
378 | spin_lock_irqsave(&common->cc_lock, flags); | ||
379 | ath9k_hw_ani_monitor(ah, ah->curchan); | ||
380 | ath_update_survey_stats(sc); | ||
381 | spin_unlock_irqrestore(&common->cc_lock, flags); | ||
382 | } | ||
383 | |||
384 | /* Perform calibration if necessary */ | ||
385 | if (longcal || shortcal) { | ||
386 | common->ani.caldone = | ||
387 | ath9k_hw_calibrate(ah, ah->curchan, | ||
388 | ah->rxchainmask, longcal); | ||
389 | } | ||
390 | |||
391 | ath_dbg(common, ANI, | ||
392 | "Calibration @%lu finished: %s %s %s, caldone: %s\n", | ||
393 | jiffies, | ||
394 | longcal ? "long" : "", shortcal ? "short" : "", | ||
395 | aniflag ? "ani" : "", common->ani.caldone ? "true" : "false"); | ||
396 | |||
397 | ath9k_ps_restore(sc); | ||
398 | |||
399 | set_timer: | ||
400 | /* | ||
401 | * Set timer interval based on previous results. | ||
402 | * The interval must be the shortest necessary to satisfy ANI, | ||
403 | * short calibration and long calibration. | ||
404 | */ | ||
405 | ath9k_debug_samp_bb_mac(sc); | ||
406 | cal_interval = ATH_LONG_CALINTERVAL; | ||
407 | if (sc->sc_ah->config.enable_ani) | ||
408 | cal_interval = min(cal_interval, | ||
409 | (u32)ah->config.ani_poll_interval); | ||
410 | if (!common->ani.caldone) | ||
411 | cal_interval = min(cal_interval, (u32)short_cal_interval); | ||
412 | |||
413 | mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); | ||
414 | if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) { | ||
415 | if (!ah->caldata->paprd_done) | ||
416 | ieee80211_queue_work(sc->hw, &sc->paprd_work); | ||
417 | else if (!ah->paprd_table_write_done) | ||
418 | ath_paprd_activate(sc); | ||
419 | } | ||
420 | } | ||
421 | |||
422 | void ath_start_ani(struct ath_common *common) | ||
423 | { | ||
424 | struct ath_hw *ah = common->ah; | ||
425 | unsigned long timestamp = jiffies_to_msecs(jiffies); | ||
426 | struct ath_softc *sc = (struct ath_softc *) common->priv; | ||
427 | |||
428 | if (!(sc->sc_flags & SC_OP_ANI_RUN)) | ||
429 | return; | ||
430 | |||
431 | if (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) | ||
432 | return; | ||
433 | |||
434 | common->ani.longcal_timer = timestamp; | ||
435 | common->ani.shortcal_timer = timestamp; | ||
436 | common->ani.checkani_timer = timestamp; | ||
437 | |||
438 | mod_timer(&common->ani.timer, | ||
439 | jiffies + msecs_to_jiffies((u32)ah->config.ani_poll_interval)); | ||
440 | } | ||
441 | |||
442 | void ath_update_survey_nf(struct ath_softc *sc, int channel) | ||
443 | { | ||
444 | struct ath_hw *ah = sc->sc_ah; | ||
445 | struct ath9k_channel *chan = &ah->channels[channel]; | ||
446 | struct survey_info *survey = &sc->survey[channel]; | ||
447 | |||
448 | if (chan->noisefloor) { | ||
449 | survey->filled |= SURVEY_INFO_NOISE_DBM; | ||
450 | survey->noise = ath9k_hw_getchan_noise(ah, chan); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | /* | ||
455 | * Updates the survey statistics and returns the busy time since last | ||
456 | * update in %, if the measurement duration was long enough for the | ||
457 | * result to be useful, -1 otherwise. | ||
458 | */ | ||
459 | int ath_update_survey_stats(struct ath_softc *sc) | ||
460 | { | ||
461 | struct ath_hw *ah = sc->sc_ah; | ||
462 | struct ath_common *common = ath9k_hw_common(ah); | ||
463 | int pos = ah->curchan - &ah->channels[0]; | ||
464 | struct survey_info *survey = &sc->survey[pos]; | ||
465 | struct ath_cycle_counters *cc = &common->cc_survey; | ||
466 | unsigned int div = common->clockrate * 1000; | ||
467 | int ret = 0; | ||
468 | |||
469 | if (!ah->curchan) | ||
470 | return -1; | ||
471 | |||
472 | if (ah->power_mode == ATH9K_PM_AWAKE) | ||
473 | ath_hw_cycle_counters_update(common); | ||
474 | |||
475 | if (cc->cycles > 0) { | ||
476 | survey->filled |= SURVEY_INFO_CHANNEL_TIME | | ||
477 | SURVEY_INFO_CHANNEL_TIME_BUSY | | ||
478 | SURVEY_INFO_CHANNEL_TIME_RX | | ||
479 | SURVEY_INFO_CHANNEL_TIME_TX; | ||
480 | survey->channel_time += cc->cycles / div; | ||
481 | survey->channel_time_busy += cc->rx_busy / div; | ||
482 | survey->channel_time_rx += cc->rx_frame / div; | ||
483 | survey->channel_time_tx += cc->tx_frame / div; | ||
484 | } | ||
485 | |||
486 | if (cc->cycles < div) | ||
487 | return -1; | ||
488 | |||
489 | if (cc->cycles > 0) | ||
490 | ret = cc->rx_busy * 100 / cc->cycles; | ||
491 | |||
492 | memset(cc, 0, sizeof(*cc)); | ||
493 | |||
494 | ath_update_survey_nf(sc, pos); | ||
495 | |||
496 | return ret; | ||
497 | } | ||