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