2011-11-28 16:18:11

by Daniel Golle

[permalink] [raw]
Subject: [PATCH v1 3/3] support for antenna configuration profiles

This adds support for controlling AR_PHY_SWITCH_COM of the AR9285 by using
antenna configuration profiles to ath9k.

Signed-off-by: Daniel Golle <[email protected]>
---
drivers/net/wireless/ath/ath9k/ar9002_phy.c | 13 ++++
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 8 ++-
drivers/net/wireless/ath/ath9k/eeprom.h | 1 +
drivers/net/wireless/ath/ath9k/eeprom_4k.c | 29 +++++++-
drivers/net/wireless/ath/ath9k/eeprom_9287.c | 8 ++-
drivers/net/wireless/ath/ath9k/eeprom_def.c | 8 ++-
drivers/net/wireless/ath/ath9k/hw.h | 28 +++++++
drivers/net/wireless/ath/ath9k/init.c | 93 ++++++++++++++++++++++++
drivers/net/wireless/ath/ath9k/main.c | 36 +++++++++
include/linux/ath9k_platform.h | 6 ++
10 files changed, 225 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
index 3cbbb03..babf8b0 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
@@ -556,6 +556,16 @@ static void ar9002_hw_antdiv_comb_conf_set(struct ath_hw *ah,
REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
}

+static void ar9002_hw_extant_switchcom_set(struct ath_hw *ah, u32 switch_com_value)
+{
+ REG_WRITE(ah, AR_PHY_SWITCH_COM, switch_com_value);
+}
+
+static u32 ar9002_hw_extant_switchcom_get(struct ath_hw *ah)
+{
+ return REG_READ(ah, AR_PHY_SWITCH_COM);
+}
+
void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
{
struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
@@ -573,5 +583,8 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
ops->antdiv_comb_conf_get = ar9002_hw_antdiv_comb_conf_get;
ops->antdiv_comb_conf_set = ar9002_hw_antdiv_comb_conf_set;

+ ops->extant_switchcom_set = ar9002_hw_extant_switchcom_set;
+ ops->extant_switchcom_get = ar9002_hw_extant_switchcom_get;
+
ar9002_hw_set_nf_limits(ah);
}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index a93bd63..85196de 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -5140,6 +5140,11 @@ unsigned int ar9003_get_paprd_scale_factor(struct ath_hw *ah,
}
}

+static u32 ath9k_hw_ar9300_get_switch_com(struct ath_hw *ah)
+{
+ return 0;
+}
+
const struct eeprom_ops eep_ar9300_ops = {
.check_eeprom = ath9k_hw_ar9300_check_eeprom,
.get_eeprom = ath9k_hw_ar9300_get_eeprom,
@@ -5150,5 +5155,6 @@ const struct eeprom_ops eep_ar9300_ops = {
.set_board_values = ath9k_hw_ar9300_set_board_values,
.set_addac = ath9k_hw_ar9300_set_addac,
.set_txpower = ath9k_hw_ar9300_set_txpower,
- .get_spur_channel = ath9k_hw_ar9300_get_spur_channel
+ .get_spur_channel = ath9k_hw_ar9300_get_spur_channel,
+ .get_switch_com = ath9k_hw_ar9300_get_switch_com
};
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h
index 5ff7ab9..98da6c5 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/eeprom.h
@@ -656,6 +656,7 @@ struct eeprom_ops {
u16 cfgCtl, u8 twiceAntennaReduction,
u8 powerLimit, bool test);
u16 (*get_spur_channel)(struct ath_hw *ah, u16 i, bool is2GHz);
+ u32 (*get_switch_com)(struct ath_hw *ah);
};

void ath9k_hw_analog_shift_regwrite(struct ath_hw *ah, u32 reg, u32 val);
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
index 9a7520f..b295ac0 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
@@ -815,6 +815,30 @@ static void ath9k_hw_4k_set_gain(struct ath_hw *ah,
}

/*
+ * Get common antenna configuration bits
+ */
+static u32 ath9k_hw_4k_get_switch_com(struct ath_hw *ah)
+{
+ struct ath_hw_switch_com_profile *easp;
+ struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
+ struct modal_eep_4k_header *pModal = &eep->modalHeader;
+
+ /* return value from eeprom if there are no profiles */
+ if (!ah->switch_com_profiles)
+ return pModal->antCtrlCommon;
+
+ /* find matching value for profile id */
+ for(easp = ah->switch_com_profiles;easp->id >= 0; easp++) {
+ if ( easp->id == ah->selected_extant_profile )
+ return easp->switch_com_value;
+ }
+
+ /* invalid profile selected, return an error */
+ return 1;
+}
+
+
+/*
* Read EEPROM header info and program the device for correct operation
* given the channel value.
*/
@@ -833,7 +857,7 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
pModal = &eep->modalHeader;
txRxAttenLocal = 23;

