htc_drv_gpio.c 11.1 KB
Newer Older
1
/*
2
 * Copyright (c) 2010-2011 Atheros Communications Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * 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.
 */

17 18 19 20 21 22
#include "htc.h"

/******************/
/*     BTCOEX     */
/******************/

23 24
#define ATH_HTC_BTCOEX_PRODUCT_ID "wb193"

25 26
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
/*
 * Detects if there is any priority bt traffic
 */
static void ath_detect_bt_priority(struct ath9k_htc_priv *priv)
{
	struct ath_btcoex *btcoex = &priv->btcoex;
	struct ath_hw *ah = priv->ah;

	if (ath9k_hw_gpio_get(ah, ah->btcoex_hw.btpriority_gpio))
		btcoex->bt_priority_cnt++;

	if (time_after(jiffies, btcoex->bt_priority_time +
			msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
		priv->op_flags &= ~(OP_BT_PRIORITY_DETECTED | OP_BT_SCAN);
		/* Detect if colocated bt started scanning */
		if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) {
43
			ath_dbg(ath9k_hw_common(ah), BTCOEX,
J
Joe Perches 已提交
44
				"BT scan detected\n");
45 46 47
			priv->op_flags |= (OP_BT_SCAN |
					 OP_BT_PRIORITY_DETECTED);
		} else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
48
			ath_dbg(ath9k_hw_common(ah), BTCOEX,
J
Joe Perches 已提交
49
				"BT priority traffic detected\n");
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
			priv->op_flags |= OP_BT_PRIORITY_DETECTED;
		}

		btcoex->bt_priority_cnt = 0;
		btcoex->bt_priority_time = jiffies;
	}
}

/*
 * This is the master bt coex work which runs for every
 * 45ms, bt traffic will be given priority during 55% of this
 * period while wlan gets remaining 45%
 */
static void ath_btcoex_period_work(struct work_struct *work)
{
	struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv,
						   coex_period_work.work);
	struct ath_btcoex *btcoex = &priv->btcoex;
	struct ath_common *common = ath9k_hw_common(priv->ah);
	u32 timer_period;
	bool is_btscan;
	int ret;

	ath_detect_bt_priority(priv);

	is_btscan = !!(priv->op_flags & OP_BT_SCAN);

77 78
	ret = ath9k_htc_update_cap_target(priv,
				  !!(priv->op_flags & OP_BT_PRIORITY_DETECTED));
79 80 81 82
	if (ret) {
		ath_err(common, "Unable to set BTCOEX parameters\n");
		return;
	}
83

84
	ath9k_hw_btcoex_bt_stomp(priv->ah, is_btscan ? ATH_BTCOEX_STOMP_ALL :
85 86
			btcoex->bt_stomp_type);

87
	ath9k_hw_btcoex_enable(priv->ah);
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
	timer_period = is_btscan ? btcoex->btscan_no_stomp :
		btcoex->btcoex_no_stomp;
	ieee80211_queue_delayed_work(priv->hw, &priv->duty_cycle_work,
				     msecs_to_jiffies(timer_period));
	ieee80211_queue_delayed_work(priv->hw, &priv->coex_period_work,
				     msecs_to_jiffies(btcoex->btcoex_period));
}

/*
 * Work to time slice between wlan and bt traffic and
 * configure weight registers
 */
static void ath_btcoex_duty_cycle_work(struct work_struct *work)
{
	struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv,
						   duty_cycle_work.work);
	struct ath_hw *ah = priv->ah;
	struct ath_btcoex *btcoex = &priv->btcoex;
	struct ath_common *common = ath9k_hw_common(ah);
	bool is_btscan = priv->op_flags & OP_BT_SCAN;

109
	ath_dbg(common, BTCOEX, "time slice work for bt and wlan\n");
110 111

	if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan)
112
		ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE);
113
	else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
114
		ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW);
115
	ath9k_hw_btcoex_enable(priv->ah);
116 117
}

118
static void ath_htc_init_btcoex_work(struct ath9k_htc_priv *priv)
119 120 121
{
	struct ath_btcoex *btcoex = &priv->btcoex;

122 123 124
	if (ath9k_hw_get_btcoex_scheme(priv->ah) == ATH_BTCOEX_CFG_NONE)
		return;

125 126 127 128 129 130 131 132 133 134 135 136 137
	btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD;
	btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
		btcoex->btcoex_period / 100;
	btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) *
				   btcoex->btcoex_period / 100;
	INIT_DELAYED_WORK(&priv->coex_period_work, ath_btcoex_period_work);
	INIT_DELAYED_WORK(&priv->duty_cycle_work, ath_btcoex_duty_cycle_work);
}

