162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2010-2011 Atheros Communications Inc.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "hw.h"
1862306a36Sopenharmony_ci#include "hw-ops.h"
1962306a36Sopenharmony_ci#include "ar9003_phy.h"
2062306a36Sopenharmony_ci#include "ar9003_rtt.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define RTT_RESTORE_TIMEOUT          1000
2362306a36Sopenharmony_ci#define RTT_ACCESS_TIMEOUT           100
2462306a36Sopenharmony_ci#define RTT_BAD_VALUE                0x0bad0bad
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * RTT (Radio Retention Table) hardware implementation information
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * There is an internal table (i.e. the rtt) for each chain (or bank).
3062306a36Sopenharmony_ci * Each table contains 6 entries and each entry is corresponding to
3162306a36Sopenharmony_ci * a specific calibration parameter as depicted below.
3262306a36Sopenharmony_ci *  0~2 - DC offset DAC calibration: loop, low, high (offsetI/Q_...)
3362306a36Sopenharmony_ci *  3   - Filter cal (filterfc)
3462306a36Sopenharmony_ci *  4   - RX gain settings
3562306a36Sopenharmony_ci *  5   - Peak detector offset calibration (agc_caldac)
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_civoid ar9003_hw_rtt_enable(struct ath_hw *ah)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	REG_WRITE(ah, AR_PHY_RTT_CTRL, 1);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_civoid ar9003_hw_rtt_disable(struct ath_hw *ah)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	REG_WRITE(ah, AR_PHY_RTT_CTRL, 0);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_civoid ar9003_hw_rtt_set_mask(struct ath_hw *ah, u32 rtt_mask)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	REG_RMW_FIELD(ah, AR_PHY_RTT_CTRL,
5162306a36Sopenharmony_ci		      AR_PHY_RTT_CTRL_RESTORE_MASK, rtt_mask);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cibool ar9003_hw_rtt_force_restore(struct ath_hw *ah)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	if (!ath9k_hw_wait(ah, AR_PHY_RTT_CTRL,
5762306a36Sopenharmony_ci			   AR_PHY_RTT_CTRL_FORCE_RADIO_RESTORE,
5862306a36Sopenharmony_ci			   0, RTT_RESTORE_TIMEOUT))
5962306a36Sopenharmony_ci		return false;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	REG_RMW_FIELD(ah, AR_PHY_RTT_CTRL,
6262306a36Sopenharmony_ci		      AR_PHY_RTT_CTRL_FORCE_RADIO_RESTORE, 1);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (!ath9k_hw_wait(ah, AR_PHY_RTT_CTRL,
6562306a36Sopenharmony_ci			   AR_PHY_RTT_CTRL_FORCE_RADIO_RESTORE,
6662306a36Sopenharmony_ci			   0, RTT_RESTORE_TIMEOUT))
6762306a36Sopenharmony_ci		return false;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return true;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void ar9003_hw_rtt_load_hist_entry(struct ath_hw *ah, u8 chain,
7362306a36Sopenharmony_ci					  u32 index, u32 data28)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	u32 val;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	val = SM(data28, AR_PHY_RTT_SW_RTT_TABLE_DATA);
7862306a36Sopenharmony_ci	REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_1_B(chain), val);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	val = SM(0, AR_PHY_RTT_SW_RTT_TABLE_ACCESS) |
8162306a36Sopenharmony_ci	      SM(1, AR_PHY_RTT_SW_RTT_TABLE_WRITE) |
8262306a36Sopenharmony_ci	      SM(index, AR_PHY_RTT_SW_RTT_TABLE_ADDR);
8362306a36Sopenharmony_ci	REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val);
8462306a36Sopenharmony_ci	udelay(1);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	val |= SM(1, AR_PHY_RTT_SW_RTT_TABLE_ACCESS);
8762306a36Sopenharmony_ci	REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val);
8862306a36Sopenharmony_ci	udelay(1);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (!ath9k_hw_wait(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain),
9162306a36Sopenharmony_ci			   AR_PHY_RTT_SW_RTT_TABLE_ACCESS, 0,
9262306a36Sopenharmony_ci			   RTT_ACCESS_TIMEOUT))
9362306a36Sopenharmony_ci		return;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	val &= ~SM(1, AR_PHY_RTT_SW_RTT_TABLE_WRITE);
9662306a36Sopenharmony_ci	REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val);
9762306a36Sopenharmony_ci	udelay(1);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	ath9k_hw_wait(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain),
10062306a36Sopenharmony_ci		      AR_PHY_RTT_SW_RTT_TABLE_ACCESS, 0,
10162306a36Sopenharmony_ci		      RTT_ACCESS_TIMEOUT);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_civoid ar9003_hw_rtt_load_hist(struct ath_hw *ah)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	int chain, i;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
10962306a36Sopenharmony_ci		if (!(ah->caps.rx_chainmask & (1 << chain)))
11062306a36Sopenharmony_ci			continue;
11162306a36Sopenharmony_ci		for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) {
11262306a36Sopenharmony_ci			ar9003_hw_rtt_load_hist_entry(ah, chain, i,
11362306a36Sopenharmony_ci					      ah->caldata->rtt_table[chain][i]);
11462306a36Sopenharmony_ci			ath_dbg(ath9k_hw_common(ah), CALIBRATE,
11562306a36Sopenharmony_ci				"Load RTT value at idx %d, chain %d: 0x%x\n",
11662306a36Sopenharmony_ci				i, chain, ah->caldata->rtt_table[chain][i]);
11762306a36Sopenharmony_ci		}
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void ar9003_hw_patch_rtt(struct ath_hw *ah, int index, int chain)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	int agc, caldac;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (!test_bit(SW_PKDET_DONE, &ah->caldata->cal_flags))
12662306a36Sopenharmony_ci		return;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if ((index != 5) || (chain >= 2))
12962306a36Sopenharmony_ci		return;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	agc = REG_READ_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
13262306a36Sopenharmony_ci			     AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE);
13362306a36Sopenharmony_ci	if (!agc)
13462306a36Sopenharmony_ci		return;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	caldac = ah->caldata->caldac[chain];
13762306a36Sopenharmony_ci	ah->caldata->rtt_table[chain][index] &= 0xFFFF05FF;
13862306a36Sopenharmony_ci	caldac = (caldac & 0x20) | ((caldac & 0x1F) << 7);
13962306a36Sopenharmony_ci	ah->caldata->rtt_table[chain][index] |= (caldac << 4);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int ar9003_hw_rtt_fill_hist_entry(struct ath_hw *ah, u8 chain, u32 index)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	u32 val;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	val = SM(0, AR_PHY_RTT_SW_RTT_TABLE_ACCESS) |
14762306a36Sopenharmony_ci	      SM(0, AR_PHY_RTT_SW_RTT_TABLE_WRITE) |
14862306a36Sopenharmony_ci	      SM(index, AR_PHY_RTT_SW_RTT_TABLE_ADDR);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val);
15162306a36Sopenharmony_ci	udelay(1);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	val |= SM(1, AR_PHY_RTT_SW_RTT_TABLE_ACCESS);
15462306a36Sopenharmony_ci	REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val);
15562306a36Sopenharmony_ci	udelay(1);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (!ath9k_hw_wait(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain),
15862306a36Sopenharmony_ci			   AR_PHY_RTT_SW_RTT_TABLE_ACCESS, 0,
15962306a36Sopenharmony_ci			   RTT_ACCESS_TIMEOUT))
16062306a36Sopenharmony_ci		return RTT_BAD_VALUE;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	val = MS(REG_READ(ah, AR_PHY_RTT_TABLE_SW_INTF_1_B(chain)),
16362306a36Sopenharmony_ci		 AR_PHY_RTT_SW_RTT_TABLE_DATA);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return val;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_civoid ar9003_hw_rtt_fill_hist(struct ath_hw *ah)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	int chain, i;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
17462306a36Sopenharmony_ci		if (!(ah->caps.rx_chainmask & (1 << chain)))
17562306a36Sopenharmony_ci			continue;
17662306a36Sopenharmony_ci		for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) {
17762306a36Sopenharmony_ci			ah->caldata->rtt_table[chain][i] =
17862306a36Sopenharmony_ci				ar9003_hw_rtt_fill_hist_entry(ah, chain, i);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci			ar9003_hw_patch_rtt(ah, i, chain);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci			ath_dbg(ath9k_hw_common(ah), CALIBRATE,
18362306a36Sopenharmony_ci				"RTT value at idx %d, chain %d is: 0x%x\n",
18462306a36Sopenharmony_ci				i, chain, ah->caldata->rtt_table[chain][i]);
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	set_bit(RTT_DONE, &ah->caldata->cal_flags);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_civoid ar9003_hw_rtt_clear_hist(struct ath_hw *ah)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	int chain, i;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
19662306a36Sopenharmony_ci		if (!(ah->caps.rx_chainmask & (1 << chain)))
19762306a36Sopenharmony_ci			continue;
19862306a36Sopenharmony_ci		for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++)
19962306a36Sopenharmony_ci			ar9003_hw_rtt_load_hist_entry(ah, chain, i, 0);
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (ah->caldata)
20362306a36Sopenharmony_ci		clear_bit(RTT_DONE, &ah->caldata->cal_flags);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cibool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	bool restore;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (!ah->caldata)
21162306a36Sopenharmony_ci		return false;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (test_bit(SW_PKDET_DONE, &ah->caldata->cal_flags)) {
21462306a36Sopenharmony_ci		if (IS_CHAN_2GHZ(chan)){
21562306a36Sopenharmony_ci			REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(0),
21662306a36Sopenharmony_ci				      AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR,
21762306a36Sopenharmony_ci				      ah->caldata->caldac[0]);
21862306a36Sopenharmony_ci			REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(1),
21962306a36Sopenharmony_ci				      AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR,
22062306a36Sopenharmony_ci				      ah->caldata->caldac[1]);
22162306a36Sopenharmony_ci		} else {
22262306a36Sopenharmony_ci			REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(0),
22362306a36Sopenharmony_ci				      AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR,
22462306a36Sopenharmony_ci				      ah->caldata->caldac[0]);
22562306a36Sopenharmony_ci			REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(1),
22662306a36Sopenharmony_ci				      AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR,
22762306a36Sopenharmony_ci				      ah->caldata->caldac[1]);
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci		REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(1),
23062306a36Sopenharmony_ci			      AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1);
23162306a36Sopenharmony_ci		REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(0),
23262306a36Sopenharmony_ci			      AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1);
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (!test_bit(RTT_DONE, &ah->caldata->cal_flags))
23662306a36Sopenharmony_ci		return false;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	ar9003_hw_rtt_enable(ah);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (test_bit(SW_PKDET_DONE, &ah->caldata->cal_flags))
24162306a36Sopenharmony_ci		ar9003_hw_rtt_set_mask(ah, 0x30);
24262306a36Sopenharmony_ci	else
24362306a36Sopenharmony_ci		ar9003_hw_rtt_set_mask(ah, 0x10);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (!ath9k_hw_rfbus_req(ah)) {
24662306a36Sopenharmony_ci		ath_err(ath9k_hw_common(ah), "Could not stop baseband\n");
24762306a36Sopenharmony_ci		restore = false;
24862306a36Sopenharmony_ci		goto fail;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	ar9003_hw_rtt_load_hist(ah);
25262306a36Sopenharmony_ci	restore = ar9003_hw_rtt_force_restore(ah);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cifail:
25562306a36Sopenharmony_ci	ath9k_hw_rfbus_done(ah);
25662306a36Sopenharmony_ci	ar9003_hw_rtt_disable(ah);
25762306a36Sopenharmony_ci	return restore;
25862306a36Sopenharmony_ci}
259