- REG_WRITE(ah, AR_PHY_SWITCH_COM, pModal->antCtrlCommon);
+ REG_WRITE(ah, AR_PHY_SWITCH_COM, ath9k_hw_4k_get_switch_com(ah));

/* Single chain for 4K EEPROM*/
ath9k_hw_4k_set_gain(ah, pModal, eep, txRxAttenLocal);
@@ -1112,5 +1136,6 @@ const struct eeprom_ops eep_4k_ops = {
.get_eeprom_rev = ath9k_hw_4k_get_eeprom_rev,
.set_board_values = ath9k_hw_4k_set_board_values,
.set_txpower = ath9k_hw_4k_set_txpower,
- .get_spur_channel = ath9k_hw_4k_get_spur_channel
+ .get_spur_channel = ath9k_hw_4k_get_spur_channel,
+ .get_switch_com = ath9k_hw_4k_get_switch_com
};
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
index 4f5c50a..cf634a7 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
@@ -1062,6 +1062,11 @@ static u16 ath9k_hw_ar9287_get_spur_channel(struct ath_hw *ah,
#undef EEP_MAP9287_SPURCHAN
}

+static u32 ath9k_hw_ar9287_get_switch_com(struct ath_hw *ah)
+{
+ return 0;
+}
+
const struct eeprom_ops eep_ar9287_ops = {
.check_eeprom = ath9k_hw_ar9287_check_eeprom,
.get_eeprom = ath9k_hw_ar9287_get_eeprom,
@@ -1071,5 +1076,6 @@ const struct eeprom_ops eep_ar9287_ops = {
.get_eeprom_rev = ath9k_hw_ar9287_get_eeprom_rev,
.set_board_values = ath9k_hw_ar9287_set_board_values,
.set_txpower = ath9k_hw_ar9287_set_txpower,
- .get_spur_channel = ath9k_hw_ar9287_get_spur_channel
+ .get_spur_channel = ath9k_hw_ar9287_get_spur_channel,
+ .get_switch_com = ath9k_hw_ar9287_get_switch_com
};
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c
index 81e6296..37f60d3 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
@@ -1420,6 +1420,11 @@ static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz)
#undef EEP_DEF_SPURCHAN
}

+static u32 ath9k_hw_def_get_switch_com(struct ath_hw *ah)
+{
+ return 0;
+}
+
const struct eeprom_ops eep_def_ops = {
.check_eeprom = ath9k_hw_def_check_eeprom,
.get_eeprom = ath9k_hw_def_get_eeprom,
@@ -1430,5 +1435,6 @@ const struct eeprom_ops eep_def_ops = {
.set_board_values = ath9k_hw_def_set_board_values,
.set_addac = ath9k_hw_def_set_addac,
.set_txpower = ath9k_hw_def_set_txpower,
- .get_spur_channel = ath9k_hw_def_get_spur_channel
+ .get_spur_channel = ath9k_hw_def_get_spur_channel,
+ .get_switch_com = ath9k_hw_def_get_switch_com
};
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 3cb878c..a33e355 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -545,6 +545,25 @@ struct ath_hw_radar_conf {
bool ext_channel;
};

+
+/**
+ * struct ath_hw_switch_com_profile - external antenna switch settings
+ *
+ * This structure holds a platform_data-supplied alternative value
+ * of AR_PHY_SWITCH_COM.
+ *
+ * @id: unique identifier
+ * @switch_com_value: bit-field to be set in register AR_PHY_SWITCH_COM
+ * @name: profile name
+ * @desc: profile description
+ */
+struct ath_hw_switch_com_profile {
+ int id;
+ u32 switch_com_value;
+ char *name;
+ char *desc;
+};
+
/**
* struct ath_hw_private_ops - callbacks used internally by hardware code
*
@@ -643,6 +662,9 @@ struct ath_hw_ops {
void (*antdiv_comb_conf_set)(struct ath_hw *ah,
struct ath_hw_antcomb_conf *antconf);

+ u32 (*extant_switchcom_get)(struct ath_hw *ah);
+ void (*extant_switchcom_set)(struct ath_hw *ah,
+ u32 switch_com_value);
};

struct ath_nf_limits {
@@ -882,6 +904,12 @@ struct ath_hw {
bool is_clk_25mhz;
int (*get_mac_revision)(void);
int (*external_reset)(void);
+
+ /*
+ * External antenna switch configuration profiles
+ */
+ struct ath_hw_switch_com_profile *switch_com_profiles;
+ u32 selected_extant_profile;
};

