1331722Seadler/*
2222301Sadrian * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
3222301Sadrian *
4222301Sadrian * Redistribution and use in source and binary forms, with or without
5222301Sadrian * modification, are permitted provided that the following conditions
6222301Sadrian * are met:
7222301Sadrian * 1. Redistributions of source code must retain the above copyright
8222301Sadrian *    notice, this list of conditions and the following disclaimer.
9222301Sadrian * 2. Redistributions in binary form must reproduce the above copyright
10222301Sadrian *    notice, this list of conditions and the following disclaimer in the
11222301Sadrian *    documentation and/or other materials provided with the distribution.
12222301Sadrian *
13222301Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14222301Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15222301Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16222301Sadrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17222301Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18222301Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19222301Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20222301Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21222301Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22222301Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23222301Sadrian * SUCH DAMAGE.
24222301Sadrian *
25222301Sadrian * $FreeBSD$
26222301Sadrian */
27222301Sadrian#include "opt_ah.h"
28222301Sadrian
29222301Sadrian#include "ah.h"
30222301Sadrian#include "ah_internal.h"
31222301Sadrian
32222301Sadrian#include "ah_eeprom_v14.h"
33222308Sadrian#include "ah_eeprom_9287.h"
34222301Sadrian
35222301Sadrian#include "ar9002/ar9280.h"
36222301Sadrian#include "ar5416/ar5416reg.h"
37222301Sadrian#include "ar5416/ar5416phy.h"
38222301Sadrian#include "ar9002/ar9002phy.h"
39222301Sadrian
40222301Sadrian#include "ar9002/ar9287phy.h"
41222301Sadrian#include "ar9002/ar9287an.h"
42222301Sadrian#include "ar9002/ar9287_olc.h"
43222301Sadrian
44222301Sadrianvoid
45222301Sadrianar9287olcInit(struct ath_hal *ah)
46222301Sadrian{
47222301Sadrian	OS_REG_SET_BIT(ah, AR_PHY_TX_PWRCTRL9,
48222301Sadrian	    AR_PHY_TX_PWRCTRL9_RES_DC_REMOVAL);
49222301Sadrian	OS_A_REG_RMW_FIELD(ah, AR9287_AN_TXPC0,
50222301Sadrian	    AR9287_AN_TXPC0_TXPCMODE,
51222301Sadrian	    AR9287_AN_TXPC0_TXPCMODE_TEMPSENSE);
52222301Sadrian	OS_DELAY(100);
53222301Sadrian}
54222301Sadrian
55222301Sadrian/*
56222301Sadrian * Run temperature compensation calibration.
57222301Sadrian *
58222301Sadrian * The TX gain table is adjusted depending upon the difference
59222301Sadrian * between the initial PDADC value and the currently read
60222301Sadrian * average TX power sample value. This value is only valid if
61222301Sadrian * frames have been transmitted, so currPDADC will be 0 if
62222301Sadrian * no frames have yet been transmitted.
63222301Sadrian */
64222301Sadrianvoid
65222301Sadrianar9287olcTemperatureCompensation(struct ath_hal *ah)
66222301Sadrian{
67222301Sadrian	uint32_t rddata;
68222301Sadrian	int32_t delta, currPDADC, slope;
69222301Sadrian
70222301Sadrian	rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4);
71222301Sadrian	currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT);
72222301Sadrian
73222314Sadrian	HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: initPDADC=%d, currPDADC=%d\n",
74222314Sadrian	     __func__, AH5416(ah)->initPDADC, currPDADC);
75222314Sadrian
76222301Sadrian	if (AH5416(ah)->initPDADC == 0 || currPDADC == 0) {
77222301Sadrian		/*
78222301Sadrian		 * Zero value indicates that no frames have been transmitted
79222301Sadrian		 * yet, can't do temperature compensation until frames are
80222301Sadrian		 * transmitted.
81222301Sadrian		 */
82222301Sadrian		return;
83222301Sadrian	} else {
84222301Sadrian		int8_t val;
85222301Sadrian		(void) (ath_hal_eepromGet(ah, AR_EEP_TEMPSENSE_SLOPE, &val));
86222301Sadrian		slope = val;
87222301Sadrian
88222301Sadrian		if (slope == 0) { /* to avoid divide by zero case */
89222301Sadrian			delta = 0;
90222301Sadrian		} else {
91222301Sadrian			delta = ((currPDADC - AH5416(ah)->initPDADC)*4) / slope;
92222301Sadrian		}
93222301Sadrian		OS_REG_RMW_FIELD(ah, AR_PHY_CH0_TX_PWRCTRL11,
94222301Sadrian		    AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta);
95222301Sadrian		OS_REG_RMW_FIELD(ah, AR_PHY_CH1_TX_PWRCTRL11,
96222301Sadrian		    AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta);
97222314Sadrian
98222314Sadrian		HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d\n", __func__, delta);
99222301Sadrian	}
100222301Sadrian}
101222308Sadrian
102222308Sadrianvoid
103222308Sadrianar9287olcGetTxGainIndex(struct ath_hal *ah,
104222308Sadrian    const struct ieee80211_channel *chan,
105222308Sadrian    struct cal_data_op_loop_ar9287 *pRawDatasetOpLoop,
106222308Sadrian    uint8_t *pCalChans,  uint16_t availPiers, int8_t *pPwr)
107222308Sadrian{
108222308Sadrian        uint16_t idxL = 0, idxR = 0, numPiers;
109222308Sadrian        HAL_BOOL match;
110222308Sadrian        CHAN_CENTERS centers;
111222308Sadrian
112222308Sadrian        ar5416GetChannelCenters(ah, chan, &centers);
113222308Sadrian
114222308Sadrian        for (numPiers = 0; numPiers < availPiers; numPiers++) {
115222308Sadrian                if (pCalChans[numPiers] == AR5416_BCHAN_UNUSED)
116222308Sadrian                        break;
117222308Sadrian        }
118222308Sadrian
119222308Sadrian        match = ath_ee_getLowerUpperIndex(
120222308Sadrian                (uint8_t)FREQ2FBIN(centers.synth_center, IEEE80211_IS_CHAN_2GHZ(chan)),
121222308Sadrian                pCalChans, numPiers, &idxL, &idxR);
122222308Sadrian
123222308Sadrian        if (match) {
124222308Sadrian                *pPwr = (int8_t) pRawDatasetOpLoop[idxL].pwrPdg[0][0];
125222308Sadrian        } else {
126222308Sadrian                *pPwr = ((int8_t) pRawDatasetOpLoop[idxL].pwrPdg[0][0] +
127222308Sadrian                         (int8_t) pRawDatasetOpLoop[idxR].pwrPdg[0][0])/2;
128222308Sadrian        }
129222308Sadrian}
130222308Sadrian
131222308Sadrianvoid
132222308Sadrianar9287olcSetPDADCs(struct ath_hal *ah, int32_t txPower,
133222308Sadrian    uint16_t chain)
134222308Sadrian{
135222308Sadrian        uint32_t tmpVal;
136222308Sadrian        uint32_t a;
137222308Sadrian
138222308Sadrian        /* Enable OLPC for chain 0 */
139222308Sadrian
140222308Sadrian        tmpVal = OS_REG_READ(ah, 0xa270);
141222308Sadrian        tmpVal = tmpVal & 0xFCFFFFFF;
142222308Sadrian        tmpVal = tmpVal | (0x3 << 24);
143222308Sadrian        OS_REG_WRITE(ah, 0xa270, tmpVal);
144222308Sadrian
145222308Sadrian        /* Enable OLPC for chain 1 */
146222308Sadrian
147222308Sadrian        tmpVal = OS_REG_READ(ah, 0xb270);
148222308Sadrian        tmpVal = tmpVal & 0xFCFFFFFF;
149222308Sadrian        tmpVal = tmpVal | (0x3 << 24);
150222308Sadrian        OS_REG_WRITE(ah, 0xb270, tmpVal);
151222308Sadrian
152222308Sadrian        /* Write the OLPC ref power for chain 0 */
153222308Sadrian
154222308Sadrian        if (chain == 0) {
155222308Sadrian                tmpVal = OS_REG_READ(ah, 0xa398);
156222308Sadrian                tmpVal = tmpVal & 0xff00ffff;
157222308Sadrian                a = (txPower)&0xff;
158222308Sadrian                tmpVal = tmpVal | (a << 16);
159222308Sadrian                OS_REG_WRITE(ah, 0xa398, tmpVal);
160222308Sadrian        }
161222308Sadrian
162222308Sadrian        /* Write the OLPC ref power for chain 1 */
163222308Sadrian
164222308Sadrian        if (chain == 1) {
165222308Sadrian                tmpVal = OS_REG_READ(ah, 0xb398);
166222308Sadrian                tmpVal = tmpVal & 0xff00ffff;
167222308Sadrian                a = (txPower)&0xff;
168222308Sadrian                tmpVal = tmpVal | (a << 16);
169222308Sadrian                OS_REG_WRITE(ah, 0xb398, tmpVal);
170222308Sadrian        }
171222308Sadrian}
172