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 }
141 
get_rx_power_val_by_reg(struct adapter * adapt,u8 channel,u8 index,u32 * powerbase0,u32 * powerbase1,u32 * out_val)142 static void get_rx_power_val_by_reg(struct adapter *adapt, u8 channel,
143 				    u8 index, u32 *powerbase0, u32 *powerbase1,
144 				    u32 *out_val)
145 {
146 	struct hal_data_8188e *hal_data = adapt->HalData;
147 	struct dm_priv	*pdmpriv = &hal_data->dmpriv;
148 	u8 i, chnlGroup = 0, pwr_diff_limit[4], customer_pwr_limit;
149 	s8 pwr_diff = 0;
150 	u32 write_val, customer_limit, rf;
151 	u8 regulatory = hal_data->EEPROMRegulatory;
152 
153 	/*  Index 0 & 1= legacy OFDM, 2-5=HT_MCS rate */
154 
155 	for (rf = 0; rf < 2; rf++) {
156 		u8 j = index + (rf ? 8 : 0);
157 
158 		switch (regulatory) {
159 		case 0:
160 			chnlGroup = 0;
161 			write_val = hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][index + (rf ? 8 : 0)] +
162 				((index < 2) ? powerbase0[rf] : powerbase1[rf]);
163 			break;
164 		case 1: /*  Realtek regulatory */
165 			/*  increase power diff defined by Realtek for regulatory */
166 			if (hal_data->pwrGroupCnt == 1)
167 				chnlGroup = 0;
168 			if (hal_data->pwrGroupCnt >= hal_data->PGMaxGroup)
169 				Hal_GetChnlGroup88E(channel, &chnlGroup);
170 
171 			write_val = hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][index + (rf ? 8 : 0)] +
172 					((index < 2) ? powerbase0[rf] : powerbase1[rf]);
173 			break;
174 		case 2:	/*  Better regulatory */
175 				/*  don't increase any power diff */
176 			write_val = (index < 2) ? powerbase0[rf] : powerbase1[rf];
177 			break;
178 		case 3:	/*  Customer defined power diff. */
179 				/*  increase power diff defined by customer. */
180 			chnlGroup = 0;
181 
182 			if (index < 2)
183 				pwr_diff = hal_data->TxPwrLegacyHtDiff[rf][channel - 1];
184 			else if (hal_data->CurrentChannelBW == HT_CHANNEL_WIDTH_20)
185 				pwr_diff = hal_data->TxPwrHt20Diff[rf][channel - 1];
186 
187 			if (hal_data->CurrentChannelBW == HT_CHANNEL_WIDTH_40)
188 				customer_pwr_limit = hal_data->PwrGroupHT40[rf][channel - 1];
189 			else
190 				customer_pwr_limit = hal_data->PwrGroupHT20[rf][channel - 1];
191 
192 			if (pwr_diff >= customer_pwr_limit)
193 				pwr_diff = 0;
194 			else
195 				pwr_diff = customer_pwr_limit - pwr_diff;
196 
197 			for (i = 0; i < 4; i++) {
198 				pwr_diff_limit[i] = (u8)((hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][j] &
199 							 (0x7f << (i * 8))) >> (i * 8));
200 
201 				if (pwr_diff_limit[i] > pwr_diff)
202 					pwr_diff_limit[i] = pwr_diff;
203 			}
204 			customer_limit = (pwr_diff_limit[3] << 24) |
205 					 (pwr_diff_limit[2] << 16) |
206 					 (pwr_diff_limit[1] << 8) |
207 					 (pwr_diff_limit[0]);
208 			write_val = customer_limit + ((index < 2) ? powerbase0[rf] : powerbase1[rf]);
209 			break;
210 		default:
211 			chnlGroup = 0;
212 			write_val = hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][j] +
213 					((index < 2) ? powerbase0[rf] : powerbase1[rf]);
214 			break;
215 		}
216 /*  20100427 Joseph: Driver dynamic Tx power shall not affect Tx power. It shall be determined by power training mechanism. */
217 /*  Currently, we cannot fully disable driver dynamic tx power mechanism because it is referenced by BT coexist mechanism. */
218 /*  In the future, two mechanism shall be separated from each other and maintained independently. Thanks for Lanhsin's reminder. */
219 		/* 92d do not need this */
220 		if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level1)
221 			write_val = 0x14141414;
222 		else if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level2)
223 			write_val = 0x00000000;
224 
225 		*(out_val + rf) = write_val;
226 	}
227 }
228 
write_ofdm_pwr_reg(struct adapter * adapt,u8 index,u32 * pvalue)229 static void write_ofdm_pwr_reg(struct adapter *adapt, u8 index, u32 *pvalue)
230 {
231 	u16 regoffset_a[6] = { rTxAGC_A_Rate18_06, rTxAGC_A_Rate54_24,
232 			       rTxAGC_A_Mcs03_Mcs00, rTxAGC_A_Mcs07_Mcs04,
233 			       rTxAGC_A_Mcs11_Mcs08, rTxAGC_A_Mcs15_Mcs12 };
234 	u16 regoffset_b[6] = { rTxAGC_B_Rate18_06, rTxAGC_B_Rate54_24,
235 			       rTxAGC_B_Mcs03_Mcs00, rTxAGC_B_Mcs07_Mcs04,
236 			       rTxAGC_B_Mcs11_Mcs08, rTxAGC_B_Mcs15_Mcs12 };
237 	u8 i, rf, pwr_val[4];
238 	u32 write_val;
239 	u16 regoffset;
240 
241 	for (rf = 0; rf < 2; rf++) {
242 		write_val = pvalue[rf];
243 		for (i = 0; i < 4; i++) {
244 			pwr_val[i] = (u8)((write_val & (0x7f << (i * 8))) >> (i * 8));
245 			if (pwr_val[i]  > RF6052_MAX_TX_PWR)
246 				pwr_val[i]  = RF6052_MAX_TX_PWR;
247 		}
248 		write_val = (pwr_val[3] << 24) | (pwr_val[2] << 16) |
249 			    (pwr_val[1] << 8) | pwr_val[0];
250 
251 		if (rf == 0)
252 			regoffset = regoffset_a[index];
253 		else
254 			regoffset = regoffset_b[index];
255 
256 		phy_set_bb_reg(adapt, regoffset, bMaskDWord, write_val);
257 	}
258 }
259 
rtl88eu_phy_rf6052_set_ofdm_txpower(struct adapter * adapt,u8 * pwr_level_ofdm,u8 * pwr_level_bw20,u8 * pwr_level_bw40,u8 channel)260 void rtl88eu_phy_rf6052_set_ofdm_txpower(struct adapter *adapt,
261 					 u8 *pwr_level_ofdm,
262 					 u8 *pwr_level_bw20,
263 					 u8 *pwr_level_bw40, u8 channel)
264 {
265 	u32 write_val[2], powerbase0[2], powerbase1[2], pwrtrac_value;
266 	u8 direction;
267 	u8 index = 0;
268 
269 	getpowerbase88e(adapt, pwr_level_ofdm, pwr_level_bw20, pwr_level_bw40,
270 			channel, &powerbase0[0], &powerbase1[0]);
271 
272 	rtl88eu_dm_txpower_track_adjust(&adapt->HalData->odmpriv, 0,
273 					&direction, &pwrtrac_value);
274 
275 	for (index = 0; index < 6; index++) {
276 		get_rx_power_val_by_reg(adapt, channel, index,
277 					&powerbase0[0], &powerbase1[0],
278 					&write_val[0]);
279 
280 		if (direction == 1) {
281 			write_val[0] += pwrtrac_value;
282 			write_val[1] += pwrtrac_value;
283 		} else if (direction == 2) {
284 			write_val[0] -= pwrtrac_value;
285 			write_val[1] -= pwrtrac_value;
286 		}
287 		write_ofdm_pwr_reg(adapt, index, &write_val[0]);
288 	}
289 }
290