link.c 14.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/*
 * Copyright (c) 2012 Qualcomm Atheros, Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "ath9k.h"

/*
 * TX polling - checks if the TX engine is stuck somewhere
 * and issues a chip reset if so.
 */
void ath_tx_complete_poll_work(struct work_struct *work)
{
	struct ath_softc *sc = container_of(work, struct ath_softc,
					    tx_complete_work.work);
	struct ath_txq *txq;
	int i;
	bool needreset = false;

L
Luis R. Rodriguez 已提交
31 32 33 34 35 36 37

	if (sc->tx99_state) {
		ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
			"skip tx hung detection on tx99\n");
		return;
	}

38 39 40 41 42 43 44 45 46 47 48
	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
		txq = sc->tx.txq_map[i];

		ath_txq_lock(sc, txq);
		if (txq->axq_depth) {
			if (txq->axq_tx_inprogress) {
				needreset = true;
				ath_txq_unlock(sc, txq);
				break;
			} else {
				txq->axq_tx_inprogress = true;
49 50
			}
		}
51
		ath_txq_unlock(sc, txq);
52
	}
53 54 55 56

	if (needreset) {
		ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
			"tx hung, resetting the chip\n");
57
		ath9k_queue_reset(sc, RESET_TYPE_TX_HANG);
S
Sujith Manoharan 已提交
58
		return;
59 60 61 62 63 64 65 66 67
	}

	ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
				     msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT));
}

/*
 * Checks if the BB/MAC is hung.
 */
S
Sujith Manoharan 已提交
68
bool ath_hw_check(struct ath_softc *sc)
69 70
{
	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
71
	enum ath_reset_type type;
S
Sujith Manoharan 已提交
72
	bool is_alive;
73 74

	ath9k_ps_wakeup(sc);
S
Sujith Manoharan 已提交
75

76 77
	is_alive = ath9k_hw_check_alive(sc->sc_ah);

S
Sujith Manoharan 已提交
78
	if (!is_alive) {
79
		ath_dbg(common, RESET,
S
Sujith Manoharan 已提交
80
			"HW hang detected, schedule chip reset\n");
81
		type = RESET_TYPE_MAC_HANG;
S
Sujith Manoharan 已提交
82
		ath9k_queue_reset(sc, type);
83 84 85
	}

	ath9k_ps_restore(sc);
S
Sujith Manoharan 已提交
86 87

	return is_alive;
88 89 90
}

/*
S
Sujith Manoharan 已提交
91
 * PLL-WAR for AR9485/AR9340
92
 */
S
Sujith Manoharan 已提交
93
static bool ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum)
94 95 96 97 98 99 100
{
	static int count;
	struct ath_common *common = ath9k_hw_common(sc->sc_ah);

	if (pll_sqsum >= 0x40000) {
		count++;
		if (count == 3) {
S
Sujith Manoharan 已提交
101
			ath_dbg(common, RESET, "PLL WAR, resetting the chip\n");
102
			ath9k_queue_reset(sc, RESET_TYPE_PLL_HANG);
103
			count = 0;
S
Sujith Manoharan 已提交
104
			return true;
105
		}
S
Sujith Manoharan 已提交
106
	} else {
107
		count = 0;
S
Sujith Manoharan 已提交
108 109 110
	}

	return false;
111 112 113 114
}

void ath_hw_pll_work(struct work_struct *work)
{
S
Sujith Manoharan 已提交
115
	u32 pll_sqsum;
116 117
	struct ath_softc *sc = container_of(work, struct ath_softc,
					    hw_pll_work.work);
118
	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
119 120 121 122 123 124
	/*
	 * ensure that the PLL WAR is executed only
	 * after the STA is associated (or) if the
	 * beaconing had started in interfaces that
	 * uses beacons.
	 */
125
	if (!test_bit(ATH_OP_BEACONS, &common->op_flags))
126
		return;
127

L
Luis R. Rodriguez 已提交
128 129 130
	if (sc->tx99_state)
		return;

S
Sujith Manoharan 已提交
131 132 133 134 135 136 137 138
	ath9k_ps_wakeup(sc);
	pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
	ath9k_ps_restore(sc);
	if (ath_hw_pll_rx_hang_check(sc, pll_sqsum))
		return;

	ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work,
				     msecs_to_jiffies(ATH_PLL_WORK_INTERVAL));
139 140 141 142 143 144 145 146
}

/*
 * PA Pre-distortion.
 */