/*
 * (Re)start btcoex work
 */

138
static void ath_htc_resume_btcoex_work(struct ath9k_htc_priv *priv)
139 140 141 142
{
	struct ath_btcoex *btcoex = &priv->btcoex;
	struct ath_hw *ah = priv->ah;

143 144 145
	if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_NONE)
		return;

146
	ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex work\n");
147 148 149 150 151 152 153 154 155 156 157

	btcoex->bt_priority_cnt = 0;
	btcoex->bt_priority_time = jiffies;
	priv->op_flags &= ~(OP_BT_PRIORITY_DETECTED | OP_BT_SCAN);
	ieee80211_queue_delayed_work(priv->hw, &priv->coex_period_work, 0);
}


/*
 * Cancel btcoex and bt duty cycle work.
 */
158
static void ath_htc_cancel_btcoex_work(struct ath9k_htc_priv *priv)
159
{
160 161 162
	if (ath9k_hw_get_btcoex_scheme(priv->ah) == ATH_BTCOEX_CFG_NONE)
		return;

163 164 165
	cancel_delayed_work_sync(&priv->coex_period_work);
	cancel_delayed_work_sync(&priv->duty_cycle_work);
}
166

167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
void ath9k_htc_start_btcoex(struct ath9k_htc_priv *priv)
{
	struct ath_hw *ah = priv->ah;

	if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) {
		ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
					   AR_STOMP_LOW_WLAN_WGHT);
		ath9k_hw_btcoex_enable(ah);
		ath_htc_resume_btcoex_work(priv);
	}
}

void ath9k_htc_stop_btcoex(struct ath9k_htc_priv *priv)
{
	struct ath_hw *ah = priv->ah;

	if (ah->btcoex_hw.enabled &&
	    ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) {
		ath9k_hw_btcoex_disable(ah);
		if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
			ath_htc_cancel_btcoex_work(priv);
	}
}

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
void ath9k_htc_init_btcoex(struct ath9k_htc_priv *priv, char *product)
{
	struct ath_hw *ah = priv->ah;
	int qnum;

	if (product && strncmp(product, ATH_HTC_BTCOEX_PRODUCT_ID, 5) == 0) {
		ah->btcoex_hw.scheme = ATH_BTCOEX_CFG_3WIRE;
		if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_NONE)
			return;
	}

	switch (ath9k_hw_get_btcoex_scheme(priv->ah)) {
	case ATH_BTCOEX_CFG_NONE:
		break;
	case ATH_BTCOEX_CFG_3WIRE:
		priv->ah->btcoex_hw.btactive_gpio = 7;
		priv->ah->btcoex_hw.btpriority_gpio = 6;
		priv->ah->btcoex_hw.wlanactive_gpio = 8;
		priv->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
		ath9k_hw_btcoex_init_3wire(priv->ah);
		ath_htc_init_btcoex_work(priv);
		qnum = priv->hwq_map[WME_AC_BE];
		ath9k_hw_init_btcoex_hw(priv->ah, qnum);
		break;
	default:
		WARN_ON(1);
		break;
	}
}

221 222
#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */

223 224 225 226
/*******/
/* LED */
/*******/

227 228
#ifdef CONFIG_MAC80211_LEDS
void ath9k_led_work(struct work_struct *work)
229
{
230 231 232
	struct ath9k_htc_priv *priv = container_of(work,
						   struct ath9k_htc_priv,
						   led_work);
233

234 235
	ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin,
			  (priv->brightness == LED_OFF));
236 237 238 239 240
}

static void ath9k_led_brightness(struct led_classdev *led_cdev,
				 enum led_brightness brightness)
{
241 242 243
	struct ath9k_htc_priv *priv = container_of(led_cdev,
						   struct ath9k_htc_priv,
						   led_cdev);
244

245 246 247
	/* Not locked, but it's just a tiny green light..*/
	priv->brightness = brightness;
	ieee80211_queue_work(priv->hw, &priv->led_work);
248 249 250 251
}

void ath9k_deinit_leds(struct ath9k_htc_priv *priv)
{
252 253 254 255 256 257
	if (!priv->led_registered)
		return;

	ath9k_led_brightness(&priv->led_cdev, LED_OFF);
	led_classdev_unregister(&priv->led_cdev);
	cancel_work_sync(&priv->led_work);
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
}

