/****************************************************************************** * * Copyright(c) 2007 - 2017 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "mp_precomp.h" #include "phydm_precomp.h" #define CALCULATE_SWINGTALBE_OFFSET(_offset, _direction, _size, _delta_thermal) \ do {\ for (_offset = 0; _offset < _size; _offset++) { \ if (_delta_thermal < thermal_threshold[_direction][_offset]) { \ if (_offset != 0)\ _offset--;\ break;\ } \ } \ if (_offset >= _size)\ _offset = _size-1;\ } while (0) void configure_txpower_track( void *dm_void, struct txpwrtrack_cfg *config ) { struct dm_struct *dm = (struct dm_struct *)dm_void; #if RTL8195B_SUPPORT if (dm->support_ic_type == ODM_RTL8195B) configure_txpower_track_8195b(config); #endif #if RTL8721D_SUPPORT if (dm->support_ic_type == ODM_RTL8721D) configure_txpower_track_8721d(config); #endif } /* ********************************************************************** * <20121113, Kordan> This function should be called when tx_agc changed. * Otherwise the previous compensation is gone, because we record the * delta of temperature between two TxPowerTracking watch dogs. * * NOTE: If Tx BB swing or Tx scaling is varified during run-time, still * need to call this function. * ********************************************************************** */ void odm_clear_txpowertracking_state( void *dm_void ) { struct dm_struct *dm = (struct dm_struct *)dm_void; struct _hal_rf_ *rf = &dm->rf_table; u8 p = 0; struct dm_rf_calibration_struct *cali_info = &dm->rf_calibrate_info; cali_info->bb_swing_idx_cck_base = cali_info->default_cck_index; cali_info->bb_swing_idx_cck = cali_info->default_cck_index; dm->rf_calibrate_info.CCK_index = 0; for (p = RF_PATH_A; p < MAX_RF_PATH; ++p) { cali_info->bb_swing_idx_ofdm_base[p] = cali_info->default_ofdm_index; cali_info->bb_swing_idx_ofdm[p] = cali_info->default_ofdm_index; cali_info->OFDM_index[p] = cali_info->default_ofdm_index; cali_info->power_index_offset[p] = 0; cali_info->delta_power_index[p] = 0; cali_info->delta_power_index_last[p] = 0; cali_info->absolute_ofdm_swing_idx[p] = 0; cali_info->remnant_ofdm_swing_idx[p] = 0; cali_info->kfree_offset[p] = 0; } cali_info->modify_tx_agc_flag_path_a = false; cali_info->modify_tx_agc_flag_path_b = false; cali_info->modify_tx_agc_flag_path_c = false; cali_info->modify_tx_agc_flag_path_d = false; cali_info->remnant_cck_swing_idx = 0; cali_info->thermal_value = rf->eeprom_thermal; cali_info->modify_tx_agc_value_cck = 0; cali_info->modify_tx_agc_value_ofdm = 0; } void odm_txpowertracking_callback_thermal_meter( void *dm_void ) { struct dm_struct *dm = (struct dm_struct *)dm_void; struct _hal_rf_ *rf = &dm->rf_table; struct dm_rf_calibration_struct *cali_info = &dm->rf_calibrate_info; struct dm_iqk_info *iqk_info = &dm->IQK_info; u8 thermal_value = 0, delta, delta_LCK, delta_IQK, p = 0, i = 0; u8 thermal_value_avg_count = 0; u32 thermal_value_avg = 0, regc80, regcd0, regcd4, regab4; u8 OFDM_min_index = 0; /* OFDM BB Swing should be less than +3.0dB, which is required by Arthur */ u8 indexforchannel = 0; /* get_right_chnl_place_for_iqk(hal_data->current_channel) */ u8 power_tracking_type = rf->pwt_type; u8 xtal_offset_eanble = 0; s8 thermal_value_temp = 0; struct txpwrtrack_cfg c = {0}; /* 4 1. The following TWO tables decide the final index of OFDM/CCK swing table. */ u8 *delta_swing_table_idx_tup_a = NULL; u8 *delta_swing_table_idx_tdown_a = NULL; u8 *delta_swing_table_idx_tup_b = NULL; u8 *delta_swing_table_idx_tdown_b = NULL; /*for Xtal Offset by James.Tung*/ s8 *delta_swing_table_xtal_up = NULL; s8 *delta_swing_table_xtal_down = NULL; /* 4 2. Initialization ( 7 steps in total ) */ configure_txpower_track(dm, &c); (*c.get_delta_swing_table)(dm, (u8 **)&delta_swing_table_idx_tup_a, (u8 **)&delta_swing_table_idx_tdown_a, (u8 **)&delta_swing_table_idx_tup_b, (u8 **)&delta_swing_table_idx_tdown_b); /*for Xtal Offset*/ if (dm->support_ic_type & (ODM_RTL8195B | ODM_RTL8721D)) (*c.get_delta_swing_xtal_table)(dm, (s8 **)&delta_swing_table_xtal_up, (s8 **)&delta_swing_table_xtal_down); cali_info->txpowertracking_callback_cnt++; /*cosa add for debug*/ cali_info->is_txpowertracking_init = true; RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "===>odm_txpowertracking_callback_thermal_meter\n cali_info->bb_swing_idx_cck_base: %d, cali_info->bb_swing_idx_ofdm_base[A]: %d, cali_info->default_ofdm_index: %d\n", cali_info->bb_swing_idx_cck_base, cali_info->bb_swing_idx_ofdm_base[RF_PATH_A], cali_info->default_ofdm_index); RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "cali_info->txpowertrack_control = %d, hal_data->eeprom_thermal_meter %d\n", cali_info->txpowertrack_control, rf->eeprom_thermal); if (dm->support_ic_type == ODM_RTL8721D) thermal_value = (u8)odm_get_rf_reg(dm, RF_PATH_A, c.thermal_reg_addr, 0x7e0); /* 0x42: RF Reg[10:5] 8721D */ else thermal_value = (u8)odm_get_rf_reg(dm, RF_PATH_A, c.thermal_reg_addr, 0xfc00); /* 0x42: RF Reg[15:10] 88E */ thermal_value_temp = thermal_value + phydm_get_thermal_offset(dm); RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "thermal_value_temp(%d) = thermal_value(%d) + power_trim_thermal(%d)\n", thermal_value_temp, thermal_value, phydm_get_thermal_offset(dm)); if (thermal_value_temp > 63) thermal_value = 63; else if (thermal_value_temp < 0) thermal_value = 0; else thermal_value = thermal_value_temp; if (!cali_info->txpowertrack_control) return; if (rf->eeprom_thermal == 0xff) { RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "no pg, hal_data->eeprom_thermal_meter = 0x%x\n", rf->eeprom_thermal); return; } /*4 4. Calculate average thermal meter*/ cali_info->thermal_value_avg[cali_info->thermal_value_avg_index] = thermal_value; cali_info->thermal_value_avg_index++; if (cali_info->thermal_value_avg_index == c.average_thermal_num) /*Average times = c.average_thermal_num*/ cali_info->thermal_value_avg_index = 0; for (i = 0; i < c.average_thermal_num; i++) { if (cali_info->thermal_value_avg[i]) { thermal_value_avg += cali_info->thermal_value_avg[i]; thermal_value_avg_count++; } } if (thermal_value_avg_count) { /* Calculate Average thermal_value after average enough times */ thermal_value = (u8)(thermal_value_avg / thermal_value_avg_count); cali_info->thermal_value_delta = thermal_value - rf->eeprom_thermal; RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "AVG Thermal Meter = 0x%X, EFUSE Thermal base = 0x%X\n", thermal_value, rf->eeprom_thermal); } /* 4 5. Calculate delta, delta_LCK, delta_IQK. */ /* "delta" here is used to determine whether thermal value changes or not. */ delta = (thermal_value > cali_info->thermal_value) ? (thermal_value - cali_info->thermal_value) : (cali_info->thermal_value - thermal_value); delta_LCK = (thermal_value > cali_info->thermal_value_lck) ? (thermal_value - cali_info->thermal_value_lck) : (cali_info->thermal_value_lck - thermal_value); delta_IQK = (thermal_value > cali_info->thermal_value_iqk) ? (thermal_value - cali_info->thermal_value_iqk) : (cali_info->thermal_value_iqk - thermal_value); /*4 6. If necessary, do LCK.*/ RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "(delta, delta_LCK, delta_IQK) = (%d, %d, %d)\n", delta, delta_LCK, delta_IQK); /* Wait sacn to do LCK by RF Jenyu*/ if ((!*dm->is_scan_in_process) && !iqk_info->rfk_forbidden && (!*dm->is_tdma)) { /* Delta temperature is equal to or larger than 20 centigrade.*/ if (delta_LCK >= c.threshold_iqk) { RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "delta_LCK(%d) >= threshold_iqk(%d)\n", delta_LCK, c.threshold_iqk); cali_info->thermal_value_lck = thermal_value; /*Use RTLCK, so close power tracking driver LCK*/ (*c.phy_lc_calibrate)(dm); } } /*3 7. If necessary, move the index of swing table to adjust Tx power.*/ if (delta > 0 && cali_info->txpowertrack_control) { /* "delta" here is used to record the absolute value of difference. */ delta = thermal_value > rf->eeprom_thermal ? (thermal_value - rf->eeprom_thermal) : (rf->eeprom_thermal - thermal_value); if (delta >= TXPWR_TRACK_TABLE_SIZE) delta = TXPWR_TRACK_TABLE_SIZE - 1; /*4 7.1 The Final Power index = BaseIndex + power_index_offset*/ if (thermal_value > rf->eeprom_thermal) { for (p = RF_PATH_A; p < c.rf_path_count; p++) { cali_info->delta_power_index_last[p] = cali_info->delta_power_index[p]; /*recording poer index offset*/ switch (p) { case RF_PATH_B: RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "delta_swing_table_idx_tup_b[%d] = %d\n", delta, delta_swing_table_idx_tup_b[delta]); cali_info->delta_power_index[p] = delta_swing_table_idx_tup_b [delta]; cali_info->absolute_ofdm_swing_idx[p] = delta_swing_table_idx_tup_b [delta]; /*Record delta swing for mix mode*/ RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "******Temp is higher and cali_info->absolute_ofdm_swing_idx[RF_PATH_B] = %d\n", cali_info->absolute_ofdm_swing_idx[p]); break; default: RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "delta_swing_table_idx_tup_a[%d] = %d\n", delta, delta_swing_table_idx_tup_a[delta]); cali_info->delta_power_index[p] = delta_swing_table_idx_tup_a[delta]; cali_info->absolute_ofdm_swing_idx[p] = delta_swing_table_idx_tup_a[delta]; /*Record delta swing*/ /*for mix mode power tracking*/ RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "******Temp is higher and cali_info->absolute_ofdm_swing_idx[RF_PATH_A] = %d\n", cali_info->absolute_ofdm_swing_idx[p]); break; } } /* JJ ADD 20161014 */ if (dm->support_ic_type & (ODM_RTL8195B | ODM_RTL8721D)) { /*Save xtal_offset from Xtal table*/ cali_info->xtal_offset_last = cali_info->xtal_offset; /*recording last Xtal offset*/ RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "[Xtal] delta_swing_table_xtal_up[%d] = %d\n", delta, delta_swing_table_xtal_up[delta]); cali_info->xtal_offset = delta_swing_table_xtal_up[delta]; xtal_offset_eanble = (cali_info->xtal_offset_last != cali_info->xtal_offset); } } else { for (p = RF_PATH_A; p < c.rf_path_count; p++) { cali_info->delta_power_index_last[p] = cali_info->delta_power_index[p]; /*recording poer index offset*/ switch (p) { case RF_PATH_B: RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "delta_swing_table_idx_tdown_b[%d] = %d\n", delta, delta_swing_table_idx_tdown_b[delta]); cali_info->delta_power_index[p] = -1 * delta_swing_table_idx_tdown_b[delta]; cali_info->absolute_ofdm_swing_idx[p] = -1 * delta_swing_table_idx_tdown_b[delta]; /*Record delta swing for mix mode power tracking*/ RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "******Temp is lower and cali_info->absolute_ofdm_swing_idx[RF_PATH_B] = %d\n", cali_info->absolute_ofdm_swing_idx[p]); break; default: RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "delta_swing_table_idx_tdown_a[%d] = %d\n", delta, delta_swing_table_idx_tdown_a[delta]); cali_info->delta_power_index[p] = -1 * delta_swing_table_idx_tdown_a[delta]; cali_info->absolute_ofdm_swing_idx[p] = -1 * delta_swing_table_idx_tdown_a[delta]; /*Record delta swing for mix mode power tracking*/ RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "******Temp is lower and cali_info->absolute_ofdm_swing_idx[RF_PATH_A] = %d\n", cali_info->absolute_ofdm_swing_idx[p]); break; } } /* JJ ADD 20161014 */ if (dm->support_ic_type & (ODM_RTL8195B | ODM_RTL8721D)) { /*Save xtal_offset from Xtal table*/ cali_info->xtal_offset_last = cali_info->xtal_offset; /*recording last Xtal offset*/ RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "[Xtal] delta_swing_table_xtal_down[%d] = %d\n", delta, delta_swing_table_xtal_down[delta]); cali_info->xtal_offset = delta_swing_table_xtal_down[delta]; xtal_offset_eanble = (cali_info->xtal_offset_last != cali_info->xtal_offset); } } } else { RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "The thermal meter is unchanged or TxPowerTracking OFF(%d): thermal_value: %d, cali_info->thermal_value: %d\n", cali_info->txpowertrack_control, thermal_value, cali_info->thermal_value); for (p = RF_PATH_A; p < c.rf_path_count; p++) cali_info->power_index_offset[p] = 0; } if (thermal_value > rf->eeprom_thermal) { RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "Temperature(%d) higher than PG value(%d)\n", thermal_value, rf->eeprom_thermal); if (dm->support_ic_type == ODM_RTL8188E || dm->support_ic_type == ODM_RTL8192E || dm->support_ic_type == ODM_RTL8821 || dm->support_ic_type == ODM_RTL8812 || dm->support_ic_type == ODM_RTL8723B || dm->support_ic_type == ODM_RTL8814A || dm->support_ic_type == ODM_RTL8703B || dm->support_ic_type == ODM_RTL8188F || dm->support_ic_type == ODM_RTL8822B || dm->support_ic_type == ODM_RTL8723D || dm->support_ic_type == ODM_RTL8821C || dm->support_ic_type == ODM_RTL8710B || dm->support_ic_type == ODM_RTL8192F || dm->support_ic_type == ODM_RTL8195B || dm->support_ic_type == ODM_RTL8721D){ RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "**********Enter POWER Tracking MIX_MODE**********\n"); for (p = RF_PATH_A; p < c.rf_path_count; p++) (*c.odm_tx_pwr_track_set_pwr)(dm, MIX_MODE, p, 0); } else { RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "**********Enter POWER Tracking BBSWING_MODE**********\n"); for (p = RF_PATH_A; p < c.rf_path_count; p++) (*c.odm_tx_pwr_track_set_pwr)(dm, BBSWING, p, indexforchannel); } } else { RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "Temperature(%d) lower than PG value(%d)\n", thermal_value, rf->eeprom_thermal); if (dm->support_ic_type == ODM_RTL8188E || dm->support_ic_type == ODM_RTL8192E || dm->support_ic_type == ODM_RTL8821 || dm->support_ic_type == ODM_RTL8812 || dm->support_ic_type == ODM_RTL8723B || dm->support_ic_type == ODM_RTL8814A || dm->support_ic_type == ODM_RTL8703B || dm->support_ic_type == ODM_RTL8188F || dm->support_ic_type == ODM_RTL8822B || dm->support_ic_type == ODM_RTL8723D || dm->support_ic_type == ODM_RTL8821C || dm->support_ic_type == ODM_RTL8710B || dm->support_ic_type == ODM_RTL8192F || dm->support_ic_type == ODM_RTL8195B || dm->support_ic_type == ODM_RTL8721D) { RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "**********Enter POWER Tracking MIX_MODE**********\n"); for (p = RF_PATH_A; p < c.rf_path_count; p++) (*c.odm_tx_pwr_track_set_pwr)(dm, MIX_MODE, p, indexforchannel); } else { RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "**********Enter POWER Tracking BBSWING_MODE**********\n"); for (p = RF_PATH_A; p < c.rf_path_count; p++) (*c.odm_tx_pwr_track_set_pwr)(dm, BBSWING, p, indexforchannel); } cali_info->bb_swing_idx_cck_base = cali_info->bb_swing_idx_cck; /*Record last time Power Tracking result as base.*/ for (p = RF_PATH_A; p < c.rf_path_count; p++) cali_info->bb_swing_idx_ofdm_base[p] = cali_info->bb_swing_idx_ofdm[p]; RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "cali_info->thermal_value = %d thermal_value= %d\n", cali_info->thermal_value, thermal_value); cali_info->thermal_value = thermal_value; /*Record last Power Tracking Thermal value*/ } /* JJ ADD 20161014 */ if (dm->support_ic_type == (ODM_RTL8195B | ODM_RTL8721D)) { if (xtal_offset_eanble != 0 && cali_info->txpowertrack_control && (rf->eeprom_thermal != 0xff)) { RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "**********Enter Xtal Tracking**********\n"); if (thermal_value > rf->eeprom_thermal) { RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "Temperature(%d) higher than PG value(%d)\n", thermal_value, rf->eeprom_thermal); (*c.odm_txxtaltrack_set_xtal)(dm); } else { RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "Temperature(%d) lower than PG value(%d)\n", thermal_value, rf->eeprom_thermal); (*c.odm_txxtaltrack_set_xtal)(dm); } RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "**********End Xtal Tracking**********\n"); } } /* Wait sacn to do IQK by RF Jenyu*/ if ((!*dm->is_scan_in_process) && (!iqk_info->rfk_forbidden)) { /*Delta temperature is equal to or larger than 20 centigrade (When threshold is 8).*/ if (delta_IQK >= c.threshold_iqk) { cali_info->thermal_value_iqk = thermal_value; RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "delta_IQK(%d) >= threshold_iqk(%d)\n", delta_IQK, c.threshold_iqk); if (!cali_info->is_iqk_in_progress) (*c.do_iqk)(dm, delta_IQK, thermal_value, 8); } } RF_DBG(dm, DBG_RF_TX_PWR_TRACK, "<===odm_txpowertracking_callback_thermal_meter\n"); cali_info->tx_powercount = 0; } /* 3============================================================ * 3 IQ Calibration * 3============================================================ */ void odm_reset_iqk_result( void *dm_void ) { return; } u8 odm_get_right_chnl_place_for_iqk(u8 chnl) { } void odm_iq_calibrate( struct dm_struct *dm ) { } void phydm_rf_init(void *dm_void) { struct dm_struct *dm = (struct dm_struct *)dm_void; odm_txpowertracking_init(dm); odm_clear_txpowertracking_state(dm); } void phydm_rf_watchdog(void *dm_void) { struct dm_struct *dm = (struct dm_struct *)dm_void; odm_txpowertracking_check(dm); }