1/******************************************************************************
2 *
3 * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
17 *
18 ******************************************************************************/
19
20#include <osdep_service.h>
21#include <drv_types.h>
22#include <phy.h>
23#include <rf.h>
24#include <rtl8188e_hal.h>
25
26void rtl88eu_phy_rf6052_set_bandwidth(struct adapter *adapt,
27				      enum ht_channel_width bandwidth)
28{
29	struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
30
31	switch (bandwidth) {
32	case HT_CHANNEL_WIDTH_20:
33		hal_data->RfRegChnlVal[0] = ((hal_data->RfRegChnlVal[0] &
34					      0xfffff3ff) | BIT(10) | BIT(11));
35		phy_set_rf_reg(adapt, RF_PATH_A, RF_CHNLBW, bRFRegOffsetMask,
36			       hal_data->RfRegChnlVal[0]);
37		break;
38	case HT_CHANNEL_WIDTH_40:
39		hal_data->RfRegChnlVal[0] = ((hal_data->RfRegChnlVal[0] &
40					      0xfffff3ff) | BIT(10));
41		phy_set_rf_reg(adapt, RF_PATH_A, RF_CHNLBW, bRFRegOffsetMask,
42			       hal_data->RfRegChnlVal[0]);
43		break;
44	default:
45		break;
46	}
47}
48
49void rtl88eu_phy_rf6052_set_cck_txpower(struct adapter *adapt, u8 *powerlevel)
50{
51	struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
52	struct dm_priv *pdmpriv = &hal_data->dmpriv;
53	struct mlme_ext_priv *pmlmeext = &adapt->mlmeextpriv;
54	u32 tx_agc[2] = {0, 0}, tmpval = 0, pwrtrac_value;
55	u8 idx1, idx2;
56	u8 *ptr;
57	u8 direction;
58
59
60	if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) {
61		tx_agc[RF_PATH_A] = 0x3f3f3f3f;
62		tx_agc[RF_PATH_B] = 0x3f3f3f3f;
63		for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) {
64			tx_agc[idx1] = powerlevel[idx1] |
65				      (powerlevel[idx1]<<8) |
66				      (powerlevel[idx1]<<16) |
67				      (powerlevel[idx1]<<24);
68			if (tx_agc[idx1] > 0x20 && hal_data->ExternalPA)
69				tx_agc[idx1] = 0x20;
70		}
71	} else {
72		if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level1) {
73			tx_agc[RF_PATH_A] = 0x10101010;
74			tx_agc[RF_PATH_B] = 0x10101010;
75		} else if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level2) {
76			tx_agc[RF_PATH_A] = 0x00000000;
77			tx_agc[RF_PATH_B] = 0x00000000;
78		} else {
79			for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) {
80				tx_agc[idx1] = powerlevel[idx1] |
81					       (powerlevel[idx1]<<8) |
82					       (powerlevel[idx1]<<16) |
83					       (powerlevel[idx1]<<24);
84			}
85			if (hal_data->EEPROMRegulatory == 0) {
86				tmpval = hal_data->MCSTxPowerLevelOriginalOffset[0][6] +
87					 (hal_data->MCSTxPowerLevelOriginalOffset[0][7]<<8);
88				tx_agc[RF_PATH_A] += tmpval;
89
90				tmpval = hal_data->MCSTxPowerLevelOriginalOffset[0][14] +
91					 (hal_data->MCSTxPowerLevelOriginalOffset[0][15]<<24);
92				tx_agc[RF_PATH_B] += tmpval;
93			}
94		}
95	}
96	for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) {
97		ptr = (u8 *)(&(tx_agc[idx1]));
98		for (idx2 = 0; idx2 < 4; idx2++) {
99			if (*ptr > RF6052_MAX_TX_PWR)
100				*ptr = RF6052_MAX_TX_PWR;
101			ptr++;
102		}
103	}
104	rtl88eu_dm_txpower_track_adjust(&hal_data->odmpriv, 1, &direction,
105					&pwrtrac_value);
106
107	if (direction == 1) {
108		/*  Increase TX power */
109		tx_agc[0] += pwrtrac_value;
110		tx_agc[1] += pwrtrac_value;
111	} else if (direction == 2) {
112		/*  Decrease TX power */
113		tx_agc[0] -=  pwrtrac_value;
114		tx_agc[1] -=  pwrtrac_value;
115	}
116
117	/*  rf-A cck tx power */
118	tmpval = tx_agc[RF_PATH_A]&0xff;
119	phy_set_bb_reg(adapt, rTxAGC_A_CCK1_Mcs32, bMaskByte1, tmpval);
120	tmpval = tx_agc[RF_PATH_A]>>8;
121	phy_set_bb_reg(adapt, rTxAGC_B_CCK11_A_CCK2_11, 0xffffff00, tmpval);
122
123	/*  rf-B cck tx power */
124	tmpval = tx_agc[RF_PATH_B]>>24;
125	phy_set_bb_reg(adapt, rTxAGC_B_CCK11_A_CCK2_11, bMaskByte0, tmpval);
126	tmpval = tx_agc[RF_PATH_B]&0x00ffffff;
127	phy_set_bb_reg(adapt, rTxAGC_B_CCK1_55_Mcs32, 0xffffff00, tmpval);
128}
129
130/*  powerbase0 for OFDM rates */
131/*  powerbase1 for HT MCS rates */
132static void getpowerbase88e(struct adapter *adapt, u8 *pwr_level_ofdm,
133			    u8 *pwr_level_bw20, u8 *pwr_level_bw40,
134			    u8 channel, u32 *ofdmbase, u32 *mcs_base)
135{
136	struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
137	u32 powerbase0, powerbase1;
138	u8 i, powerlevel[2];
139
140	for (i = 0; i < 2; i++) {
141		powerbase0 = pwr_level_ofdm[i];
142
143		powerbase0 = (powerbase0<<24) | (powerbase0<<16) |
144			     (powerbase0<<8) | powerbase0;
145		*(ofdmbase+i) = powerbase0;
146	}
147	for (i = 0; i < hal_data->NumTotalRFPath; i++) {
148		/* Check HT20 to HT40 diff */
149		if (hal_data->CurrentChannelBW == HT_CHANNEL_WIDTH_20)
150			powerlevel[i] = pwr_level_bw20[i];
151		else
152			powerlevel[i] = pwr_level_bw40[i];
153		powerbase1 = powerlevel[i];
154		powerbase1 = (powerbase1<<24) | (powerbase1<<16) |
155			     (powerbase1<<8) | powerbase1;
156		*(mcs_base+i) = powerbase1;
157	}
158}
159static void get_rx_power_val_by_reg(struct adapter *adapt, u8 channel,
160				    u8 index, u32 *powerbase0, u32 *powerbase1,
161				    u32 *out_val)
162{
163	struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
164	struct dm_priv	*pdmpriv = &hal_data->dmpriv;
165	u8 i, chnlGroup = 0, pwr_diff_limit[4], customer_pwr_limit;
166	s8 pwr_diff = 0;
167	u32 write_val, customer_limit, rf;
168	u8 regulatory = hal_data->EEPROMRegulatory;
169
170	/*  Index 0 & 1= legacy OFDM, 2-5=HT_MCS rate */
171
172	for (rf = 0; rf < 2; rf++) {
173		u8 j = index + (rf ? 8 : 0);
174
175		switch (regulatory) {
176		case 0:
177			chnlGroup = 0;
178			write_val = hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][index+(rf ? 8 : 0)] +
179				((index < 2) ? powerbase0[rf] : powerbase1[rf]);
180			break;
181		case 1: /*  Realtek regulatory */
182			/*  increase power diff defined by Realtek for regulatory */
183			if (hal_data->pwrGroupCnt == 1)
184				chnlGroup = 0;
185			if (hal_data->pwrGroupCnt >= hal_data->PGMaxGroup) {
186				if (channel < 3)
187					chnlGroup = 0;
188				else if (channel < 6)
189					chnlGroup = 1;
190				else if (channel < 9)
191					chnlGroup = 2;
192				else if (channel < 12)
193					chnlGroup = 3;
194				else if (channel < 14)
195					chnlGroup = 4;
196				else if (channel == 14)
197					chnlGroup = 5;
198			}
199			write_val = hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][index+(rf ? 8 : 0)] +
200					((index < 2) ? powerbase0[rf] : powerbase1[rf]);
201			break;
202		case 2:	/*  Better regulatory */
203				/*  don't increase any power diff */
204			write_val = (index < 2) ? powerbase0[rf] : powerbase1[rf];
205			break;
206		case 3:	/*  Customer defined power diff. */
207				/*  increase power diff defined by customer. */
208			chnlGroup = 0;
209
210			if (index < 2)
211				pwr_diff = hal_data->TxPwrLegacyHtDiff[rf][channel-1];
212			else if (hal_data->CurrentChannelBW == HT_CHANNEL_WIDTH_20)
213				pwr_diff = hal_data->TxPwrHt20Diff[rf][channel-1];
214
215			if (hal_data->CurrentChannelBW == HT_CHANNEL_WIDTH_40)
216				customer_pwr_limit = hal_data->PwrGroupHT40[rf][channel-1];
217			else
218				customer_pwr_limit = hal_data->PwrGroupHT20[rf][channel-1];
219
220			if (pwr_diff >= customer_pwr_limit)
221				pwr_diff = 0;
222			else
223				pwr_diff = customer_pwr_limit - pwr_diff;
224
225			for (i = 0; i < 4; i++) {
226				pwr_diff_limit[i] = (u8)((hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][j] &
227							 (0x7f << (i * 8))) >> (i * 8));
228
229				if (pwr_diff_limit[i] > pwr_diff)
230					pwr_diff_limit[i] = pwr_diff;
231			}
232			customer_limit = (pwr_diff_limit[3]<<24) |
233					 (pwr_diff_limit[2]<<16) |
234					 (pwr_diff_limit[1]<<8) |
235					 (pwr_diff_limit[0]);
236			write_val = customer_limit + ((index < 2) ? powerbase0[rf] : powerbase1[rf]);
237			break;
238		default:
239			chnlGroup = 0;
240			write_val = hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][j] +
241					((index < 2) ? powerbase0[rf] : powerbase1[rf]);
242			break;
243		}
244/*  20100427 Joseph: Driver dynamic Tx power shall not affect Tx power. It shall be determined by power training mechanism. */
245/*  Currently, we cannot fully disable driver dynamic tx power mechanism because it is referenced by BT coexist mechanism. */
246/*  In the future, two mechanism shall be separated from each other and maintained independently. Thanks for Lanhsin's reminder. */
247		/* 92d do not need this */
248		if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level1)
249			write_val = 0x14141414;
250		else if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level2)
251			write_val = 0x00000000;
252
253		*(out_val+rf) = write_val;
254	}
255}
256
257static void write_ofdm_pwr_reg(struct adapter *adapt, u8 index, u32 *pvalue)
258{
259	u16 regoffset_a[6] = { rTxAGC_A_Rate18_06, rTxAGC_A_Rate54_24,
260			       rTxAGC_A_Mcs03_Mcs00, rTxAGC_A_Mcs07_Mcs04,
261			       rTxAGC_A_Mcs11_Mcs08, rTxAGC_A_Mcs15_Mcs12 };
262	u16 regoffset_b[6] = { rTxAGC_B_Rate18_06, rTxAGC_B_Rate54_24,
263			       rTxAGC_B_Mcs03_Mcs00, rTxAGC_B_Mcs07_Mcs04,
264			       rTxAGC_B_Mcs11_Mcs08, rTxAGC_B_Mcs15_Mcs12 };
265	u8 i, rf, pwr_val[4];
266	u32 write_val;
267	u16 regoffset;
268
269	for (rf = 0; rf < 2; rf++) {
270		write_val = pvalue[rf];
271		for (i = 0; i < 4; i++) {
272			pwr_val[i] = (u8)((write_val & (0x7f<<(i*8)))>>(i*8));
273			if (pwr_val[i]  > RF6052_MAX_TX_PWR)
274				pwr_val[i]  = RF6052_MAX_TX_PWR;
275		}
276		write_val = (pwr_val[3]<<24) | (pwr_val[2]<<16) |
277			    (pwr_val[1]<<8) | pwr_val[0];
278
279		if (rf == 0)
280			regoffset = regoffset_a[index];
281		else
282			regoffset = regoffset_b[index];
283
284		phy_set_bb_reg(adapt, regoffset, bMaskDWord, write_val);
285	}
286}
287
288void rtl88eu_phy_rf6052_set_ofdm_txpower(struct adapter *adapt,
289					 u8 *pwr_level_ofdm,
290					 u8 *pwr_level_bw20,
291					 u8 *pwr_level_bw40, u8 channel)
292{
293	struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
294	u32 write_val[2], powerbase0[2], powerbase1[2], pwrtrac_value;
295	u8 direction;
296	u8 index = 0;
297
298	getpowerbase88e(adapt, pwr_level_ofdm, pwr_level_bw20, pwr_level_bw40,
299			channel, &powerbase0[0], &powerbase1[0]);
300
301	rtl88eu_dm_txpower_track_adjust(&hal_data->odmpriv, 0, &direction,
302					&pwrtrac_value);
303
304	for (index = 0; index < 6; index++) {
305		get_rx_power_val_by_reg(adapt, channel, index,
306					&powerbase0[0], &powerbase1[0],
307					&write_val[0]);
308
309		if (direction == 1) {
310			write_val[0] += pwrtrac_value;
311			write_val[1] += pwrtrac_value;
312		} else if (direction == 2) {
313			write_val[0] -= pwrtrac_value;
314			write_val[1] -= pwrtrac_value;
315		}
316		write_ofdm_pwr_reg(adapt, index, &write_val[0]);
317	}
318}
319