void ath9k_init_leds(struct ath9k_htc_priv *priv)
{
	int ret;

	if (AR_SREV_9287(priv->ah))
		priv->ah->led_pin = ATH_LED_PIN_9287;
	else if (AR_SREV_9271(priv->ah))
		priv->ah->led_pin = ATH_LED_PIN_9271;
	else if (AR_DEVID_7010(priv->ah))
		priv->ah->led_pin = ATH_LED_PIN_7010;
	else
		priv->ah->led_pin = ATH_LED_PIN_DEF;

	/* Configure gpio 1 for output */
	ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin,
			    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
	/* LED off, active low */
	ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1);

279 280 281 282
	snprintf(priv->led_name, sizeof(priv->led_name),
		"ath9k_htc-%s", wiphy_name(priv->hw->wiphy));
	priv->led_cdev.name = priv->led_name;
	priv->led_cdev.brightness_set = ath9k_led_brightness;
283

284 285 286
	ret = led_classdev_register(wiphy_dev(priv->hw->wiphy), &priv->led_cdev);
	if (ret < 0)
		return;
287

288 289 290 291
	INIT_WORK(&priv->led_work, ath9k_led_work);
	priv->led_registered = true;

	return;
292
}
293
#endif
294 295 296 297 298 299 300

/*******************/
/*	Rfkill	   */
/*******************/

static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv)
{
301 302 303 304 305 306 307 308
	bool is_blocked;

	ath9k_htc_ps_wakeup(priv);
	is_blocked = ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) ==
						 priv->ah->rfkill_polarity;
	ath9k_htc_ps_restore(priv);

	return is_blocked;
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
}

void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw)
{
	struct ath9k_htc_priv *priv = hw->priv;
	bool blocked = !!ath_is_rfkill_set(priv);

	wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
}

void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv)
{
	if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
		wiphy_rfkill_start_polling(priv->hw->wiphy);
}

void ath9k_htc_radio_enable(struct ieee80211_hw *hw)
{
	struct ath9k_htc_priv *priv = hw->priv;
	struct ath_hw *ah = priv->ah;
	struct ath_common *common = ath9k_hw_common(ah);
	int ret;
	u8 cmd_rsp;

	if (!ah->curchan)
		ah->curchan = ath9k_cmn_get_curchannel(hw, ah);

	/* Reset the HW */
	ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
	if (ret) {
		ath_err(common,
			"Unable to reset hardware; reset status %d (freq %u MHz)\n",
			ret, ah->curchan->channel);
	}

344 345
	ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
			       &priv->curtxpow);
346 347 348 349 350 351 352

	/* Start RX */
	WMI_CMD(WMI_START_RECV_CMDID);
	ath9k_host_rx_init(priv);

	/* Start TX */
	htc_start(priv->htc);
353
	spin_lock_bh(&priv->tx.tx_lock);
354
	priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
355
	spin_unlock_bh(&priv->tx.tx_lock);
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
	ieee80211_wake_queues(hw);

	WMI_CMD(WMI_ENABLE_INTR_CMDID);

	/* Enable LED */
	ath9k_hw_cfg_output(ah, ah->led_pin,
			    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
	ath9k_hw_set_gpio(ah, ah->led_pin, 0);
}

void ath9k_htc_radio_disable(struct ieee80211_hw *hw)
{
	struct ath9k_htc_priv *priv = hw->priv;
	struct ath_hw *ah = priv->ah;
	struct ath_common *common = ath9k_hw_common(ah);
	int ret;
	u8 cmd_rsp;

	ath9k_htc_ps_wakeup(priv);

	/* Disable LED */
	ath9k_hw_set_gpio(ah, ah->led_pin, 1);
	ath9k_hw_cfg_gpio_input(ah, ah->led_pin);

	WMI_CMD(WMI_DISABLE_INTR_CMDID);

	/* Stop TX */
	ieee80211_stop_queues(hw);
384
	ath9k_htc_tx_drain(priv);
385 386 387 388 389
	WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);

	/* Stop RX */
	WMI_CMD(WMI_STOP_RECV_CMDID);

S
Sujith Manoharan 已提交
390 391 392
	/* Clear the WMI event queue */
	ath9k_wmi_event_drain(priv);

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
	/*
	 * The MIB counters have to be disabled here,
	 * since the target doesn't do it.
	 */
	ath9k_hw_disable_mib_counters(ah);

	if (!ah->curchan)
		ah->curchan = ath9k_cmn_get_curchannel(hw, ah);

	/* Reset the HW */
	ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
	if (ret) {
		ath_err(common,
			"Unable to reset hardware; reset status %d (freq %u MHz)\n",
			ret, ah->curchan->channel);
	}

	/* Disable the PHY */
	ath9k_hw_phy_disable(ah);

	ath9k_htc_ps_restore(priv);
	ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
}