GNU Linux-libre 4.14.266-gnu1
[releases.git] / drivers / staging / rtlwifi / phydm / phydm_noisemonitor.c
1 /******************************************************************************
2  *
3  * Copyright(c) 2007 - 2016  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  * The full GNU General Public License is included in this distribution in the
15  * file called LICENSE.
16  *
17  * Contact Information:
18  * wlanfae <wlanfae@realtek.com>
19  * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
20  * Hsinchu 300, Taiwan.
21  *
22  * Larry Finger <Larry.Finger@lwfinger.net>
23  *
24  *****************************************************************************/
25
26 /* ************************************************************
27  * include files
28  * *************************************************************/
29 #include "mp_precomp.h"
30 #include "phydm_precomp.h"
31 #include "phydm_noisemonitor.h"
32
33 /* *************************************************
34  * This function is for inband noise test utility only
35  * To obtain the inband noise level(dbm), do the following.
36  * 1. disable DIG and Power Saving
37  * 2. Set initial gain = 0x1a
38  * 3. Stop updating idle time pwer report (for driver read)
39  *      - 0x80c[25]
40  *
41  * **************************************************/
42
43 #define VALID_MIN -35
44 #define VALID_MAX 10
45 #define VALID_CNT 5
46
47 static inline void phydm_set_noise_data_sum(struct noise_level *noise_data,
48                                             u8 max_rf_path)
49 {
50         u8 rf_path;
51
52         for (rf_path = ODM_RF_PATH_A; rf_path < max_rf_path; rf_path++) {
53                 if (noise_data->valid_cnt[rf_path])
54                         noise_data->sum[rf_path] /=
55                                 noise_data->valid_cnt[rf_path];
56                 else
57                         noise_data->sum[rf_path] = 0;
58         }
59 }
60
61 static s16 odm_inband_noise_monitor_n_series(struct phy_dm_struct *dm,
62                                              u8 is_pause_dig, u8 igi_value,
63                                              u32 max_time)
64 {
65         u32 tmp4b;
66         u8 max_rf_path = 0, rf_path;
67         u8 reg_c50, reg_c58, valid_done = 0;
68         struct noise_level noise_data;
69         u64 start = 0, func_start = 0, func_end = 0;
70
71         func_start = odm_get_current_time(dm);
72         dm->noise_level.noise_all = 0;
73
74         if ((dm->rf_type == ODM_1T2R) || (dm->rf_type == ODM_2T2R))
75                 max_rf_path = 2;
76         else
77                 max_rf_path = 1;
78
79         ODM_RT_TRACE(dm, ODM_COMP_COMMON, "%s() ==>\n", __func__);
80
81         odm_memory_set(dm, &noise_data, 0, sizeof(struct noise_level));
82
83         /*  */
84         /* step 1. Disable DIG && Set initial gain. */
85         /*  */
86
87         if (is_pause_dig)
88                 odm_pause_dig(dm, PHYDM_PAUSE, PHYDM_PAUSE_LEVEL_1, igi_value);
89         /*  */
90         /* step 2. Disable all power save for read registers */
91         /*  */
92         /* dcmd_DebugControlPowerSave(adapter, PSDisable); */
93
94         /*  */
95         /* step 3. Get noise power level */
96         /*  */
97         start = odm_get_current_time(dm);
98         while (1) {
99                 /* Stop updating idle time pwer report (for driver read) */
100                 odm_set_bb_reg(dm, REG_FPGA0_TX_GAIN_STAGE, BIT(25), 1);
101
102                 /* Read Noise Floor Report */
103                 tmp4b = odm_get_bb_reg(dm, 0x8f8, MASKDWORD);
104                 ODM_RT_TRACE(dm, ODM_COMP_COMMON,
105                              "Noise Floor Report (0x8f8) = 0x%08x\n", tmp4b);
106
107                 /* update idle time pwer report per 5us */
108                 odm_set_bb_reg(dm, REG_FPGA0_TX_GAIN_STAGE, BIT(25), 0);
109
110                 noise_data.value[ODM_RF_PATH_A] = (u8)(tmp4b & 0xff);
111                 noise_data.value[ODM_RF_PATH_B] = (u8)((tmp4b & 0xff00) >> 8);
112
113                 ODM_RT_TRACE(dm, ODM_COMP_COMMON,
114                              "value_a = 0x%x(%d), value_b = 0x%x(%d)\n",
115                              noise_data.value[ODM_RF_PATH_A],
116                              noise_data.value[ODM_RF_PATH_A],
117                              noise_data.value[ODM_RF_PATH_B],
118                              noise_data.value[ODM_RF_PATH_B]);
119
120                 for (rf_path = ODM_RF_PATH_A; rf_path < max_rf_path;
121                      rf_path++) {
122                         noise_data.sval[rf_path] =
123                                 (s8)noise_data.value[rf_path];
124                         noise_data.sval[rf_path] /= 2;
125                 }
126
127                 ODM_RT_TRACE(dm, ODM_COMP_COMMON, "sval_a = %d, sval_b = %d\n",
128                              noise_data.sval[ODM_RF_PATH_A],
129                              noise_data.sval[ODM_RF_PATH_B]);
130
131                 for (rf_path = ODM_RF_PATH_A; rf_path < max_rf_path;
132                      rf_path++) {
133                         if (!(noise_data.valid_cnt[rf_path] < VALID_CNT) ||
134                             !(noise_data.sval[rf_path] < VALID_MAX &&
135                               noise_data.sval[rf_path] >= VALID_MIN)) {
136                                 continue;
137                         }
138
139                         noise_data.valid_cnt[rf_path]++;
140                         noise_data.sum[rf_path] += noise_data.sval[rf_path];
141                         ODM_RT_TRACE(dm, ODM_COMP_COMMON,
142                                      "rf_path:%d Valid sval = %d\n", rf_path,
143                                      noise_data.sval[rf_path]);
144                         ODM_RT_TRACE(dm, ODM_COMP_COMMON, "Sum of sval = %d,\n",
145                                      noise_data.sum[rf_path]);
146                         if (noise_data.valid_cnt[rf_path] == VALID_CNT) {
147                                 valid_done++;
148                                 ODM_RT_TRACE(
149                                         dm, ODM_COMP_COMMON,
150                                         "After divided, rf_path:%d,sum = %d\n",
151                                         rf_path, noise_data.sum[rf_path]);
152                         }
153                 }
154
155                 if ((valid_done == max_rf_path) ||
156                     (odm_get_progressing_time(dm, start) > max_time)) {
157                         phydm_set_noise_data_sum(&noise_data, max_rf_path);
158                         break;
159                 }
160         }
161         reg_c50 = (u8)odm_get_bb_reg(dm, REG_OFDM_0_XA_AGC_CORE1, MASKBYTE0);
162         reg_c50 &= ~BIT(7);
163         ODM_RT_TRACE(dm, ODM_COMP_COMMON, "0x%x = 0x%02x(%d)\n",
164                      REG_OFDM_0_XA_AGC_CORE1, reg_c50, reg_c50);
165         dm->noise_level.noise[ODM_RF_PATH_A] =
166                 (u8)(-110 + reg_c50 + noise_data.sum[ODM_RF_PATH_A]);
167         dm->noise_level.noise_all += dm->noise_level.noise[ODM_RF_PATH_A];
168
169         if (max_rf_path == 2) {
170                 reg_c58 = (u8)odm_get_bb_reg(dm, REG_OFDM_0_XB_AGC_CORE1,
171                                              MASKBYTE0);
172                 reg_c58 &= ~BIT(7);
173                 ODM_RT_TRACE(dm, ODM_COMP_COMMON, "0x%x = 0x%02x(%d)\n",
174                              REG_OFDM_0_XB_AGC_CORE1, reg_c58, reg_c58);
175                 dm->noise_level.noise[ODM_RF_PATH_B] =
176                         (u8)(-110 + reg_c58 + noise_data.sum[ODM_RF_PATH_B]);
177                 dm->noise_level.noise_all +=
178                         dm->noise_level.noise[ODM_RF_PATH_B];
179         }
180         dm->noise_level.noise_all /= max_rf_path;
181
182         ODM_RT_TRACE(dm, ODM_COMP_COMMON, "noise_a = %d, noise_b = %d\n",
183                      dm->noise_level.noise[ODM_RF_PATH_A],
184                      dm->noise_level.noise[ODM_RF_PATH_B]);
185
186         /*  */
187         /* step 4. Recover the Dig */
188         /*  */
189         if (is_pause_dig)
190                 odm_pause_dig(dm, PHYDM_RESUME, PHYDM_PAUSE_LEVEL_1, igi_value);
191         func_end = odm_get_progressing_time(dm, func_start);
192
193         ODM_RT_TRACE(dm, ODM_COMP_COMMON, "%s() <==\n", __func__);
194         return dm->noise_level.noise_all;
195 }
196
197 static s16 odm_inband_noise_monitor_ac_series(struct phy_dm_struct *dm,
198                                               u8 is_pause_dig, u8 igi_value,
199                                               u32 max_time)
200 {
201         s32 rxi_buf_anta, rxq_buf_anta; /*rxi_buf_antb, rxq_buf_antb;*/
202         s32 value32, pwdb_A = 0, sval, noise, sum;
203         bool pd_flag;
204         u8 valid_cnt;
205         u64 start = 0, func_start = 0, func_end = 0;
206
207         if (!(dm->support_ic_type & (ODM_RTL8812 | ODM_RTL8821 | ODM_RTL8814A)))
208                 return 0;
209
210         func_start = odm_get_current_time(dm);
211         dm->noise_level.noise_all = 0;
212
213         ODM_RT_TRACE(dm, ODM_COMP_COMMON, "%s() ==>\n", __func__);
214
215         /* step 1. Disable DIG && Set initial gain. */
216         if (is_pause_dig)
217                 odm_pause_dig(dm, PHYDM_PAUSE, PHYDM_PAUSE_LEVEL_1, igi_value);
218
219         /* step 2. Disable all power save for read registers */
220         /*dcmd_DebugControlPowerSave(adapter, PSDisable); */
221
222         /* step 3. Get noise power level */
223         start = odm_get_current_time(dm);
224
225         /* reset counters */
226         sum = 0;
227         valid_cnt = 0;
228
229         /* step 3. Get noise power level */
230         while (1) {
231                 /*Set IGI=0x1C */
232                 odm_write_dig(dm, 0x1C);
233                 /*stop CK320&CK88 */
234                 odm_set_bb_reg(dm, 0x8B4, BIT(6), 1);
235                 /*Read path-A */
236                 odm_set_bb_reg(dm, 0x8FC, MASKDWORD, 0x200); /*set debug port*/
237                 value32 = odm_get_bb_reg(dm, 0xFA0,
238                                          MASKDWORD); /*read debug port*/
239
240                 rxi_buf_anta = (value32 & 0xFFC00) >>
241                                10; /*rxi_buf_anta=RegFA0[19:10]*/
242                 rxq_buf_anta = value32 & 0x3FF; /*rxq_buf_anta=RegFA0[19:10]*/
243
244                 pd_flag = (bool)((value32 & BIT(31)) >> 31);
245
246                 /*Not in packet detection period or Tx state */
247                 if ((!pd_flag) || (rxi_buf_anta != 0x200)) {
248                         /*sign conversion*/
249                         rxi_buf_anta = odm_sign_conversion(rxi_buf_anta, 10);
250                         rxq_buf_anta = odm_sign_conversion(rxq_buf_anta, 10);
251
252                         pwdb_A = odm_pwdb_conversion(
253                                 rxi_buf_anta * rxi_buf_anta +
254                                         rxq_buf_anta * rxq_buf_anta,
255                                 20, 18); /*S(10,9)*S(10,9)=S(20,18)*/
256
257                         ODM_RT_TRACE(
258                                 dm, ODM_COMP_COMMON,
259                                 "pwdb_A= %d dB, rxi_buf_anta= 0x%x, rxq_buf_anta= 0x%x\n",
260                                 pwdb_A, rxi_buf_anta & 0x3FF,
261                                 rxq_buf_anta & 0x3FF);
262                 }
263                 /*Start CK320&CK88*/
264                 odm_set_bb_reg(dm, 0x8B4, BIT(6), 0);
265                 /*BB Reset*/
266                 odm_write_1byte(dm, 0x02, odm_read_1byte(dm, 0x02) & (~BIT(0)));
267                 odm_write_1byte(dm, 0x02, odm_read_1byte(dm, 0x02) | BIT(0));
268                 /*PMAC Reset*/
269                 odm_write_1byte(dm, 0xB03,
270                                 odm_read_1byte(dm, 0xB03) & (~BIT(0)));
271                 odm_write_1byte(dm, 0xB03, odm_read_1byte(dm, 0xB03) | BIT(0));
272                 /*CCK Reset*/
273                 if (odm_read_1byte(dm, 0x80B) & BIT(4)) {
274                         odm_write_1byte(dm, 0x80B,
275                                         odm_read_1byte(dm, 0x80B) & (~BIT(4)));
276                         odm_write_1byte(dm, 0x80B,
277                                         odm_read_1byte(dm, 0x80B) | BIT(4));
278                 }
279
280                 sval = pwdb_A;
281
282                 if ((sval < 0 && sval >= -27) && (valid_cnt < VALID_CNT)) {
283                         valid_cnt++;
284                         sum += sval;
285                         ODM_RT_TRACE(dm, ODM_COMP_COMMON, "Valid sval = %d\n",
286                                      sval);
287                         ODM_RT_TRACE(dm, ODM_COMP_COMMON, "Sum of sval = %d,\n",
288                                      sum);
289                         if ((valid_cnt >= VALID_CNT) ||
290                             (odm_get_progressing_time(dm, start) > max_time)) {
291                                 sum /= VALID_CNT;
292                                 ODM_RT_TRACE(dm, ODM_COMP_COMMON,
293                                              "After divided, sum = %d\n", sum);
294                                 break;
295                         }
296                 }
297         }
298
299         /*ADC backoff is 12dB,*/
300         /*Ptarget=0x1C-110=-82dBm*/
301         noise = sum + 12 + 0x1C - 110;
302
303         /*Offset*/
304         noise = noise - 3;
305         ODM_RT_TRACE(dm, ODM_COMP_COMMON, "noise = %d\n", noise);
306         dm->noise_level.noise_all = (s16)noise;
307
308         /* step 4. Recover the Dig*/
309         if (is_pause_dig)
310                 odm_pause_dig(dm, PHYDM_RESUME, PHYDM_PAUSE_LEVEL_1, igi_value);
311
312         func_end = odm_get_progressing_time(dm, func_start);
313
314         ODM_RT_TRACE(dm, ODM_COMP_COMMON, "%s() <==\n", __func__);
315
316         return dm->noise_level.noise_all;
317 }
318
319 s16 odm_inband_noise_monitor(void *dm_void, u8 is_pause_dig, u8 igi_value,
320                              u32 max_time)
321 {
322         struct phy_dm_struct *dm = (struct phy_dm_struct *)dm_void;
323
324         if (dm->support_ic_type & ODM_IC_11AC_SERIES)
325                 return odm_inband_noise_monitor_ac_series(dm, is_pause_dig,
326                                                           igi_value, max_time);
327         else
328                 return odm_inband_noise_monitor_n_series(dm, is_pause_dig,
329                                                          igi_value, max_time);
330 }