xref: /freebsd/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c (revision a03411e84728e9b267056fd31c7d1d9d1dc1b01e)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 #include "opt_ah.h"
28 
29 #include "ah.h"
30 #include "ah_internal.h"
31 
32 #include "ah_eeprom_v14.h"
33 
34 #include "ar9002/ar9280.h"
35 #include "ar5416/ar5416reg.h"
36 #include "ar5416/ar5416phy.h"
37 #include "ar9002/ar9002phy.h"
38 
39 #include "ar9002/ar9280_olc.h"
40 
41 void
42 ar9280olcInit(struct ath_hal *ah)
43 {
44 	uint32_t i;
45 
46 	/* Only do OLC if it's enabled for this chipset */
47 	if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
48 		return;
49 
50 	HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Setting up TX gain tables.\n", __func__);
51 
52 	for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++)
53 		AH9280(ah)->originalGain[i] = MS(OS_REG_READ(ah,
54 		    AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN);
55 
56 	AH9280(ah)->PDADCdelta = 0;
57 }
58 
59 void
60 ar9280olcGetTxGainIndex(struct ath_hal *ah,
61     const struct ieee80211_channel *chan,
62     struct calDataPerFreqOpLoop *rawDatasetOpLoop,
63     uint8_t *calChans, uint16_t availPiers, uint8_t *pwr, uint8_t *pcdacIdx)
64 {
65 	uint8_t pcdac, i = 0;
66 	uint16_t idxL = 0, idxR = 0, numPiers;
67 	HAL_BOOL match;
68 	CHAN_CENTERS centers;
69 
70 	ar5416GetChannelCenters(ah, chan, &centers);
71 
72 	for (numPiers = 0; numPiers < availPiers; numPiers++)
73 		if (calChans[numPiers] == AR5416_BCHAN_UNUSED)
74 			break;
75 
76 	match = ath_ee_getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center,
77 		    IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers,
78 		    &idxL, &idxR);
79 	if (match) {
80 		pcdac = rawDatasetOpLoop[idxL].pcdac[0][0];
81 		*pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0];
82 	} else {
83 		pcdac = rawDatasetOpLoop[idxR].pcdac[0][0];
84 		*pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] +
85 				rawDatasetOpLoop[idxR].pwrPdg[0][0])/2;
86 	}
87 	while (pcdac > AH9280(ah)->originalGain[i] &&
88 			i < (AR9280_TX_GAIN_TABLE_SIZE - 1))
89 		i++;
90 
91 	*pcdacIdx = i;
92 }
93 
94 /*
95  * XXX txPower here is likely not the target txPower in the traditional
96  * XXX sense, but is set by a call to ar9280olcGetTxGainIndex().
97  * XXX Thus, be careful if you're trying to use this routine yourself.
98  */
99 void
100 ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower,
101     uint8_t *pPDADCValues)
102 {
103 	uint32_t i;
104 	uint32_t offset;
105 
106 	OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
107 	OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
108 
109 	OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain);
110 
111 	offset = txPower;
112 	for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++)
113 		if (i < offset)
114 			pPDADCValues[i] = 0x0;
115 		else
116 			pPDADCValues[i] = 0xFF;
117 }
118 
119 /*
120  * Run temperature compensation calibration.
121  *
122  * The TX gain table is adjusted depending upon the difference
123  * between the initial PDADC value and the currently read
124  * average TX power sample value. This value is only valid if
125  * frames have been transmitted, so currPDADC will be 0 if
126  * no frames have yet been transmitted.
127  */
128 void
129 ar9280olcTemperatureCompensation(struct ath_hal *ah)
130 {
131 	uint32_t rddata, i;
132 	int delta, currPDADC, regval;
133 	uint8_t hpwr_5g = 0;
134 
135 	if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
136 		return;
137 
138 	rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4);
139 	currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT);
140 
141 	HALDEBUG(ah, HAL_DEBUG_PERCAL,
142 	    "%s: called: initPDADC=%d, currPDADC=%d\n",
143 	    __func__, AH5416(ah)->initPDADC, currPDADC);
144 
145 	if (AH5416(ah)->initPDADC == 0 || currPDADC == 0)
146 		return;
147 
148 	(void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g));
149 
150 	if (hpwr_5g)
151 		delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8;
152 	else
153 		delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10;
154 
155 	HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n",
156 	    __func__, delta, AH9280(ah)->PDADCdelta);
157 
158 	if (delta != AH9280(ah)->PDADCdelta) {
159 		AH9280(ah)->PDADCdelta = delta;
160 		for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) {
161 			regval = AH9280(ah)->originalGain[i] - delta;
162 			if (regval < 0)
163 				regval = 0;
164 
165 			OS_REG_RMW_FIELD(ah,
166 				      AR_PHY_TX_GAIN_TBL1 + i * 4,
167 				      AR_PHY_TX_GAIN, regval);
168 		}
169 	}
170 }
171 
172 static int16_t
173 ar9280ChangeGainBoundarySettings(struct ath_hal *ah, uint16_t *gb,
174     uint16_t numXpdGain, uint16_t pdGainOverlap_t2, int8_t pwr_table_offset,
175     int16_t *diff)
176 {
177 	uint16_t k;
178 
179 	/* Prior to writing the boundaries or the pdadc vs. power table
180 	 * into the chip registers the default starting point on the pdadc
181 	 * vs. power table needs to be checked and the curve boundaries
182 	 * adjusted accordingly
183 	 */
184 	if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
185 		uint16_t gb_limit;
186 
187 		if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
188 			/* get the difference in dB */
189 			*diff = (uint16_t)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB);
190 			/* get the number of half dB steps */
191 			*diff *= 2;
192 			/* change the original gain boundary settings
193 			 * by the number of half dB steps
194 			 */
195 			for (k = 0; k < numXpdGain; k++)
196 				gb[k] = (uint16_t)(gb[k] - *diff);
197 		}
198 		/* Because of a hardware limitation, ensure the gain boundary
199 		 * is not larger than (63 - overlap)
200 		 */
201 		gb_limit = (uint16_t)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2);
202 
203 		for (k = 0; k < numXpdGain; k++)
204 			gb[k] = (uint16_t)min(gb_limit, gb[k]);
205 	}
206 
207 	return *diff;
208 }
209 
210 static void
211 ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset,
212     int16_t diff, uint8_t *pdadcValues)
213 {
214 #define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff)
215 	uint16_t k;
216 
217 	/* If this is a board that has a pwrTableOffset that differs from
218 	 * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
219 	 * pdadc vs pwr table needs to be adjusted prior to writing to the
220 	 * chip.
221 	 */
222 	if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
223 		if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
224 			/* shift the table to start at the new offset */
225 			for (k = 0; k < (uint16_t)NUM_PDADC(diff); k++ ) {
226 				pdadcValues[k] = pdadcValues[k + diff];
227 			}
228 
229 			/* fill the back of the table */
230 			for (k = (uint16_t)NUM_PDADC(diff); k < NUM_PDADC(0); k++) {
231 				pdadcValues[k] = pdadcValues[NUM_PDADC(diff)];
232 			}
233 		}
234 	}
235 #undef NUM_PDADC
236 }
237 /*
238  * This effectively disables the gain boundaries leaving it
239  * to the open-loop TX power control.
240  */
241 static void
242 ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int i,
243     uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[])
244 {
245 	int regChainOffset;
246 
247 	regChainOffset = ar5416GetRegChainOffset(ah, i);
248 
249 	/* These are unused for OLC */
250 	(void) pdGainOverlap_t2;
251 	(void) gainBoundaries;
252 
253 	HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: writing closed loop values\n",
254 	    __func__, i);
255 
256 	OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset,
257 	    SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |
258 	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1)  |
259 	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2)  |
260 	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3)  |
261 	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
262 }
263 
264 /* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */
265 /* XXX shouldn't be here! */
266 #define EEP_MINOR(_ah) \
267         (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK)
268 #define IS_EEP_MINOR_V2(_ah)    (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2)
269 #define IS_EEP_MINOR_V3(_ah)    (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3)
270 
271 /**************************************************************
272  * ar9280SetPowerCalTable
273  *
274  * Pull the PDADC piers from cal data and interpolate them across the given
275  * points as well as from the nearest pier(s) to get a power detector
276  * linear voltage to power level table.
277  *
278  * Handle OLC for Merlin where required.
279  */
280 HAL_BOOL
281 ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData,
282 	const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset)
283 {
284 	CAL_DATA_PER_FREQ *pRawDataset;
285 	uint8_t  *pCalBChans = AH_NULL;
286 	uint16_t pdGainOverlap_t2;
287 	static uint8_t  pdadcValues[AR5416_NUM_PDADC_VALUES];
288 	uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK];
289 	uint16_t numPiers, i;
290 	int16_t  tMinCalPower;
291 	uint16_t numXpdGain, xpdMask;
292 	uint16_t xpdGainValues[AR5416_NUM_PD_GAINS];
293 	uint32_t regChainOffset;
294 	int8_t pwr_table_offset;
295 
296 	OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues));
297 
298 	xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain;
299 
300 	(void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset);
301 
302 	if (IS_EEP_MINOR_V2(ah)) {
303 		pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap;
304 	} else {
305 		pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
306 	}
307 
308 	if (IEEE80211_IS_CHAN_2GHZ(chan)) {
309 		pCalBChans = pEepData->calFreqPier2G;
310 		numPiers = AR5416_NUM_2G_CAL_PIERS;
311 	} else {
312 		pCalBChans = pEepData->calFreqPier5G;
313 		numPiers = AR5416_NUM_5G_CAL_PIERS;
314 	}
315 
316 	/* If OLC is being done, set the init PDADC value appropriately */
317 	if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) &&
318 	    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
319 		struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0];
320 		AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0];
321 	} else {
322 		/*
323 		 * XXX ath9k doesn't clear this for 5ghz mode if
324 		 * it were set in 2ghz mode before!
325 		 * The Merlin OLC temperature compensation code
326 		 * uses this to calculate the PDADC delta during
327 		 * calibration ; 0 here effectively stops the
328 		 * temperature compensation calibration from
329 		 * occurring.
330 		 */
331 		AH5416(ah)->initPDADC = 0;
332 	}
333 
334 	/* Calculate the value of xpdgains from the xpdGain Mask */
335 	numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues);
336 
337 	/* Write the detector gain biases and their number */
338 	ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues);
339 
340 	for (i = 0; i < AR5416_MAX_CHAINS; i++) {
341 		regChainOffset = ar5416GetRegChainOffset(ah, i);
342 		if (pEepData->baseEepHeader.txMask & (1 << i)) {
343 			uint16_t diff;
344 
345 			if (IEEE80211_IS_CHAN_2GHZ(chan)) {
346 				pRawDataset = pEepData->calPierData2G[i];
347 			} else {
348 				pRawDataset = pEepData->calPierData5G[i];
349 			}
350 
351 			/* Fetch the gain boundaries and the PDADC values */
352 			if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
353 			    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
354 				uint8_t pcdacIdx;
355 				uint8_t txPower;
356 
357 				ar9280olcGetTxGainIndex(ah, chan,
358 				    (struct calDataPerFreqOpLoop *) pRawDataset,
359 				    pCalBChans, numPiers, &txPower, &pcdacIdx);
360 				ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues);
361 			} else {
362 				ar5416GetGainBoundariesAndPdadcs(ah,  chan,
363 				    pRawDataset, pCalBChans, numPiers,
364 				    pdGainOverlap_t2, &tMinCalPower,
365 				    gainBoundaries, pdadcValues, numXpdGain);
366 			}
367 
368 			/*
369 			 * Prior to writing the boundaries or the pdadc vs. power table
370 			 * into the chip registers the default starting point on the pdadc
371 			 * vs. power table needs to be checked and the curve boundaries
372 			 * adjusted accordingly
373 			 */
374 			diff = ar9280ChangeGainBoundarySettings(ah,
375 			    gainBoundaries, numXpdGain, pdGainOverlap_t2,
376 			    pwr_table_offset, &diff);
377 
378 			if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
379 				/* Set gain boundaries for either open- or closed-loop TPC */
380 				if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
381 				    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
382 					ar9280SetGainBoundariesOpenLoop(ah,
383 					    i, pdGainOverlap_t2,
384 					    gainBoundaries);
385 				else
386 					ar5416SetGainBoundariesClosedLoop(ah,
387 					    i, pdGainOverlap_t2,
388 					    gainBoundaries);
389 			}
390 
391 			/*
392 			 * If this is a board that has a pwrTableOffset that differs from
393 			 * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
394 			 * pdadc vs pwr table needs to be adjusted prior to writing to the
395 			 * chip.
396 			 */
397 			ar9280AdjustPDADCValues(ah, pwr_table_offset, diff, pdadcValues);
398 
399 			/* Write the power values into the baseband power table */
400 			ar5416WritePdadcValues(ah, i, pdadcValues);
401 		}
402 	}
403 	*pTxPowerIndexOffset = 0;
404 
405 	return AH_TRUE;
406 }
407