struct ath_bus_ops {
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index e046de9..6420b00 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -522,6 +522,43 @@ static void ath9k_init_misc(struct ath_softc *sc)
sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT;
}

+static int ath9k_init_switch_com_profiles(struct ath_softc *sc, struct ath_hw *ah)
+{
+ struct ath9k_platform_data *pdata = sc->dev->platform_data;
+ int i,j;
+
+ /* count valid profiles in platform data */
+ j=0;
+ for(i=0;i<ATH9K_PLAT_MAX_SWITCH_COM_PROFILES;i++) {
+ if ( pdata->switch_com_profiles[i].name )
+ j++;
+ }
+ /*
+ * allocate space for the j profiles found + 1 for termination
+ */
+ if ( j>0 && ! ah->switch_com_profiles ) {
+ ah->switch_com_profiles = kcalloc(j + 1,
+ sizeof(struct ath_hw_switch_com_profile),
+ GFP_KERNEL);
+ if ( ! ah->switch_com_profiles )
+ return -ENOMEM;
+ }
+ /* populate the space from platform_data */
+ j=0;
+ for(i=0;i<ATH9K_PLAT_MAX_SWITCH_COM_PROFILES;i++) {
+ if ( pdata->switch_com_profiles[i].name ) {
+ ah->switch_com_profiles[j].id = j;
+ ah->switch_com_profiles[j].switch_com_value = pdata->switch_com_profiles[i].val;
+ ah->switch_com_profiles[j].name = pdata->switch_com_profiles[i].name;
+ ah->switch_com_profiles[j].desc = pdata->switch_com_profiles[i].desc;
+ j++;
+ }
+ }
+ if ( j>0 )
+ ah->switch_com_profiles[j].id = -1;
+ return 0;
+}
+
static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
const struct ath_bus_ops *bus_ops)
{
@@ -605,11 +642,15 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
if (ret)
goto err_btcoex;

+ ret = ath9k_init_switch_com_profiles(sc, ah);
+ if (ret)
+ goto err_easp;
ath9k_cmn_init_crypto(sc->sc_ah);
ath9k_init_misc(sc);

return 0;

+err_easp:
err_btcoex:
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
if (ATH_TXQ_SETUP(sc, i))
@@ -618,6 +659,8 @@ err_queues:
ath9k_hw_deinit(ah);
err_hw:

+ if ( ah->switch_com_profiles )
+ kfree(ah->switch_com_profiles);
kfree(ah);
sc->sc_ah = NULL;

@@ -733,6 +776,48 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
}