static void ath_paprd_activate(struct ath_softc *sc)
{
	struct ath_hw *ah = sc->sc_ah;
147
	struct ath_common *common = ath9k_hw_common(ah);
148 149 150
	struct ath9k_hw_cal_data *caldata = ah->caldata;
	int chain;

151
	if (!caldata || !test_bit(PAPRD_DONE, &caldata->cal_flags)) {
152
		ath_dbg(common, CALIBRATE, "Failed to activate PAPRD\n");
153
		return;
154
	}
155 156 157 158 159 160 161 162 163

	ar9003_paprd_enable(ah, false);
	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
		if (!(ah->txchainmask & BIT(chain)))
			continue;

		ar9003_paprd_populate_single_table(ah, caldata, chain);
	}

164
	ath_dbg(common, CALIBRATE, "Activating PAPRD\n");
165 166 167 168 169 170 171 172 173 174 175 176 177
	ar9003_paprd_enable(ah, true);
}

static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain)
{
	struct ieee80211_hw *hw = sc->hw;
	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
	struct ath_hw *ah = sc->sc_ah;
	struct ath_common *common = ath9k_hw_common(ah);
	struct ath_tx_control txctl;
	int time_left;

	memset(&txctl, 0, sizeof(txctl));
178
	txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE];
179 180

	memset(tx_info, 0, sizeof(*tx_info));
181
	tx_info->band = sc->cur_chandef.chan->band;
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
	tx_info->flags |= IEEE80211_TX_CTL_NO_ACK;
	tx_info->control.rates[0].idx = 0;
	tx_info->control.rates[0].count = 1;
	tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
	tx_info->control.rates[1].idx = -1;

	init_completion(&sc->paprd_complete);
	txctl.paprd = BIT(chain);

	if (ath_tx_start(hw, skb, &txctl) != 0) {
		ath_dbg(common, CALIBRATE, "PAPRD TX failed\n");
		dev_kfree_skb_any(skb);
		return false;
	}

	time_left = wait_for_completion_timeout(&sc->paprd_complete,
			msecs_to_jiffies(ATH_PAPRD_TIMEOUT));

	if (!time_left)
		ath_dbg(common, CALIBRATE,
			"Timeout waiting for paprd training on TX chain %d\n",
			chain);

	return !!time_left;
}

void ath_paprd_calibrate(struct work_struct *work)
{
	struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work);
	struct ieee80211_hw *hw = sc->hw;
	struct ath_hw *ah = sc->sc_ah;
	struct ieee80211_hdr *hdr;
	struct sk_buff *skb = NULL;
	struct ath9k_hw_cal_data *caldata = ah->caldata;
	struct ath_common *common = ath9k_hw_common(ah);
	int ftype;
	int chain_ok = 0;
	int chain;
	int len = 1800;
221
	int ret;
222

223 224 225
	if (!caldata ||
	    !test_bit(PAPRD_PACKET_SENT, &caldata->cal_flags) ||
	    test_bit(PAPRD_DONE, &caldata->cal_flags)) {
226
		ath_dbg(common, CALIBRATE, "Skipping PAPRD calibration\n");
227
		return;
228
	}
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266

	ath9k_ps_wakeup(sc);

	if (ar9003_paprd_init_table(ah) < 0)
		goto fail_paprd;

	skb = alloc_skb(len, GFP_KERNEL);
	if (!skb)
		goto fail_paprd;

	skb_put(skb, len);
	memset(skb->data, 0, len);
	hdr = (struct ieee80211_hdr *)skb->data;
	ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC;
	hdr->frame_control = cpu_to_le16(ftype);
	hdr->duration_id = cpu_to_le16(10);
	memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN);
	memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN);
	memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);

	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
		if (!(ah->txchainmask & BIT(chain)))
			continue;

		chain_ok = 0;
		ar9003_paprd_setup_gain_table(ah, chain);

		ath_dbg(common, CALIBRATE,
			"Sending PAPRD training frame on chain %d\n", chain);
		if (!ath_paprd_send_frame(sc, skb, chain))
			goto fail_paprd;

		if (!ar9003_paprd_is_done(ah)) {
			ath_dbg(common, CALIBRATE,
				"PAPRD not yet done on chain %d\n", chain);
			break;
		}

267 268 269 270 271 272 273
		ret = ar9003_paprd_create_curve(ah, caldata, chain);
		if (ret == -EINPROGRESS) {
			ath_dbg(common, CALIBRATE,
				"PAPRD curve on chain %d needs to be re-trained\n",
				chain);
			break;
		} else if (ret) {
274 275
			ath_dbg(common, CALIBRATE,
				"PAPRD create curve failed on chain %d\n",
S
Sujith Manoharan 已提交
276
				chain);
277 278 279 280 281 282 283 284
			break;
		}

		chain_ok = 1;
	}
	kfree_skb(skb);

	if (chain_ok) {
285
		set_bit(PAPRD_DONE, &caldata->cal_flags);
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
		ath_paprd_activate(sc);
	}

fail_paprd:
	ath9k_ps_restore(sc);
}

