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