+int ath9k_set_extant_profiles(struct ath_hw *ah, struct ieee80211_hw *hw)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+ int i;
+ /* nothing to do if there are no profiles */
+ if (! ah->switch_com_profiles)
+ return 0;
+
+ ath_dbg(common, ATH_DBG_CONFIG,
+ "Device got extant support.\n");
+ hw->wiphy->flags |= WIPHY_FLAG_HAS_EXTANT_SWITCH;
+
+ /* allocate profile structure in wiphy for all available profiles + 1 */
+ for (i=0; ah->switch_com_profiles[i].id >= 0; i++);
+ hw->wiphy->extant = kcalloc(i+1, sizeof(struct wiphy_extant_profile), GFP_KERNEL);
+ if ( ! hw->wiphy->extant )
+ return -ENOMEM;
+
+ ath_dbg(common, ATH_DBG_CONFIG,
+ "Detected %d antenna configuration profiles:\n", i);
+ /* populate wiphy->exant structure */
+ for (i=0; ah->switch_com_profiles[i].id >= 0; i++) {
+ hw->wiphy->extant[i].id = ah->switch_com_profiles[i].id;
+ hw->wiphy->extant[i].name = ah->switch_com_profiles[i].name;
+ hw->wiphy->extant[i].desc = ah->switch_com_profiles[i].desc;
+ ath_dbg(common, ATH_DBG_CONFIG,
+ "\t%d \"%s\" (%s)\n",
+ hw->wiphy->extant[i].id,
+ hw->wiphy->extant[i].name,
+ hw->wiphy->extant[i].desc);
+ }
+ /* terminate with dummy */
+ hw->wiphy->extant[i].id = -1;
+ return 0;
+}
+
+void ath9k_free_extant_profiles(struct ieee80211_hw *hw)
+{
+ if (hw->wiphy->extant)
+ kfree(hw->wiphy->extant);
+}
+
int ath9k_init_device(u16 devid, struct ath_softc *sc,
const struct ath_bus_ops *bus_ops)
{
@@ -751,6 +836,11 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
common = ath9k_hw_common(ah);
ath9k_set_hw_capab(sc, hw);

+ /* Initialize extant profiles */
+ error = ath9k_set_extant_profiles(ah, hw);
+ if (error != 0)
+ goto error_extant;
+
/* Initialize regulatory */
error = ath_regd_init(&common->regulatory, sc->hw->wiphy,
ath9k_reg_notifier);
@@ -816,6 +906,8 @@ error_rx:
error_tx:
/* Nothing */
error_regd:
+ ath9k_free_extant_profiles(hw);
+error_extant:
ath9k_deinit_softc(sc);
error_init:
return error;
@@ -863,6 +955,7 @@ void ath9k_deinit_device(struct ath_softc *sc)
ieee80211_unregister_hw(hw);
ath_rx_cleanup(sc);
ath_tx_cleanup(sc);
+ ath9k_free_extant_profiles(hw);
ath9k_deinit_softc(sc);
}

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index e43c41c..8f03642 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2484,6 +2484,40 @@ static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
return 0;
}

+static int ath9k_set_extant(struct ieee80211_hw *hw, u32 extant)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ u32 backup_extant = ah->selected_extant_profile;
+ u32 switch_com_value = 0;
+
+ ath_dbg(common, ATH_DBG_CONFIG, "entering set_extant(%d)\n", extant);
+
+ ah->selected_extant_profile = extant;
+ switch_com_value = ah->eep_ops->get_switch_com(ah);
+ if (switch_com_value & 0xf)
+ goto invalid_profile;
+
+ ath_dbg(common, ATH_DBG_CONFIG, "switch_com: %08x\n", switch_com_value);
+ return 0;
+
+invalid_profile:
+ ath_dbg(common, ATH_DBG_CONFIG, "invalid profile (%08x)\n", switch_com_value);
+ ah->selected_extant_profile = backup_extant;
+ return -EINVAL;
+}
+
+static int ath9k_get_extant(struct ieee80211_hw *hw, u32 *extant)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_hw *ah = sc->sc_ah;
+ if (!ah->switch_com_profiles)
+ return -EINVAL;
+ *extant = ah->selected_extant_profile;
+ return 0;
+}
+
struct ieee80211_ops ath9k_ops = {
.tx = ath9k_tx,
.start = ath9k_start,
@@ -2512,4 +2546,6 @@ struct ieee80211_ops ath9k_ops = {
.get_stats = ath9k_get_stats,
.set_antenna = ath9k_set_antenna,
.get_antenna = ath9k_get_antenna,
+ .set_extant = ath9k_set_extant,
+ .get_extant = ath9k_get_extant,
};
diff --git a/include/linux/ath9k_platform.h b/include/linux/ath9k_platform.h
index 6e3f54f..1bfe926 100644
--- a/include/linux/ath9k_platform.h
+++ b/include/linux/ath9k_platform.h
@@ -20,6 +20,7 @@
#define _LINUX_ATH9K_PLATFORM_H

#define ATH9K_PLAT_EEP_MAX_WORDS 2048
+#define ATH9K_PLAT_MAX_SWITCH_COM_PROFILES 4

struct ath9k_platform_data {
u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS];
@@ -32,6 +33,11 @@ struct ath9k_platform_data {
bool is_clk_25mhz;
int (*get_mac_revision)(void);
int (*external_reset)(void);
+ struct {
+ u32 val;
+ char *name;
+ char *desc;
+ } switch_com_profiles[ATH9K_PLAT_MAX_SWITCH_COM_PROFILES];
};

#endif /* _LINUX_ATH9K_PLATFORM_H */
--
1.7.4.1


Attachments:
(No filename) (14.97 kB)
signature.asc (836.00 B)
Digital signature
Download all attachments