/*
 *  ANI performs periodic noise floor calibration
 *  that is used to adjust and optimize the chip performance.  This
 *  takes environmental changes (location, temperature) into account.
 *  When the task is complete, it reschedules itself depending on the
 *  appropriate interval that was calculated.
 */
void ath_ani_calibrate(unsigned long data)
{
	struct ath_softc *sc = (struct ath_softc *)data;
	struct ath_hw *ah = sc->sc_ah;
	struct ath_common *common = ath9k_hw_common(ah);
	bool longcal = false;
	bool shortcal = false;
	bool aniflag = false;
	unsigned int timestamp = jiffies_to_msecs(jiffies);
	u32 cal_interval, short_cal_interval, long_cal_interval;
	unsigned long flags;

312
	if (ah->caldata && test_bit(NFCAL_INTF, &ah->caldata->cal_flags))
313 314 315 316 317 318 319 320
		long_cal_interval = ATH_LONG_CALINTERVAL_INT;
	else
		long_cal_interval = ATH_LONG_CALINTERVAL;

	short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
		ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;

	/* Only calibrate if awake */
321 322 323 324 325 326
	if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) {
		if (++ah->ani_skip_count >= ATH_ANI_MAX_SKIP_COUNT) {
			spin_lock_irqsave(&sc->sc_pm_lock, flags);
			sc->ps_flags |= PS_WAIT_FOR_ANI;
			spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
		}
327
		goto set_timer;
328 329 330 331 332
	}
	ah->ani_skip_count = 0;
	spin_lock_irqsave(&sc->sc_pm_lock, flags);
	sc->ps_flags &= ~PS_WAIT_FOR_ANI;
	spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358

	ath9k_ps_wakeup(sc);

	/* Long calibration runs independently of short calibration. */
	if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) {
		longcal = true;
		common->ani.longcal_timer = timestamp;
	}

	/* Short calibration applies only while caldone is false */
	if (!common->ani.caldone) {
		if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) {
			shortcal = true;
			common->ani.shortcal_timer = timestamp;
			common->ani.resetcal_timer = timestamp;
		}
	} else {
		if ((timestamp - common->ani.resetcal_timer) >=
		    ATH_RESTART_CALINTERVAL) {
			common->ani.caldone = ath9k_hw_reset_calvalid(ah);
			if (common->ani.caldone)
				common->ani.resetcal_timer = timestamp;
		}
	}

	/* Verify whether we must check ANI */
359
	if ((timestamp - common->ani.checkani_timer) >= ah->config.ani_poll_interval) {
360 361 362 363 364 365
		aniflag = true;
		common->ani.checkani_timer = timestamp;
	}

	/* Call ANI routine if necessary */
	if (aniflag) {
S
Sujith Manoharan 已提交
366
		spin_lock(&common->cc_lock);
367 368
		ath9k_hw_ani_monitor(ah, ah->curchan);
		ath_update_survey_stats(sc);
S
Sujith Manoharan 已提交
369
		spin_unlock(&common->cc_lock);
370 371 372 373
	}

	/* Perform calibration if necessary */
	if (longcal || shortcal) {
374 375 376 377 378 379 380 381 382
		int ret = ath9k_hw_calibrate(ah, ah->curchan, ah->rxchainmask,
					     longcal);
		if (ret < 0) {
			common->ani.caldone = 0;
			ath9k_queue_reset(sc, RESET_TYPE_CALIBRATION);
			return;
		}

		common->ani.caldone = ret;
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
	}

	ath_dbg(common, ANI,
		"Calibration @%lu finished: %s %s %s, caldone: %s\n",
		jiffies,
		longcal ? "long" : "", shortcal ? "short" : "",
		aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");

	ath9k_ps_restore(sc);

set_timer:
	/*
	* Set timer interval based on previous results.
	* The interval must be the shortest necessary to satisfy ANI,
	* short calibration and long calibration.
	*/
	cal_interval = ATH_LONG_CALINTERVAL;
400
	cal_interval = min(cal_interval, (u32)ah->config.ani_poll_interval);
401 402 403 404
	if (!common->ani.caldone)
		cal_interval = min(cal_interval, (u32)short_cal_interval);

	mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
405

S
Sujith Manoharan 已提交
406
	if (ar9003_is_paprd_enabled(ah) && ah->caldata) {
407
		if (!test_bit(PAPRD_DONE, &ah->caldata->cal_flags)) {
408
			ieee80211_queue_work(sc->hw, &sc->paprd_work);
409 410
		} else if (!ah->paprd_table_write_done) {
			ath9k_ps_wakeup(sc);
411
			ath_paprd_activate(sc);
412 413
			ath9k_ps_restore(sc);
		}
414 415 416
	}
}

