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