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 "odm_precomp.h"
17 
18 static void odm_SetCrystalCap(void *pDM_VOID, u8 CrystalCap)
19 {
20 	PDM_ODM_T pDM_Odm = (PDM_ODM_T)pDM_VOID;
21 	PCFO_TRACKING pCfoTrack = &pDM_Odm->DM_CfoTrack;
22 	bool bEEPROMCheck;
23 	struct adapter *Adapter = pDM_Odm->Adapter;
24 	struct hal_com_data *pHalData = GET_HAL_DATA(Adapter);
25 
26 	bEEPROMCheck = (pHalData->EEPROMVersion >= 0x01) ? true : false;
27 
28 	if (pCfoTrack->CrystalCap == CrystalCap)
29 		return;
30 
31 	pCfoTrack->CrystalCap = CrystalCap;
32 
33 	/*  0x2C[23:18] = 0x2C[17:12] = CrystalCap */
34 	CrystalCap = CrystalCap & 0x3F;
35 	PHY_SetBBReg(
36 		pDM_Odm->Adapter,
37 		REG_MAC_PHY_CTRL,
38 		0x00FFF000,
39 		(CrystalCap | (CrystalCap << 6))
40 	);
41 
42 	ODM_RT_TRACE(
43 		pDM_Odm,
44 		ODM_COMP_CFO_TRACKING,
45 		ODM_DBG_LOUD,
46 		(
47 			"odm_SetCrystalCap(): CrystalCap = 0x%x\n",
48 			CrystalCap
49 		)
50 	);
51 }
52 
53 static u8 odm_GetDefaultCrytaltalCap(void *pDM_VOID)
54 {
55 	PDM_ODM_T pDM_Odm = (PDM_ODM_T)pDM_VOID;
56 	u8 CrystalCap = 0x20;
57 
58 	struct adapter *Adapter = pDM_Odm->Adapter;
59 	struct hal_com_data *pHalData = GET_HAL_DATA(Adapter);
60 
61 	CrystalCap = pHalData->CrystalCap;
62 
63 	CrystalCap = CrystalCap & 0x3f;
64 
65 	return CrystalCap;
66 }
67 
68 static void odm_SetATCStatus(void *pDM_VOID, bool ATCStatus)
69 {
70 	PDM_ODM_T pDM_Odm = (PDM_ODM_T)pDM_VOID;
71 	PCFO_TRACKING pCfoTrack = &pDM_Odm->DM_CfoTrack;
72 
73 	if (pCfoTrack->bATCStatus == ATCStatus)
74 		return;
75 
76 	PHY_SetBBReg(
77 		pDM_Odm->Adapter,
78 		ODM_REG(BB_ATC, pDM_Odm),
79 		ODM_BIT(BB_ATC, pDM_Odm),
80 		ATCStatus
81 	);
82 	pCfoTrack->bATCStatus = ATCStatus;
83 }
84 
85 static bool odm_GetATCStatus(void *pDM_VOID)
86 {
87 	bool ATCStatus;
88 	PDM_ODM_T pDM_Odm = (PDM_ODM_T)pDM_VOID;
89 
90 	ATCStatus = (bool)PHY_QueryBBReg(
91 		pDM_Odm->Adapter,
92 		ODM_REG(BB_ATC, pDM_Odm),
93 		ODM_BIT(BB_ATC, pDM_Odm)
94 	);
95 	return ATCStatus;
96 }
97 
98 void ODM_CfoTrackingReset(void *pDM_VOID)
99 {
100 	PDM_ODM_T pDM_Odm = (PDM_ODM_T)pDM_VOID;
101 	PCFO_TRACKING pCfoTrack = &pDM_Odm->DM_CfoTrack;
102 
103 	pCfoTrack->DefXCap = odm_GetDefaultCrytaltalCap(pDM_Odm);
104 	pCfoTrack->bAdjust = true;
105 
106 	odm_SetCrystalCap(pDM_Odm, pCfoTrack->DefXCap);
107 	odm_SetATCStatus(pDM_Odm, true);
108 }
109 
110 void ODM_CfoTrackingInit(void *pDM_VOID)
111 {
112 	PDM_ODM_T pDM_Odm = (PDM_ODM_T)pDM_VOID;
113 	PCFO_TRACKING pCfoTrack = &pDM_Odm->DM_CfoTrack;
114 
115 	pCfoTrack->DefXCap =
116 		pCfoTrack->CrystalCap = odm_GetDefaultCrytaltalCap(pDM_Odm);
117 	pCfoTrack->bATCStatus = odm_GetATCStatus(pDM_Odm);
118 	pCfoTrack->bAdjust = true;
119 	ODM_RT_TRACE(
120 		pDM_Odm,
121 		ODM_COMP_CFO_TRACKING,
122 		ODM_DBG_LOUD,
123 		("ODM_CfoTracking_init() =========>\n")
124 	);
125 	ODM_RT_TRACE(
126 		pDM_Odm,
127 		ODM_COMP_CFO_TRACKING,
128 		ODM_DBG_LOUD,
129 		(
130 			"ODM_CfoTracking_init(): bATCStatus = %d, CrystalCap = 0x%x\n",
131 			pCfoTrack->bATCStatus,
132 			pCfoTrack->DefXCap
133 		)
134 	);
135 }
136 
137 void ODM_CfoTracking(void *pDM_VOID)
138 {
139 	PDM_ODM_T pDM_Odm = (PDM_ODM_T)pDM_VOID;
140 	PCFO_TRACKING pCfoTrack = &pDM_Odm->DM_CfoTrack;
141 	int CFO_kHz_A, CFO_kHz_B, CFO_ave = 0;
142 	int CFO_ave_diff;
143 	int CrystalCap = (int)pCfoTrack->CrystalCap;
144 	u8 Adjust_Xtal = 1;
145 
146 	/* 4 Support ability */
147 	if (!(pDM_Odm->SupportAbility & ODM_BB_CFO_TRACKING)) {
148 		ODM_RT_TRACE(
149 			pDM_Odm,
150 			ODM_COMP_CFO_TRACKING,
151 			ODM_DBG_LOUD,
152 			("ODM_CfoTracking(): Return: SupportAbility ODM_BB_CFO_TRACKING is disabled\n")
153 		);
154 		return;
155 	}
156 
157 	ODM_RT_TRACE(
158 		pDM_Odm,
159 		ODM_COMP_CFO_TRACKING,
160 		ODM_DBG_LOUD,
161 		("ODM_CfoTracking() =========>\n")
162 	);
163 
164 	if (!pDM_Odm->bLinked || !pDM_Odm->bOneEntryOnly) {
165 		/* 4 No link or more than one entry */
166 		ODM_CfoTrackingReset(pDM_Odm);
167 		ODM_RT_TRACE(
168 			pDM_Odm,
169 			ODM_COMP_CFO_TRACKING,
170 			ODM_DBG_LOUD,
171 			(
172 				"ODM_CfoTracking(): Reset: bLinked = %d, bOneEntryOnly = %d\n",
173 				pDM_Odm->bLinked,
174 				pDM_Odm->bOneEntryOnly
175 			)
176 		);
177 	} else {
178 		/* 3 1. CFO Tracking */
179 		/* 4 1.1 No new packet */
180 		if (pCfoTrack->packetCount == pCfoTrack->packetCount_pre) {
181 			ODM_RT_TRACE(
182 				pDM_Odm,
183 				ODM_COMP_CFO_TRACKING,
184 				ODM_DBG_LOUD,
185 				(
186 					"ODM_CfoTracking(): packet counter doesn't change\n"
187 				)
188 			);
189 			return;
190 		}
191 		pCfoTrack->packetCount_pre = pCfoTrack->packetCount;
192 
193 		/* 4 1.2 Calculate CFO */
194 		CFO_kHz_A =  (int)(pCfoTrack->CFO_tail[0] * 3125)  / 1280;
195 		CFO_kHz_B =  (int)(pCfoTrack->CFO_tail[1] * 3125)  / 1280;
196 
197 		if (pDM_Odm->RFType < ODM_2T2R)
198 			CFO_ave = CFO_kHz_A;
199 		else
200 			CFO_ave = (int)(CFO_kHz_A + CFO_kHz_B) >> 1;
201 		ODM_RT_TRACE(
202 			pDM_Odm,
203 			ODM_COMP_CFO_TRACKING,
204 			ODM_DBG_LOUD,
205 			(
206 				"ODM_CfoTracking(): CFO_kHz_A = %dkHz, CFO_kHz_B = %dkHz, CFO_ave = %dkHz\n",
207 				CFO_kHz_A,
208 				CFO_kHz_B,
209 				CFO_ave
210 			)
211 		);
212 
213 		/* 4 1.3 Avoid abnormal large CFO */
214 		CFO_ave_diff =
215 			(pCfoTrack->CFO_ave_pre >= CFO_ave) ?
216 			(pCfoTrack->CFO_ave_pre-CFO_ave) :
217 			(CFO_ave-pCfoTrack->CFO_ave_pre);
218 
219 		if (
220 			CFO_ave_diff > 20 &&
221 			pCfoTrack->largeCFOHit == 0 &&
222 			!pCfoTrack->bAdjust
223 		) {
224 			ODM_RT_TRACE(pDM_Odm, ODM_COMP_CFO_TRACKING, ODM_DBG_LOUD, ("ODM_CfoTracking(): first large CFO hit\n"));
225 			pCfoTrack->largeCFOHit = 1;
226 			return;
227 		} else
228 			pCfoTrack->largeCFOHit = 0;
229 		pCfoTrack->CFO_ave_pre = CFO_ave;
230 
231 		/* 4 1.4 Dynamic Xtal threshold */
232 		if (pCfoTrack->bAdjust == false) {
233 			if (CFO_ave > CFO_TH_XTAL_HIGH || CFO_ave < (-CFO_TH_XTAL_HIGH))
234 				pCfoTrack->bAdjust = true;
235 		} else {
236 			if (CFO_ave < CFO_TH_XTAL_LOW && CFO_ave > (-CFO_TH_XTAL_LOW))
237 				pCfoTrack->bAdjust = false;
238 		}
239 
240 		/* 4 1.5 BT case: Disable CFO tracking */
241 		if (pDM_Odm->bBtEnabled) {
242 			pCfoTrack->bAdjust = false;
243 			odm_SetCrystalCap(pDM_Odm, pCfoTrack->DefXCap);
244 			ODM_RT_TRACE(
245 				pDM_Odm,
246 				ODM_COMP_CFO_TRACKING,
247 				ODM_DBG_LOUD,
248 				("ODM_CfoTracking(): Disable CFO tracking for BT!!\n")
249 			);
250 		}
251 
252 		/* 4 1.6 Big jump */
253 		if (pCfoTrack->bAdjust) {
254 			if (CFO_ave > CFO_TH_XTAL_LOW)
255 				Adjust_Xtal = Adjust_Xtal+((CFO_ave-CFO_TH_XTAL_LOW)>>2);
256 			else if (CFO_ave < (-CFO_TH_XTAL_LOW))
257 				Adjust_Xtal = Adjust_Xtal+((CFO_TH_XTAL_LOW-CFO_ave)>>2);
258 
259 			ODM_RT_TRACE(
260 				pDM_Odm,
261 				ODM_COMP_CFO_TRACKING,
262 				ODM_DBG_LOUD,
263 				(
264 					"ODM_CfoTracking(): Crystal cap offset = %d\n",
265 					Adjust_Xtal
266 				)
267 			);
268 		}
269 
270 		/* 4 1.7 Adjust Crystal Cap. */
271 		if (pCfoTrack->bAdjust) {
272 			if (CFO_ave > CFO_TH_XTAL_LOW)
273 				CrystalCap = CrystalCap + Adjust_Xtal;
274 			else if (CFO_ave < (-CFO_TH_XTAL_LOW))
275 				CrystalCap = CrystalCap - Adjust_Xtal;
276 
277 			if (CrystalCap > 0x3f)
278 				CrystalCap = 0x3f;
279 			else if (CrystalCap < 0)
280 				CrystalCap = 0;
281 
282 			odm_SetCrystalCap(pDM_Odm, (u8)CrystalCap);
283 		}
284 		ODM_RT_TRACE(
285 			pDM_Odm,
286 			ODM_COMP_CFO_TRACKING,
287 			ODM_DBG_LOUD,
288 			(
289 				"ODM_CfoTracking(): Crystal cap = 0x%x, Default Crystal cap = 0x%x\n",
290 				pCfoTrack->CrystalCap,
291 				pCfoTrack->DefXCap
292 			)
293 		);
294 
295 		/* 3 2. Dynamic ATC switch */
296 		if (CFO_ave < CFO_TH_ATC && CFO_ave > -CFO_TH_ATC) {
297 			odm_SetATCStatus(pDM_Odm, false);
298 			ODM_RT_TRACE(
299 				pDM_Odm,
300 				ODM_COMP_CFO_TRACKING,
301 				ODM_DBG_LOUD,
302 				("ODM_CfoTracking(): Disable ATC!!\n")
303 			);
304 		} else {
305 			odm_SetATCStatus(pDM_Odm, true);
306 			ODM_RT_TRACE(
307 				pDM_Odm,
308 				ODM_COMP_CFO_TRACKING,
309 				ODM_DBG_LOUD,
310 				("ODM_CfoTracking(): Enable ATC!!\n")
311 			);
312 		}
313 	}
314 }
315 
316 void ODM_ParsingCFO(void *pDM_VOID, void *pPktinfo_VOID, s8 *pcfotail)
317 {
318 	PDM_ODM_T pDM_Odm = (PDM_ODM_T)pDM_VOID;
319 	PODM_PACKET_INFO_T pPktinfo = (PODM_PACKET_INFO_T)pPktinfo_VOID;
320 	PCFO_TRACKING pCfoTrack = &pDM_Odm->DM_CfoTrack;
321 	u8 i;
322 
323 	if (!(pDM_Odm->SupportAbility & ODM_BB_CFO_TRACKING))
324 		return;
325 
326 	if (pPktinfo->StationID != 0) {
327 		/* 3 Update CFO report for path-A & path-B */
328 		/*  Only paht-A and path-B have CFO tail and short CFO */
329 		for (i = ODM_RF_PATH_A; i <= ODM_RF_PATH_B; i++)
330 			pCfoTrack->CFO_tail[i] = (int)pcfotail[i];
331 
332 		/* 3 Update packet counter */
333 		if (pCfoTrack->packetCount == 0xffffffff)
334 			pCfoTrack->packetCount = 0;
335 		else
336 			pCfoTrack->packetCount++;
337 	}
338 }
339