1/******************************************************************************
2 *
3 * Copyright(c) 2009-2012  Realtek Corporation.
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 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
17 *
18 * The full GNU General Public License is included in this distribution in the
19 * file called LICENSE.
20 *
21 * Contact Information:
22 * wlanfae <wlanfae@realtek.com>
23 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
24 * Hsinchu 300, Taiwan.
25 *
26 * Larry Finger <Larry.Finger@lwfinger.net>
27 *
28 *****************************************************************************/
29
30#include "../wifi.h"
31#include "reg.h"
32#include "def.h"
33#include "phy.h"
34#include "rf.h"
35#include "dm.h"
36
37
38static void _rtl92s_get_powerbase(struct ieee80211_hw *hw, u8 *p_pwrlevel,
39				  u8 chnl, u32 *ofdmbase, u32 *mcsbase,
40				  u8 *p_final_pwridx)
41{
42	struct rtl_priv *rtlpriv = rtl_priv(hw);
43	struct rtl_phy *rtlphy = &(rtlpriv->phy);
44	struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
45	u32 pwrbase0, pwrbase1;
46	u8 legacy_pwrdiff = 0, ht20_pwrdiff = 0;
47	u8 i, pwrlevel[4];
48
49	for (i = 0; i < 2; i++)
50		pwrlevel[i] = p_pwrlevel[i];
51
52	/* We only care about the path A for legacy. */
53	if (rtlefuse->eeprom_version < 2) {
54		pwrbase0 = pwrlevel[0] + (rtlefuse->legacy_httxpowerdiff & 0xf);
55	} else {
56		legacy_pwrdiff = rtlefuse->txpwr_legacyhtdiff
57						[RF90_PATH_A][chnl - 1];
58
59		/* For legacy OFDM, tx pwr always > HT OFDM pwr.
60		 * We do not care Path B
61		 * legacy OFDM pwr diff. NO BB register
62		 * to notify HW. */
63		pwrbase0 = pwrlevel[0] + legacy_pwrdiff;
64	}
65
66	pwrbase0 = (pwrbase0 << 24) | (pwrbase0 << 16) | (pwrbase0 << 8) |
67		    pwrbase0;
68	*ofdmbase = pwrbase0;
69
70	/* MCS rates */
71	if (rtlefuse->eeprom_version >= 2) {
72		/* Check HT20 to HT40 diff	*/
73		if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20) {
74			for (i = 0; i < 2; i++) {
75				/* rf-A, rf-B */
76				/* HT 20<->40 pwr diff */
77				ht20_pwrdiff = rtlefuse->txpwr_ht20diff
78							[i][chnl - 1];
79
80				if (ht20_pwrdiff < 8) /* 0~+7 */
81					pwrlevel[i] += ht20_pwrdiff;
82				else /* index8-15=-8~-1 */
83					pwrlevel[i] -= (16 - ht20_pwrdiff);
84			}
85		}
86	}
87
88	/* use index of rf-A */
89	pwrbase1 = pwrlevel[0];
90	pwrbase1 = (pwrbase1 << 24) | (pwrbase1 << 16) | (pwrbase1 << 8) |
91				pwrbase1;
92	*mcsbase = pwrbase1;
93
94	/* The following is for Antenna
95	 * diff from Ant-B to Ant-A */
96	p_final_pwridx[0] = pwrlevel[0];
97	p_final_pwridx[1] = pwrlevel[1];
98
99	switch (rtlefuse->eeprom_regulatory) {
100	case 3:
101		/* The following is for calculation
102		 * of the power diff for Ant-B to Ant-A. */
103		if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) {
104			p_final_pwridx[0] += rtlefuse->pwrgroup_ht40
105						[RF90_PATH_A][
106						chnl - 1];
107			p_final_pwridx[1] += rtlefuse->pwrgroup_ht40
108						[RF90_PATH_B][
109						chnl - 1];
110		} else {
111			p_final_pwridx[0] += rtlefuse->pwrgroup_ht20
112						[RF90_PATH_A][
113						chnl - 1];
114			p_final_pwridx[1] += rtlefuse->pwrgroup_ht20
115						[RF90_PATH_B][
116						chnl - 1];
117		}
118		break;
119	default:
120		break;
121	}
122
123	if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) {
124		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
125			 "40MHz finalpwr_idx (A / B) = 0x%x / 0x%x\n",
126			 p_final_pwridx[0], p_final_pwridx[1]);
127	} else {
128		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
129			 "20MHz finalpwr_idx (A / B) = 0x%x / 0x%x\n",
130			 p_final_pwridx[0], p_final_pwridx[1]);
131	}
132}
133
134static void _rtl92s_set_antennadiff(struct ieee80211_hw *hw,
135				    u8 *p_final_pwridx)
136{
137	struct rtl_priv *rtlpriv = rtl_priv(hw);
138	struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
139	struct rtl_phy *rtlphy = &(rtlpriv->phy);
140	char ant_pwr_diff = 0;
141	u32	u4reg_val = 0;
142
143	if (rtlphy->rf_type == RF_2T2R) {
144		ant_pwr_diff = p_final_pwridx[1] - p_final_pwridx[0];
145
146		/* range is from 7~-8,
147		 * index = 0x0~0xf */
148		if (ant_pwr_diff > 7)
149			ant_pwr_diff = 7;
150		if (ant_pwr_diff < -8)
151			ant_pwr_diff = -8;
152
153		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
154			 "Antenna Diff from RF-B to RF-A = %d (0x%x)\n",
155			 ant_pwr_diff, ant_pwr_diff & 0xf);
156
157		ant_pwr_diff &= 0xf;
158	}
159
160	/* Antenna TX power difference */
161	rtlefuse->antenna_txpwdiff[2] = 0;/* RF-D, don't care */
162	rtlefuse->antenna_txpwdiff[1] = 0;/* RF-C, don't care */
163	rtlefuse->antenna_txpwdiff[0] = (u8)(ant_pwr_diff);	/* RF-B */
164
165	u4reg_val = rtlefuse->antenna_txpwdiff[2] << 8 |
166				rtlefuse->antenna_txpwdiff[1] << 4 |
167				rtlefuse->antenna_txpwdiff[0];
168
169	rtl_set_bbreg(hw, RFPGA0_TXGAINSTAGE, (BXBTXAGC | BXCTXAGC | BXDTXAGC),
170		      u4reg_val);
171
172	RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Write BCD-Diff(0x%x) = 0x%x\n",
173		 RFPGA0_TXGAINSTAGE, u4reg_val);
174}
175
176static void _rtl92s_get_txpower_writeval_byregulatory(struct ieee80211_hw *hw,
177						      u8 chnl, u8 index,
178						      u32 pwrbase0,
179						      u32 pwrbase1,
180						      u32 *p_outwrite_val)
181{
182	struct rtl_priv *rtlpriv = rtl_priv(hw);
183	struct rtl_phy *rtlphy = &(rtlpriv->phy);
184	struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
185	u8 i, chnlgroup, pwrdiff_limit[4];
186	u32 writeval, customer_limit;
187
188	/* Index 0 & 1= legacy OFDM, 2-5=HT_MCS rate */
189	switch (rtlefuse->eeprom_regulatory) {
190	case 0:
191		/* Realtek better performance increase power diff
192		 * defined by Realtek for large power */
193		chnlgroup = 0;
194
195		writeval = rtlphy->mcs_offset[chnlgroup][index] +
196				((index < 2) ? pwrbase0 : pwrbase1);
197
198		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
199			 "RTK better performance, writeval = 0x%x\n", writeval);
200		break;
201	case 1:
202		/* Realtek regulatory increase power diff defined
203		 * by Realtek for regulatory */
204		if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) {
205			writeval = ((index < 2) ? pwrbase0 : pwrbase1);
206
207			RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
208				 "Realtek regulatory, 40MHz, writeval = 0x%x\n",
209				 writeval);
210		} else {
211			if (rtlphy->pwrgroup_cnt == 1)
212				chnlgroup = 0;
213
214			if (rtlphy->pwrgroup_cnt >= 3) {
215				if (chnl <= 3)
216					chnlgroup = 0;
217				else if (chnl >= 4 && chnl <= 8)
218					chnlgroup = 1;
219				else if (chnl > 8)
220					chnlgroup = 2;
221				if (rtlphy->pwrgroup_cnt == 4)
222					chnlgroup++;
223			}
224
225			writeval = rtlphy->mcs_offset[chnlgroup][index]
226					+ ((index < 2) ?
227					pwrbase0 : pwrbase1);
228
229			RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
230				 "Realtek regulatory, 20MHz, writeval = 0x%x\n",
231				 writeval);
232		}
233		break;
234	case 2:
235		/* Better regulatory don't increase any power diff */
236		writeval = ((index < 2) ? pwrbase0 : pwrbase1);
237		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
238			 "Better regulatory, writeval = 0x%x\n", writeval);
239		break;
240	case 3:
241		/* Customer defined power diff. increase power diff
242		  defined by customer. */
243		chnlgroup = 0;
244
245		if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) {
246			RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
247				 "customer's limit, 40MHz = 0x%x\n",
248				 rtlefuse->pwrgroup_ht40
249				 [RF90_PATH_A][chnl - 1]);
250		} else {
251			RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
252				 "customer's limit, 20MHz = 0x%x\n",
253				 rtlefuse->pwrgroup_ht20
254				 [RF90_PATH_A][chnl - 1]);
255		}
256
257		for (i = 0; i < 4; i++) {
258			pwrdiff_limit[i] = (u8)((rtlphy->mcs_offset
259				[chnlgroup][index] & (0x7f << (i * 8)))
260				>> (i * 8));
261
262			if (rtlphy->current_chan_bw ==
263			    HT_CHANNEL_WIDTH_20_40) {
264				if (pwrdiff_limit[i] >
265				    rtlefuse->pwrgroup_ht40
266				    [RF90_PATH_A][chnl - 1]) {
267					pwrdiff_limit[i] =
268					  rtlefuse->pwrgroup_ht40
269					  [RF90_PATH_A][chnl - 1];
270				}
271			} else {
272				if (pwrdiff_limit[i] >
273				    rtlefuse->pwrgroup_ht20
274				    [RF90_PATH_A][chnl - 1]) {
275					pwrdiff_limit[i] =
276					    rtlefuse->pwrgroup_ht20
277					    [RF90_PATH_A][chnl - 1];
278				}
279			}
280		}
281
282		customer_limit = (pwrdiff_limit[3] << 24) |
283				(pwrdiff_limit[2] << 16) |
284				(pwrdiff_limit[1] << 8) |
285				(pwrdiff_limit[0]);
286		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
287			 "Customer's limit = 0x%x\n", customer_limit);
288
289		writeval = customer_limit + ((index < 2) ?
290					     pwrbase0 : pwrbase1);
291		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
292			 "Customer, writeval = 0x%x\n", writeval);
293		break;
294	default:
295		chnlgroup = 0;
296		writeval = rtlphy->mcs_offset[chnlgroup][index] +
297				((index < 2) ? pwrbase0 : pwrbase1);
298		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
299			 "RTK better performance, writeval = 0x%x\n", writeval);
300		break;
301	}
302
303	if (rtlpriv->dm.dynamic_txhighpower_lvl == TX_HIGH_PWR_LEVEL_LEVEL1)
304		writeval = 0x10101010;
305	else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
306		 TX_HIGH_PWR_LEVEL_LEVEL2)
307		writeval = 0x0;
308
309	*p_outwrite_val = writeval;
310
311}
312
313static void _rtl92s_write_ofdm_powerreg(struct ieee80211_hw *hw,
314					u8 index, u32 val)
315{
316	struct rtl_priv *rtlpriv = rtl_priv(hw);
317	struct rtl_phy *rtlphy = &(rtlpriv->phy);
318	struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
319	u16 regoffset[6] = {0xe00, 0xe04, 0xe10, 0xe14, 0xe18, 0xe1c};
320	u8 i, rfa_pwr[4];
321	u8 rfa_lower_bound = 0, rfa_upper_bound = 0, rf_pwr_diff = 0;
322	u32 writeval = val;
323
324	/* If path A and Path B coexist, we must limit Path A tx power.
325	 * Protect Path B pwr over or under flow. We need to calculate
326	 * upper and lower bound of path A tx power. */
327	if (rtlphy->rf_type == RF_2T2R) {
328		rf_pwr_diff = rtlefuse->antenna_txpwdiff[0];
329
330		/* Diff=-8~-1 */
331		if (rf_pwr_diff >= 8) {
332			/* Prevent underflow!! */
333			rfa_lower_bound = 0x10 - rf_pwr_diff;
334		/* if (rf_pwr_diff >= 0) Diff = 0-7 */
335		} else {
336			rfa_upper_bound = RF6052_MAX_TX_PWR - rf_pwr_diff;
337		}
338	}
339
340	for (i = 0; i < 4; i++) {
341		rfa_pwr[i] = (u8)((writeval & (0x7f << (i * 8))) >> (i * 8));
342		if (rfa_pwr[i]  > RF6052_MAX_TX_PWR)
343			rfa_pwr[i]  = RF6052_MAX_TX_PWR;
344
345		/* If path A and Path B coexist, we must limit Path A tx power.
346		 * Protect Path B pwr over or under flow. We need to calculate
347		 * upper and lower bound of path A tx power. */
348		if (rtlphy->rf_type == RF_2T2R) {
349			/* Diff=-8~-1 */
350			if (rf_pwr_diff >= 8) {
351				/* Prevent underflow!! */
352				if (rfa_pwr[i] < rfa_lower_bound)
353					rfa_pwr[i] = rfa_lower_bound;
354			/* Diff = 0-7 */
355			} else if (rf_pwr_diff >= 1) {
356				/* Prevent overflow */
357				if (rfa_pwr[i] > rfa_upper_bound)
358					rfa_pwr[i] = rfa_upper_bound;
359			}
360		}
361
362	}
363
364	writeval = (rfa_pwr[3] << 24) | (rfa_pwr[2] << 16) | (rfa_pwr[1] << 8) |
365				rfa_pwr[0];
366
367	rtl_set_bbreg(hw, regoffset[index], 0x7f7f7f7f, writeval);
368}
369
370void rtl92s_phy_rf6052_set_ofdmtxpower(struct ieee80211_hw *hw,
371				       u8 *p_pwrlevel, u8 chnl)
372{
373	u32 writeval, pwrbase0, pwrbase1;
374	u8 index = 0;
375	u8 finalpwr_idx[4];
376
377	_rtl92s_get_powerbase(hw, p_pwrlevel, chnl, &pwrbase0, &pwrbase1,
378			&finalpwr_idx[0]);
379	_rtl92s_set_antennadiff(hw, &finalpwr_idx[0]);
380
381	for (index = 0; index < 6; index++) {
382		_rtl92s_get_txpower_writeval_byregulatory(hw, chnl, index,
383				pwrbase0, pwrbase1, &writeval);
384
385		_rtl92s_write_ofdm_powerreg(hw, index, writeval);
386	}
387}
388
389void rtl92s_phy_rf6052_set_ccktxpower(struct ieee80211_hw *hw, u8 pwrlevel)
390{
391	struct rtl_priv *rtlpriv = rtl_priv(hw);
392	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
393	struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
394	u32 txagc = 0;
395	bool dont_inc_cck_or_turboscanoff = false;
396
397	if (((rtlefuse->eeprom_version >= 2) &&
398	      (rtlefuse->txpwr_safetyflag == 1)) ||
399	      ((rtlefuse->eeprom_version >= 2) &&
400	      (rtlefuse->eeprom_regulatory != 0)))
401		dont_inc_cck_or_turboscanoff = true;
402
403	if (mac->act_scanning) {
404		txagc = 0x3f;
405		if (dont_inc_cck_or_turboscanoff)
406			txagc = pwrlevel;
407	} else {
408		txagc = pwrlevel;
409
410		if (rtlpriv->dm.dynamic_txhighpower_lvl ==
411		    TX_HIGH_PWR_LEVEL_LEVEL1)
412			txagc = 0x10;
413		else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
414			TX_HIGH_PWR_LEVEL_LEVEL2)
415			txagc = 0x0;
416	}
417
418	if (txagc > RF6052_MAX_TX_PWR)
419		txagc = RF6052_MAX_TX_PWR;
420
421	rtl_set_bbreg(hw, RTXAGC_CCK_MCS32, BTX_AGCRATECCK, txagc);
422
423}
424
425bool rtl92s_phy_rf6052_config(struct ieee80211_hw *hw)
426{
427	struct rtl_priv *rtlpriv = rtl_priv(hw);
428	struct rtl_phy *rtlphy = &(rtlpriv->phy);
429	u32 u4reg_val = 0;
430	u8 rfpath;
431	bool rtstatus = true;
432	struct bb_reg_def *pphyreg;
433
434	/* Initialize RF */
435	for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) {
436
437		pphyreg = &rtlphy->phyreg_def[rfpath];
438
439		/* Store original RFENV control type */
440		switch (rfpath) {
441		case RF90_PATH_A:
442		case RF90_PATH_C:
443			u4reg_val = rtl92s_phy_query_bb_reg(hw,
444							    pphyreg->rfintfs,
445							    BRFSI_RFENV);
446			break;
447		case RF90_PATH_B:
448		case RF90_PATH_D:
449			u4reg_val = rtl92s_phy_query_bb_reg(hw,
450							    pphyreg->rfintfs,
451							    BRFSI_RFENV << 16);
452			break;
453		}
454
455		/* Set RF_ENV enable */
456		rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfe,
457				      BRFSI_RFENV << 16, 0x1);
458
459		/* Set RF_ENV output high */
460		rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1);
461
462		/* Set bit number of Address and Data for RF register */
463		rtl92s_phy_set_bb_reg(hw, pphyreg->rfhssi_para2,
464				B3WIRE_ADDRESSLENGTH, 0x0);
465		rtl92s_phy_set_bb_reg(hw, pphyreg->rfhssi_para2,
466				B3WIRE_DATALENGTH, 0x0);
467
468		/* Initialize RF fom connfiguration file */
469		switch (rfpath) {
470		case RF90_PATH_A:
471			rtstatus = rtl92s_phy_config_rf(hw,
472						(enum radio_path)rfpath);
473			break;
474		case RF90_PATH_B:
475			rtstatus = rtl92s_phy_config_rf(hw,
476						(enum radio_path)rfpath);
477			break;
478		case RF90_PATH_C:
479			break;
480		case RF90_PATH_D:
481			break;
482		}
483
484		/* Restore RFENV control type */
485		switch (rfpath) {
486		case RF90_PATH_A:
487		case RF90_PATH_C:
488			rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfs, BRFSI_RFENV,
489					      u4reg_val);
490			break;
491		case RF90_PATH_B:
492		case RF90_PATH_D:
493			rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfs,
494					      BRFSI_RFENV << 16,
495					      u4reg_val);
496			break;
497		}
498
499		if (!rtstatus) {
500			pr_err("Radio[%d] Fail!!\n", rfpath);
501			goto fail;
502		}
503
504	}
505
506	return rtstatus;
507
508fail:
509	return rtstatus;
510}
511
512void rtl92s_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth)
513{
514	struct rtl_priv *rtlpriv = rtl_priv(hw);
515	struct rtl_phy *rtlphy = &(rtlpriv->phy);
516
517	switch (bandwidth) {
518	case HT_CHANNEL_WIDTH_20:
519		rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] &
520					   0xfffff3ff) | 0x0400);
521		rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK,
522					rtlphy->rfreg_chnlval[0]);
523		break;
524	case HT_CHANNEL_WIDTH_20_40:
525		rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] &
526					    0xfffff3ff));
527		rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK,
528					rtlphy->rfreg_chnlval[0]);
529		break;
530	default:
531		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
532			 "unknown bandwidth: %#X\n", bandwidth);
533		break;
534	}
535}
536