S
Sujith Manoharan 已提交
417
void ath_start_ani(struct ath_softc *sc)
418
{
S
Sujith Manoharan 已提交
419 420
	struct ath_hw *ah = sc->sc_ah;
	struct ath_common *common = ath9k_hw_common(ah);
421 422
	unsigned long timestamp = jiffies_to_msecs(jiffies);

S
Sujith Manoharan 已提交
423
	if (common->disable_ani ||
424
	    !test_bit(ATH_OP_ANI_RUN, &common->op_flags) ||
425
	    sc->cur_chan->offchannel)
426 427 428 429 430 431
		return;

	common->ani.longcal_timer = timestamp;
	common->ani.shortcal_timer = timestamp;
	common->ani.checkani_timer = timestamp;

S
Sujith Manoharan 已提交
432
	ath_dbg(common, ANI, "Starting ANI\n");
433 434 435 436
	mod_timer(&common->ani.timer,
		  jiffies + msecs_to_jiffies((u32)ah->config.ani_poll_interval));
}

S
Sujith Manoharan 已提交
437 438 439 440 441 442 443 444 445 446 447
void ath_stop_ani(struct ath_softc *sc)
{
	struct ath_common *common = ath9k_hw_common(sc->sc_ah);

	ath_dbg(common, ANI, "Stopping ANI\n");
	del_timer_sync(&common->ani.timer);
}

void ath_check_ani(struct ath_softc *sc)
{
	struct ath_hw *ah = sc->sc_ah;
448
	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
449
	struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
S
Sujith Manoharan 已提交
450 451 452 453 454 455 456 457 458 459 460 461 462 463

	/*
	 * Check for the various conditions in which ANI has to
	 * be stopped.
	 */
	if (ah->opmode == NL80211_IFTYPE_ADHOC) {
		if (!cur_conf->enable_beacon)
			goto stop_ani;
	} else if (ah->opmode == NL80211_IFTYPE_AP) {
		if (!cur_conf->enable_beacon) {
			/*
			 * Disable ANI only when there are no
			 * associated stations.
			 */
464
			if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags))
S
Sujith Manoharan 已提交
465 466 467
				goto stop_ani;
		}
	} else if (ah->opmode == NL80211_IFTYPE_STATION) {
468
		if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags))
S
Sujith Manoharan 已提交
469 470 471
			goto stop_ani;
	}

472 473
	if (!test_bit(ATH_OP_ANI_RUN, &common->op_flags)) {
		set_bit(ATH_OP_ANI_RUN, &common->op_flags);
S
Sujith Manoharan 已提交
474 475 476 477 478 479
		ath_start_ani(sc);
	}

	return;

stop_ani:
480
	clear_bit(ATH_OP_ANI_RUN, &common->op_flags);
S
Sujith Manoharan 已提交
481 482 483
	ath_stop_ani(sc);
}

484 485 486 487 488 489 490 491
void ath_update_survey_nf(struct ath_softc *sc, int channel)
{
	struct ath_hw *ah = sc->sc_ah;
	struct ath9k_channel *chan = &ah->channels[channel];
	struct survey_info *survey = &sc->survey[channel];

	if (chan->noisefloor) {
		survey->filled |= SURVEY_INFO_NOISE_DBM;
492 493
		survey->noise = ath9k_hw_getchan_noise(ah, chan,
						       chan->noisefloor);
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
	}
}

/*
 * Updates the survey statistics and returns the busy time since last
 * update in %, if the measurement duration was long enough for the
 * result to be useful, -1 otherwise.
 */
int ath_update_survey_stats(struct ath_softc *sc)
{
	struct ath_hw *ah = sc->sc_ah;
	struct ath_common *common = ath9k_hw_common(ah);
	int pos = ah->curchan - &ah->channels[0];
	struct survey_info *survey = &sc->survey[pos];
	struct ath_cycle_counters *cc = &common->cc_survey;
	unsigned int div = common->clockrate * 1000;
	int ret = 0;

	if (!ah->curchan)
		return -1;

	if (ah->power_mode == ATH9K_PM_AWAKE)
		ath_hw_cycle_counters_update(common);

	if (cc->cycles > 0) {
		survey->filled |= SURVEY_INFO_CHANNEL_TIME |
			SURVEY_INFO_CHANNEL_TIME_BUSY |
			SURVEY_INFO_CHANNEL_TIME_RX |
			SURVEY_INFO_CHANNEL_TIME_TX;
		survey->channel_time += cc->cycles / div;
		survey->channel_time_busy += cc->rx_busy / div;
		survey->channel_time_rx += cc->rx_frame / div;
		survey->channel_time_tx += cc->tx_frame / div;
	}

	if (cc->cycles < div)
		return -1;

	if (cc->cycles > 0)
		ret = cc->rx_busy * 100 / cc->cycles;

	memset(cc, 0, sizeof(*cc));

	ath_update_survey_nf(sc, pos);

	return ret;
}