This adds support for controlling AR_PHY_SWITCH_COM of the AR9285 by using antenna configuration profiles to ath9k. Signed-off-by: Daniel Golle <dgolle@xxxxxxxxx> --- 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
Attachment:
signature.asc
Description: Digital signature