/* Check if the channel is in our supported range */ if (channel->band == NL80211_BAND_2GHZ) { if ((freq >= ah->ah_capabilities.cap_range.range_2ghz_min) &&
(freq <= ah->ah_capabilities.cap_range.range_2ghz_max)) returntrue;
} elseif (channel->band == NL80211_BAND_5GHZ) if ((freq >= ah->ah_capabilities.cap_range.range_5ghz_min) &&
(freq <= ah->ah_capabilities.cap_range.range_5ghz_max)) returntrue;
for (i = 0; i < ah->ah_rf_regs_count; i++) { if (rf_regs[i].index == reg_id) {
rfreg = &rf_regs[i]; break;
}
}
if (rfb == NULL || rfreg == NULL) {
ATH5K_PRINTF("Rf register not found!\n"); /* should not happen */ return0;
}
bank = rfreg->bank;
num_bits = rfreg->field.len;
first_bit = rfreg->field.pos;
col = rfreg->field.col;
/* first_bit is an offset from bank's *start.Sincewehaveallbankson *thesamearray,weusethisoffset
* to mark each bank's start */
offset = ah->ah_offset[bank];
/* Skip if gain calibration is inactive or
* we already handle a probe request */ if (ah->ah_gain.g_state != AR5K_RFGAIN_ACTIVE) return;
/* Send the packet with 2dB below max power as
* patent doc suggest */
ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_ofdm - 4,
AR5K_PHY_PAPD_PROBE_TXPOWER) |
AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE);
done:
ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "ret %d, gain step %u, current gain %u, target gain %u\n",
ret, ah->ah_gain.g_step_idx, ah->ah_gain.g_current,
ah->ah_gain.g_target);
if (ah->ah_rf_banks == NULL ||
ah->ah_gain.g_state == AR5K_RFGAIN_INACTIVE) return AR5K_RFGAIN_INACTIVE;
/* No check requested, either engine is inactive
* or an adjustment is already requested */ if (ah->ah_gain.g_state != AR5K_RFGAIN_READ_REQUESTED) goto done;
/* Read the PAPD (Peak to Average Power Detector)
* register */
data = ath5k_hw_reg_read(ah, AR5K_PHY_PAPD_PROBE);
/* No probe is scheduled, read gain_F measurement */ if (!(data & AR5K_PHY_PAPD_PROBE_TX_NEXT)) {
ah->ah_gain.g_current = data >> AR5K_PHY_PAPD_PROBE_GAINF_S;
type = AR5K_REG_MS(data, AR5K_PHY_PAPD_PROBE_TYPE);
/* If tx packet is CCK correct the gain_F measurement
* by cck ofdm gain delta */ if (type == AR5K_PHY_PAPD_PROBE_TYPE_CCK) { if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A)
ah->ah_gain.g_current +=
ee->ee_cck_ofdm_gain_delta; else
ah->ah_gain.g_current +=
AR5K_GAIN_CCK_PROBE_CORR;
}
/* Further correct gain_F measurement for
* RF5112A radios */ if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) {
ath5k_hw_rf_gainf_corr(ah);
ah->ah_gain.g_current =
ah->ah_gain.g_current >= ah->ah_gain.g_f_corr ?
(ah->ah_gain.g_current - ah->ah_gain.g_f_corr) : 0;
}
/* Check if measurement is ok and if we need *toadjustgain,scheduleagainadjustment,
* else switch back to the active state */ if (ath5k_hw_rf_check_gainf_readback(ah) &&
AR5K_GAIN_CHECK_ADJUST(&ah->ah_gain) &&
ath5k_hw_rf_gainf_adjust(ah)) {
ah->ah_gain.g_state = AR5K_RFGAIN_NEED_CHANGE;
} else {
ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
}
}
/* If it's the first time we set RF buffer, allocate *ah->ah_rf_banksbasedonah->ah_rf_banks_size
* we set above */ if (ah->ah_rf_banks == NULL) {
ah->ah_rf_banks = kmalloc_array(ah->ah_rf_banks_size, sizeof(u32),
GFP_KERNEL); if (ah->ah_rf_banks == NULL) {
ATH5K_ERR(ah, "out of memory\n"); return -ENOMEM;
}
}
/* Copy values to modify them */
rfb = ah->ah_rf_banks;
for (i = 0; i < ah->ah_rf_banks_size; i++) { if (ini_rfb[i].rfb_bank >= AR5K_MAX_RF_BANKS) {
ATH5K_ERR(ah, "invalid bank\n"); return -EINVAL;
}
/* Bank changed, write down the offset */ if (bank != ini_rfb[i].rfb_bank) {
bank = ini_rfb[i].rfb_bank;
ah->ah_offset[bank] = i;
}
rfb[i] = ini_rfb[i].rfb_mode_data[mode];
}
/* Set Output and Driver bias current (OB/DB) */ if (channel->band == NL80211_BAND_2GHZ) {
/* Set optimum value for early revisions (on pci-e chips) */ if (ah->ah_mac_srev >= AR5K_SREV_AR5424 &&
ah->ah_mac_srev < AR5K_SREV_AR5413)
ath5k_hw_rfb_op(ah, rf_regs, ath5k_hw_bitswap(6, 3),
AR5K_RF_PWD_ICLOBUF_2G, true);
}
/* Write RF banks on hw */ for (i = 0; i < ah->ah_rf_banks_size; i++) {
AR5K_REG_WAIT(i);
ath5k_hw_reg_write(ah, rfb[i], ini_rfb[i].rfb_ctrl_register);
}
if (channel->band == NL80211_BAND_2GHZ) { /* Map 2GHz channel to 5GHz Atheros channel ID */
ret = ath5k_hw_rf5111_chan2athchan(
ieee80211_frequency_to_channel(channel->center_freq),
&ath5k_channel_2ghz); if (ret) return ret;
/** *ath5k_hw_channel()-Setachannelontheradiochip *@ah:The&structath5k_hw *@channel:The&structieee80211_channel * *Thisisthemainfunctioncalledtosetachannelonthe *radiochipbasedontheradiochipversion.
*/ staticint
ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel)
{ int ret; /* *CheckboundssupportedbythePHY(wedon'tcareaboutregulatory *restrictionsatthispoint).
*/ if (!ath5k_channel_ok(ah, channel)) {
ATH5K_ERR(ah, "channel frequency (%u MHz) out of supported " "band range\n",
channel->center_freq); return -EINVAL;
}
/* *Setthechannelandwait
*/ switch (ah->ah_radio) { case AR5K_RF5110:
ret = ath5k_hw_rf5110_channel(ah, channel); break; case AR5K_RF5111:
ret = ath5k_hw_rf5111_channel(ah, channel); break; case AR5K_RF2317: case AR5K_RF2425:
ret = ath5k_hw_rf2425_channel(ah, channel); break; default:
ret = ath5k_hw_rf5112_channel(ah, channel); break;
}
if (ret) return ret;
/* Set JAPAN setting for channel 14 */ if (channel->center_freq == 2484) {
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL,
AR5K_PHY_CCKTXCTL_JAPAN);
} else {
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL,
AR5K_PHY_CCKTXCTL_WORLD);
}
/* keep last value if calibration hasn't completed */ if (ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL) & AR5K_PHY_AGCCTL_NF) {
ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "NF did not complete in calibration window\n");
/* load noise floor (in .5 dBm) so the hardware will use it */
val = ath5k_hw_reg_read(ah, AR5K_PHY_NF) & ~AR5K_PHY_NF_M;
val |= (nf * 2) & AR5K_PHY_NF_M;
ath5k_hw_reg_write(ah, val, AR5K_PHY_NF);
ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
AR5K_PHY_AGCCTL_CAL, 0, false);
/* Reset to normal state */
ath5k_hw_reg_write(ah, phy_sig, AR5K_PHY_SIG);
ath5k_hw_reg_write(ah, phy_agc, AR5K_PHY_AGCCOARSE);
ath5k_hw_reg_write(ah, phy_sat, AR5K_PHY_ADCSAT);
/* Skip if I/Q calibration is not needed or if it's still running */ if (!ah->ah_iq_cal_needed) return -EINVAL; elseif (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN) {
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE, "I/Q calibration still running"); return -EBUSY;
}
/* Calibration has finished, get the results and re-run */
/* Work around for empty results which can apparently happen on 5212:
* Read registers up to 10 times until we get both i_pr and q_pwr */ for (i = 0; i <= 10; i++) {
iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR);
i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I);
q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q);
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE, "iq_corr:%x i_pwr:%x q_pwr:%x", iq_corr, i_pwr, q_pwr); if (i_pwr && q_pwr) break;
}
/* In case i_coffd became zero, cancel calibration *notonlyit'stoosmall,it'llalsoresultadivide
* by zero later on. */ if (i_coffd == 0 || q_coffd < 2) return -ECANCELED;
/* Protect against loss of sign bits */
i_coff = (-iq_corr) / i_coffd;
i_coff = clamp(i_coff, -32, 31); /* signed 6 bit */
if (ah->ah_version == AR5K_AR5211)
q_coff = (i_pwr / q_coffd) - 64; else
q_coff = (i_pwr / q_coffd) - 128;
q_coff = clamp(q_coff, -16, 15); /* signed 5 bit */
/* Commit new I/Q values (set enable bit last to match HAL sources) */
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_I_COFF, i_coff);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_Q_COFF, q_coff);
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE);
/* Re-enable calibration -if we don't we'll commit
* the same values again and again */
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ,
AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15);
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_RUN);
return0;
}
/** *ath5k_hw_phy_calibrate()-PerformaPHYcalibration *@ah:The&structath5k_hw *@channel:The&structieee80211_channel * *Themainfunctionwecallfromabovetoperform *ashortorfullPHYcalibrationbasedonRFchip *andcurrentchannel
*/ int
ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel)
{ int ret;
if (ah->ah_radio == AR5K_RF5110) return ath5k_hw_rf5110_calibrate(ah, channel);
ret = ath5k_hw_rf511x_iq_calibrate(ah); if (ret) {
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE, "No I/Q correction performed (%uMHz)\n",
channel->center_freq);
/* Happens all the time if there is not much
* traffic, consider it normal behaviour. */
ret = 0;
}
/* On full calibration request a PAPD probe for
* gainf calibration if needed */ if ((ah->ah_cal_mask & AR5K_CALIBRATION_FULL) &&
(ah->ah_radio == AR5K_RF5111 ||
ah->ah_radio == AR5K_RF5112) &&
channel->hw_value != AR5K_MODE_11B)
ath5k_hw_request_rfgain_probe(ah);
/* Update noise floor */ if (!(ah->ah_cal_mask & AR5K_CALIBRATION_NF))
ath5k_hw_update_noise_floor(ah);
/* Convert current frequency to fbin value (the same way channels *arestoredonEEPROM,checkoutath5k_eeprom_bin2freq)andscale
* up by 2 so we can compare it later */ if (channel->band == NL80211_BAND_2GHZ) {
chan_fbin = (channel->center_freq - 2300) * 10;
freq_band = AR5K_EEPROM_BAND_2GHZ;
} else {
chan_fbin = (channel->center_freq - 4900) * 10;
freq_band = AR5K_EEPROM_BAND_5GHZ;
}
/* Check if any spur_chan_fbin from EEPROM is
* within our current channel's spur detection range */
spur_chan_fbin = AR5K_EEPROM_NO_SPUR;
spur_detection_window = AR5K_SPUR_CHAN_WIDTH; /* XXX: Half/Quarter channels ?*/ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
spur_detection_window *= 2;
for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) {
spur_chan_fbin = ee->ee_spur_chans[i][freq_band];
/* Note: mask cleans AR5K_EEPROM_NO_SPUR flag
* so it's zero if we got nothing from EEPROM */ if (spur_chan_fbin == AR5K_EEPROM_NO_SPUR) {
spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK; break;
}
/* We need to enable spur filter for this channel */ if (spur_chan_fbin) {
spur_offset = spur_chan_fbin - chan_fbin; /* *Calculatedeltas: *spur_freq_sigma_delta->spur_offset/sample_freq<<21 *spur_delta_phase->spur_offset/chip_freq<<11 *Note:Bothvalueshave100Hzresolution
*/ switch (ah->ah_bwmode) { case AR5K_BWMODE_40MHZ: /* Both sample_freq and chip_freq are 80MHz */
spur_delta_phase = (spur_offset << 16) / 25;
spur_freq_sigma_delta = (spur_delta_phase >> 10);
symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz * 2; break; case AR5K_BWMODE_10MHZ: /* Both sample_freq and chip_freq are 20MHz (?) */
spur_delta_phase = (spur_offset << 18) / 25;
spur_freq_sigma_delta = (spur_delta_phase >> 10);
symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 2; break; case AR5K_BWMODE_5MHZ: /* Both sample_freq and chip_freq are 10MHz (?) */
spur_delta_phase = (spur_offset << 19) / 25;
spur_freq_sigma_delta = (spur_delta_phase >> 10);
symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 4; break; default: if (channel->band == NL80211_BAND_5GHZ) { /* Both sample_freq and chip_freq are 40MHz */
spur_delta_phase = (spur_offset << 17) / 25;
spur_freq_sigma_delta =
(spur_delta_phase >> 10);
symbol_width =
AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
} else { /* sample_freq -> 40MHz chip_freq -> 44MHz
* (for b compatibility) */
spur_delta_phase = (spur_offset << 17) / 25;
spur_freq_sigma_delta =
(spur_offset << 8) / 55;
symbol_width =
AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
} break;
}
/* Calculate pilot and magnitude masks */
/* Scale up spur_offset by 1000 to switch to 100HZ resolution *anddividebysymbol_widthtofindhowmanysymbolswehave
* Note: number of symbols is scaled up by 16 */
num_symbols_x16 = ((spur_offset * 1000) << 4) / symbol_width;
/* Spur is on a symbol if num_symbols_x16 % 16 is zero */ if (!(num_symbols_x16 & 0xF)) /* _X_ */
num_symbol_offsets = 3; else /* _xx_ */
num_symbol_offsets = 4;
for (i = 0; i < num_symbol_offsets; i++) {
/* Calculate pilot mask */
s32 curr_sym_off =
(num_symbols_x16 / 16) + i + 25;
/* Pilot magnitude mask seems to be a way to *declaretheboundariesforourdetection *windoworsomething,it's2forthemiddle *value(s)wherethesymbolisexpectedtobe
* and 1 on the boundary values */
u8 plt_mag_map =
(i == 0 || i == (num_symbol_offsets - 1))
? 1 : 2;
/* if channel is not initialized yet we can't set the antennas
* so just store the mode. it will be set on the next reset */ if (channel == NULL) {
ah->ah_ant_mode = ant_mode; return;
}
if (sta_id1)
AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, sta_id1);
ath5k_hw_set_antenna_switch(ah, ee_mode); /* Note: set diversity before default antenna
* because it won't work correctly */
ath5k_hw_set_fast_div(ah, ee_mode, fast_div);
ath5k_hw_set_def_antenna(ah, def_ant);
}
/* We want the whole line, so adjust boundaries *tocovertheentirepowerrange.Notethat *powervaluesarealready0.25dBsononeed
* to multiply pwr_i by 2 */ if (type == AR5K_PWRTABLE_LINEAR_PCDAC) {
pwr_i = pmin;
pmin = 0;
pmax = 63;
}
/* Find surrounding turning points (TPs)
* and interpolate between them */ for (i = 0; (i <= (u16) (pmax - pmin)) &&
(i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {
/* We passed the right TP, move to the next set of TPs *ifwepassthelastTP,extrapolateaboveusingthelast
* two TPs for ratio */ if ((pwr_i > pwr[idx[1]]) && (idx[1] < num_points - 1)) {
idx[0]++;
idx[1]++;
}
switch (channel->hw_value) { case AR5K_MODE_11A: if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
ctl_mode |= AR5K_CTL_TURBO; else
ctl_mode |= AR5K_CTL_11A; break; case AR5K_MODE_11G: if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
ctl_mode |= AR5K_CTL_TURBOG; else
ctl_mode |= AR5K_CTL_11G; break; case AR5K_MODE_11B:
ctl_mode |= AR5K_CTL_11B; break; default: return;
}
for (i = 0; i < ee->ee_ctls; i++) { if (ctl_val[i] == ctl_mode) {
ctl_idx = i; break;
}
}
/* If we have a CTL dataset available grab it and find the
* edge power for our frequency */ if (ctl_idx == 0xFF) return;
/* Edge powers are sorted by frequency from lower *tohigher.EachCTLcorrespondsto8edgepower
* measurements. */
rep_idx = ctl_idx * AR5K_EEPROM_N_EDGES;
/* Don't do boundaries check because we *mighthavemorethatonebandsdefined
* for this mode */
/* Get the edge power that's closer to our
* frequency */ for (i = 0; i < AR5K_EEPROM_N_EDGES; i++) {
rep_idx += i; if (target <= rep[rep_idx].freq)
edge_pwr = (s16) rep[rep_idx].edge;
}
if (edge_pwr)
ah->ah_txpower.txp_max_pwr = 4 * min(edge_pwr, max_chan_pwr);
}
/* Extrapolate below minimum using pcdac_0 */
pcdac_i = 0; for (i = 0; i < min_pwr; i++)
pcdac_out[pcdac_i++] = pcdac_0;
/* Copy values from pcdac_tmp */
pwr_idx = min_pwr; for (i = 0; pwr_idx <= max_pwr &&
pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE; i++) {
pcdac_out[pcdac_i++] = pcdac_tmp[i];
pwr_idx++;
}
/* Extrapolate above maximum */ while (pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE)
pcdac_out[pcdac_i++] = pcdac_n;
}
/** *ath5k_combine_linear_pcdac_curves()-CombineavailablePCDACCurves *@ah:The&structath5k_hw *@table_min:Minimumpower(xmin) *@table_max:Maximumpower(xmax) *@pdcurves:Numberofpdcurves * *CombineavailableXPDCurvesandfillLinearPowertoPCDACtableonRF5112 *RFX112canhaveupto2curves(oneforlowtxpowerrangeandonefor *highertxpowerrange).Weneedtoputthembothonpcdac_outandplace *theminthecorrectlocation.Incaseweonlyhaveonecurveavailable *justfititonpcdac_out(it'ssupposedtocovertheentirerangeof *availablepwrlevelssinceit'salwaysthehigherpowercurve).Extrapolate *belowandabovefinaltableifneeded.
*/ staticvoid
ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min,
s16 *table_max, u8 pdcurves)
{
u8 *pcdac_out = ah->ah_txpower.txp_pd_table;
u8 *pcdac_low_pwr;
u8 *pcdac_high_pwr;
u8 *pcdac_tmp;
u8 pwr;
s16 max_pwr_idx;
s16 min_pwr_idx;
s16 mid_pwr_idx = 0; /* Edge flag turns on the 7nth bit on the PCDAC *todeclarethehigherpowercurve(forcevalues *tobegreaterthan64).Ifweonlyhaveonecurve *wedon'tneedtosetthis,ifwehave2curvesand *fillthetablebackwardsthiscanalsobeusedto
* switch from higher power curve to lower power curve */
u8 edge_flag; int i;
/* When we have only one curve available *that'sthehigherpowercurve.Ifwehave *twocurvesthefirstisthehighpowercurve
* and the next is the low power curve. */ if (pdcurves > 1) {
pcdac_low_pwr = ah->ah_txpower.tmpL[1];
pcdac_high_pwr = ah->ah_txpower.tmpL[0];
mid_pwr_idx = table_max[1] - table_min[1] - 1;
max_pwr_idx = (table_max[0] - table_min[0]) / 2;
/* If table size goes beyond 31.5dB, keep the *upper31.5dBrangewhensettingtxpower.
* Note: 126 = 31.5 dB in quarter dB steps */ if (table_max[0] - table_min[1] > 126)
min_pwr_idx = table_max[0] - 126; else
min_pwr_idx = table_min[1];
/* Since we fill table backwards
* start from high power curve */
pcdac_tmp = pcdac_high_pwr;
/* This is used when setting tx power*/
ah->ah_txpower.txp_min_idx = min_pwr_idx / 2;
/* Fill Power to PCDAC table backwards */
pwr = max_pwr_idx; for (i = 63; i >= 0; i--) { /* Entering lower power range, reset *edgeflagandsetpcdac_tmptolower
* power curve.*/ if (edge_flag == 0x40 &&
(2 * pwr <= (table_max[1] - table_min[0]) || pwr == 0)) {
edge_flag = 0x00;
pcdac_tmp = pcdac_low_pwr;
pwr = mid_pwr_idx / 2;
}
/* Don't go below 1, extrapolate below if we have *alreadyswitchedtothelowerpowercurve-or *weonlyhaveonecurveandedge_flagiszero
* anyway */ if (pcdac_tmp[pwr] < 1 && (edge_flag == 0x00)) { while (i >= 0) {
pcdac_out[i] = pcdac_out[i + 1];
i--;
} break;
}
pcdac_out[i] = pcdac_tmp[pwr] | edge_flag;
/* Extrapolate above if pcdac is greater than *126-thiscanhappenbecauseweORpcdac_out
* value with edge_flag on high power curve */ if (pcdac_out[i] > 126)
pcdac_out[i] = 126;
/* Note: Register value is initialized on initvals *thereisnofeedbackfromhw.
* XXX: What about pd_gain_overlap from EEPROM ? */
pd_gain_overlap = (u8) ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG5) &
AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP;
if (pdg == pdcurves - 1) /* 2 dB boundary stretch for last
* (higher power) curve */
gain_boundaries[pdg] = pwr_max[pdg] + 4; else /* Set gain boundary in the middle
* between this curve and the next one */
gain_boundaries[pdg] =
(pwr_max[pdg] + pwr_min[pdg + 1]) / 2;
/* Sanity check in case our 2 db stretch got out of
* range. */ if (gain_boundaries[pdg] > AR5K_TUNE_MAX_TXPOWER)
gain_boundaries[pdg] = AR5K_TUNE_MAX_TXPOWER;
/* For the first curve (lower power)
* start from 0 dB */ if (pdg == 0)
pdadc_0 = 0; else /* For the other curves use the gain overlap */
pdadc_0 = (gain_boundaries[pdg - 1] - pwr_min[pdg]) -
pd_gain_overlap;
/* Force each power step to be at least 0.5 dB */
pwr_step = max(pdadc_tmp[1] - pdadc_tmp[0], 1);
/* If pdadc_0 is negative, we need to extrapolate
* below this pdgain by a number of pwr_steps */ while ((pdadc_0 < 0) && (pdadc_i < 128)) {
s16 tmp = pdadc_tmp[0] + pdadc_0 * pwr_step;
pdadc_out[pdadc_i++] = (tmp < 0) ? 0 : (u8) tmp;
pdadc_0++;
}
/* Set last pwr level, using gain boundaries */
pdadc_n = gain_boundaries[pdg] + pd_gain_overlap - pwr_min[pdg]; /* Limit it to be inside pwr range */
table_size = pwr_max[pdg] - pwr_min[pdg];
max_idx = min(pdadc_n, table_size);
/* Fill pdadc_out table */ while (pdadc_0 < max_idx && pdadc_i < 128)
pdadc_out[pdadc_i++] = pdadc_tmp[pdadc_0++];
/* Need to extrapolate above this pdgain? */ if (pdadc_n <= max_idx) continue;
/* Force each power step to be at least 0.5 dB */
pwr_step = max(pdadc_tmp[table_size - 1] -
pdadc_tmp[table_size - 2], 1);
/* Get surrounding freq piers for this channel */
ath5k_get_chan_pcal_surrounding_piers(ah, channel,
&pcinfo_L,
&pcinfo_R);
/* Loop over pd gain curves on
* surrounding freq piers by index */ for (pdg = 0; pdg < ee->ee_pd_gains[ee_mode]; pdg++) {
/* Fill curves in reverse order *fromlowerpower(maxgain) *tohigherpower.Usecurve->idx
* backmapping we did on eeprom init */
u8 idx = pdg_curve_to_idx[pdg];
/* Grab the needed curves by index */
pdg_L = &pcinfo_L->pd_curves[idx];
pdg_R = &pcinfo_R->pd_curves[idx];
/* Now create the curves on surrounding channels *andinterpolateifneededtogetthefinal
* curve for this gain on this channel */ switch (type) { case AR5K_PWRTABLE_LINEAR_PCDAC: /* Override min/max so that we don't loose
* accuracy (don't divide by 2) */
table_min[pdg] = min(pdg_L->pd_pwr[0],
pdg_R->pd_pwr[0]);
/* Override minimum so that we don't get *outofboundswhileextrapolating *below.Don'tdothiswhenwehave2 *curvesandweareonthehighpowercurve
* because table_min is ok in this case */ if (!(ee->ee_pd_gains[ee_mode] > 1 && pdg == 0)) {
/* Don't go too low because we will *misstheupperpartofthecurve. *Note:126=31.5dB(maxpowersupported)
* in 0.25dB units */ if (table_max[pdg] - table_min[pdg] > 126)
table_min[pdg] = table_max[pdg] - 126;
}
fallthrough; case AR5K_PWRTABLE_PWR_TO_PCDAC: case AR5K_PWRTABLE_PWR_TO_PDADC:
/* Interpolate between curves *ofsurroundingfreqpiersto *getthefinalcurveforthis *pdgain.Re-usetmpLforinterpolation
* output */ for (i = 0; (i < (u16) (table_max[pdg] - table_min[pdg])) &&
(i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {
tmpL[i] = (u8) ath5k_get_interpolated_value(target,
(s16) pcinfo_L->freq,
(s16) pcinfo_R->freq,
(s16) tmpL[i],
(s16) tmpR[i]);
}
}
/* Now we have a set of curves for this *channelontmpL(xrangeistable_max-table_min *andyvaluesaretmpL[pdg][])sortedinthesame *orderasEEPROM(becausewe'veusedthebackmapping). *SoforRF5112it'sfromhigherpowertolowerpower *andforRF2413it'sfromlowerpowertohigherpower.
* For RF5111 we only have one curve. */
/* Fill min and max power levels for this *channelbyinterpolatingthevalueson
* surrounding channels to complete the dataset */
ah->ah_txpower.txp_min_pwr = ath5k_get_interpolated_value(target,
(s16) pcinfo_L->freq,
(s16) pcinfo_R->freq,
pcinfo_L->min_pwr, pcinfo_R->min_pwr);
/* Fill PCDAC/PDADC table */ switch (type) { case AR5K_PWRTABLE_LINEAR_PCDAC: /* For RF5112 we can have one or two curves *andeachcurvecoversacertainpowerlvl
* range so we need to do some more processing */
ath5k_combine_linear_pcdac_curves(ah, table_min, table_max,
ee->ee_pd_gains[ee_mode]);
/* Set txp.offset so that we can *matchmaxpowervaluewithmax
* table index */
ah->ah_txpower.txp_offset = 64 - (table_max[0] / 2); break; case AR5K_PWRTABLE_PWR_TO_PCDAC: /* We are done for RF5111 since it has only
* one curve, just fit the curve on the table */
ath5k_fill_pwr_to_pcdac_table(ah, table_min, table_max);
/* No rate powertable adjustment for RF5111 */
ah->ah_txpower.txp_min_idx = 0;
ah->ah_txpower.txp_offset = 0; break; case AR5K_PWRTABLE_PWR_TO_PDADC: /* Set PDADC boundaries and fill
* final PDADC table */
ath5k_combine_pwr_to_pdadc_curves(ah, table_min, table_max,
ee->ee_pd_gains[ee_mode]);
/* Set txp.offset, note that table_min
* can be negative */
ah->ah_txpower.txp_offset = table_min[0]; break; default: return -EINVAL;
}
/* max_pwr is power level we got from driver/user in 0.5dB
* units, switch to 0.25dB units so we can compare */
max_pwr *= 2;
max_pwr = min(max_pwr, (u16) ah->ah_txpower.txp_max_pwr) / 2;
/* CCK rates have different peak to average ratio *sowehavetotweaktheirpowersothatgainf *correctionworksok.ForthisweuseOFDMto
* CCK delta from eeprom */ if ((ee_mode == AR5K_EEPROM_MODE_11G) &&
(ah->ah_phy_revision < AR5K_SREV_PHY_5212A)) for (i = 8; i <= 15; i++)
rates[i] -= ah->ah_txpower.txp_cck_ofdm_gainf_delta;
/* Save min/max and current tx power for this channel *in0.25dBunits. * *Note:Weuserates[0]forcurrenttxpowerbecause *itcoversmostoftherates,inmostcases.It'sour
* tx power limit and what the user expects to see. */
ah->ah_txpower.txp_min_pwr = 2 * rates[7];
ah->ah_txpower.txp_cur_pwr = 2 * rates[0];
/* Set max txpower for correct OFDM operation on all rates *-thatisthetxpowerfor54Mbit-,it'susedforthePAPD
* gain probe and it's in 0.5dB units */
ah->ah_txpower.txp_ofdm = rates[7];
/* Now that we have all rates setup use table offset to *matchthepowerrangesetbyuserwiththepowerindices
* on PCDAC/PDADC table */ for (i = 0; i < 16; i++) {
rate_idx_scaled = rates[i] + ah->ah_txpower.txp_offset; /* Don't get out of bounds */ if (rate_idx_scaled > 63)
rate_idx_scaled = 63; if (rate_idx_scaled < 0)
rate_idx_scaled = 0;
rates[i] = rate_idx_scaled;
}
}
/* Initialize TX power table */ switch (ah->ah_radio) { case AR5K_RF5110: /* TODO */ return0; case AR5K_RF5111:
type = AR5K_PWRTABLE_PWR_TO_PCDAC; break; case AR5K_RF5112:
type = AR5K_PWRTABLE_LINEAR_PCDAC; break; case AR5K_RF2413: case AR5K_RF5413: case AR5K_RF2316: case AR5K_RF2317: case AR5K_RF2425:
type = AR5K_PWRTABLE_PWR_TO_PDADC; break; default: return -EINVAL;
}
/* *Ifwedon'tchangechannel/modeskiptxpowertablecalculation *andusethecachedone.
*/ if (!ah->ah_txpower.txp_setup ||
(channel->hw_value != curr_channel->hw_value) ||
(channel->center_freq != curr_channel->center_freq)) { /* Reset TX power values but preserve requested
* tx power from above */ int requested_txpower = ah->ah_txpower.txp_requested;
/* *Onfastchannelchangeweonlysetthesynthparameters *whilePHYisrunning,enablecalibrationandskiptherest.
*/ if (fast) {
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_RFBUS_REQ,
AR5K_PHY_RFBUS_REQ_REQUEST); for (i = 0; i < 100; i++) { if (ath5k_hw_reg_read(ah, AR5K_PHY_RFBUS_GRANT)) break;
udelay(5);
} /* Failed */ if (i >= 100) return -EIO;
/* Set channel and wait for synth */
ret = ath5k_hw_channel(ah, channel); if (ret) return ret;
/* Write OFDM timings on 5212*/ if (ah->ah_version == AR5K_AR5212 &&
channel->hw_value != AR5K_MODE_11B) {
ret = ath5k_hw_write_ofdm_timings(ah, channel); if (ret) return ret;
/* Spur info is available only from EEPROM versions *greaterthan5.3,buttheEEPROMroutineswilluse
* static values for older versions */ if (ah->ah_mac_srev >= AR5K_SREV_AR5424)
ath5k_hw_set_spur_mitigation_filter(ah,
channel);
}
/* If we used fast channel switching *wearedone,releaseRFbusand *fireupNFcalibration. * *Note:OnlyNFcalibrationdueto *channelchange,notAGCcalibration *sinceAGCisstillrunning!
*/ if (fast) { /* *ReleaseRFBusgrant
*/
AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_RFBUS_REQ,
AR5K_PHY_RFBUS_REQ_REQUEST);
/* At the same time start I/Q calibration for QAM constellation
* -no need for CCK- */
ah->ah_iq_cal_needed = false; if (!(mode == AR5K_MODE_11B)) {
ah->ah_iq_cal_needed = true;
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ,
AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15);
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ,
AR5K_PHY_IQ_RUN);
}
/* Wait for gain calibration to finish (we check for I/Q calibration
* during ath5k_phy_calibrate) */ if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
AR5K_PHY_AGCCTL_CAL, 0, false)) {
ATH5K_ERR(ah, "gain calibration timeout (%uMHz)\n",
channel->center_freq);
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.98Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-02)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.