|
@@ -556,7 +556,6 @@ int ar9170_init_phy(struct ar9170 *ar, enum ieee80211_band band)
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
|
- /* TODO: (heavy clip) regulatory domain power level fine-tuning. */
|
|
|
err = ar9170_init_phy_from_eeprom(ar, is_2ghz, is_40mhz);
|
|
|
if (err)
|
|
|
return err;
|
|
@@ -1238,6 +1237,164 @@ static int ar9170_set_freq_cal_data(struct ar9170 *ar,
|
|
|
return ar9170_regwrite_result();
|
|
|
}
|
|
|
|
|
|
+static u8 ar9170_get_max_edge_power(struct ar9170 *ar,
|
|
|
+ struct ar9170_calctl_edges edges[],
|
|
|
+ u32 freq)
|
|
|
+{
|
|
|
+/* TODO: move somewhere else */
|
|
|
+#define AR5416_MAX_RATE_POWER 63
|
|
|
+
|
|
|
+ int i;
|
|
|
+ u8 rc = AR5416_MAX_RATE_POWER;
|
|
|
+ u8 f;
|
|
|
+ if (freq < 3000)
|
|
|
+ f = freq - 2300;
|
|
|
+ else
|
|
|
+ f = (freq - 4800) / 5;
|
|
|
+
|
|
|
+ for (i = 0; i < AR5416_NUM_BAND_EDGES; i++) {
|
|
|
+ if (edges[i].channel == 0xff)
|
|
|
+ break;
|
|
|
+ if (f == edges[i].channel) {
|
|
|
+ /* exact freq match */
|
|
|
+ rc = edges[i].power_flags & ~AR9170_CALCTL_EDGE_FLAGS;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (i > 0 && f < edges[i].channel) {
|
|
|
+ if (f > edges[i-1].channel &&
|
|
|
+ edges[i-1].power_flags & AR9170_CALCTL_EDGE_FLAGS) {
|
|
|
+ /* lower channel has the inband flag set */
|
|
|
+ rc = edges[i-1].power_flags &
|
|
|
+ ~AR9170_CALCTL_EDGE_FLAGS;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i == AR5416_NUM_BAND_EDGES) {
|
|
|
+ if (f > edges[i-1].channel &&
|
|
|
+ edges[i-1].power_flags & AR9170_CALCTL_EDGE_FLAGS) {
|
|
|
+ /* lower channel has the inband flag set */
|
|
|
+ rc = edges[i-1].power_flags &
|
|
|
+ ~AR9170_CALCTL_EDGE_FLAGS;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+/* calculate the conformance test limits and apply them to ar->power*
|
|
|
+ * (derived from otus hal/hpmain.c, line 3706 ff.)
|
|
|
+ */
|
|
|
+static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw)
|
|
|
+{
|
|
|
+ u8 ctl_grp; /* CTL group */
|
|
|
+ u8 ctl_idx; /* CTL index */
|
|
|
+ int i, j;
|
|
|
+ struct ctl_modes {
|
|
|
+ u8 ctl_mode;
|
|
|
+ u8 max_power;
|
|
|
+ u8 *pwr_cal_data;
|
|
|
+ int pwr_cal_len;
|
|
|
+ } *modes;
|
|
|
+
|
|
|
+ /* order is relevant in the mode_list_*: we fall back to the
|
|
|
+ * lower indices if any mode is missed in the EEPROM.
|
|
|
+ */
|
|
|
+ struct ctl_modes mode_list_2ghz[] = {
|
|
|
+ { CTL_11B, 0, ar->power_2G_cck, 4 },
|
|
|
+ { CTL_11G, 0, ar->power_2G_ofdm, 4 },
|
|
|
+ { CTL_2GHT20, 0, ar->power_2G_ht20, 8 },
|
|
|
+ { CTL_2GHT40, 0, ar->power_2G_ht40, 8 },
|
|
|
+ };
|
|
|
+ struct ctl_modes mode_list_5ghz[] = {
|
|
|
+ { CTL_11A, 0, ar->power_5G_leg, 4 },
|
|
|
+ { CTL_5GHT20, 0, ar->power_5G_ht20, 8 },
|
|
|
+ { CTL_5GHT40, 0, ar->power_5G_ht40, 8 },
|
|
|
+ };
|
|
|
+ int nr_modes;
|
|
|
+
|
|
|
+#define EDGES(c, n) (ar->eeprom.ctl_data[c].control_edges[n])
|
|
|
+
|
|
|
+ /* TODO: investigate the differences between OTUS'
|
|
|
+ * hpreg.c::zfHpGetRegulatoryDomain() and
|
|
|
+ * ath/regd.c::ath_regd_get_band_ctl() -
|
|
|
+ * e.g. for FCC3_WORLD the OTUS procedure
|
|
|
+ * always returns CTL_FCC, while the one in ath/ delivers
|
|
|
+ * CTL_ETSI for 2GHz and CTL_FCC for 5GHz.
|
|
|
+ */
|
|
|
+ ctl_grp = ath_regd_get_band_ctl(&ar->common.regulatory,
|
|
|
+ ar->hw->conf.channel->band);
|
|
|
+
|
|
|
+ /* ctl group not found - either invalid band (NO_CTL) or ww roaming */
|
|
|
+ if (ctl_grp == NO_CTL || ctl_grp == SD_NO_CTL)
|
|
|
+ ctl_grp = CTL_FCC;
|
|
|
+
|
|
|
+ if (ctl_grp != CTL_FCC)
|
|
|
+ /* skip CTL and heavy clip for CTL_MKK and CTL_ETSI */
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) {
|
|
|
+ modes = mode_list_2ghz;
|
|
|
+ nr_modes = ARRAY_SIZE(mode_list_2ghz);
|
|
|
+ } else {
|
|
|
+ modes = mode_list_5ghz;
|
|
|
+ nr_modes = ARRAY_SIZE(mode_list_5ghz);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < nr_modes; i++) {
|
|
|
+ u8 c = ctl_grp | modes[i].ctl_mode;
|
|
|
+ for (ctl_idx = 0; ctl_idx < AR5416_NUM_CTLS; ctl_idx++)
|
|
|
+ if (c == ar->eeprom.ctl_index[ctl_idx])
|
|
|
+ break;
|
|
|
+ if (ctl_idx < AR5416_NUM_CTLS) {
|
|
|
+ int f_off = 0;
|
|
|
+
|
|
|
+ /* adjust freq for 40MHz */
|
|
|
+ if (modes[i].ctl_mode == CTL_2GHT40 ||
|
|
|
+ modes[i].ctl_mode == CTL_5GHT40) {
|
|
|
+ if (bw == AR9170_BW_40_BELOW)
|
|
|
+ f_off = -10;
|
|
|
+ else
|
|
|
+ f_off = 10;
|
|
|
+ }
|
|
|
+
|
|
|
+ modes[i].max_power =
|
|
|
+ ar9170_get_max_edge_power(ar, EDGES(ctl_idx, 1),
|
|
|
+ freq+f_off);
|
|
|
+
|
|
|
+ /* TODO: check if the regulatory max. power is
|
|
|
+ * controlled by cfg80211 for DFS
|
|
|
+ * (hpmain applies it to max_power itself for DFS freq)
|
|
|
+ */
|
|
|
+
|
|
|
+ } else {
|
|
|
+ /* Workaround in otus driver, hpmain.c, line 3906:
|
|
|
+ * if no data for 5GHT20 are found, take the
|
|
|
+ * legacy 5G value.
|
|
|
+ * We extend this here to fallback from any other *HT or
|
|
|
+ * 11G, too.
|
|
|
+ */
|
|
|
+ int k = i;
|
|
|
+
|
|
|
+ modes[i].max_power = AR5416_MAX_RATE_POWER;
|
|
|
+ while (k-- > 0) {
|
|
|
+ if (modes[k].max_power !=
|
|
|
+ AR5416_MAX_RATE_POWER) {
|
|
|
+ modes[i].max_power = modes[k].max_power;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* apply max power to pwr_cal_data (ar->power_*) */
|
|
|
+ for (j = 0; j < modes[i].pwr_cal_len; j++) {
|
|
|
+ modes[i].pwr_cal_data[j] = min(modes[i].pwr_cal_data[j],
|
|
|
+ modes[i].max_power);
|
|
|
+ }
|
|
|
+ }
|
|
|
+#undef EDGES
|
|
|
+}
|
|
|
+
|
|
|
static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw)
|
|
|
{
|
|
|
struct ar9170_calibration_target_power_legacy *ctpl;
|
|
@@ -1340,6 +1497,12 @@ static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw)
|
|
|
ctph[idx + 1].power[n]);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ /* calc. conformance test limits and apply to ar->power*[] */
|
|
|
+ ar9170_calc_ctl(ar, freq, bw);
|
|
|
+
|
|
|
+ /* TODO: (heavy clip) regulatory domain power level fine-tuning. */
|
|
|
+
|
|
|
/* set ACK/CTS TX power */
|
|
|
ar9170_regwrite_begin(ar);
|
|
|
|