aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/mwl8k.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/mwl8k.c')
-rw-r--r--drivers/net/wireless/mwl8k.c197
1 files changed, 191 insertions, 6 deletions
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index 4987c3f942ce..3c0a0a86ba12 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -81,6 +81,9 @@ MODULE_PARM_DESC(ap_mode_default,
81 */ 81 */
82 82
83#define MWL8K_HW_TIMER_REGISTER 0x0000a600 83#define MWL8K_HW_TIMER_REGISTER 0x0000a600
84#define BBU_RXRDY_CNT_REG 0x0000a860
85#define NOK_CCA_CNT_REG 0x0000a6a0
86#define BBU_AVG_NOISE_VAL 0x67
84 87
85#define MWL8K_A2H_EVENTS (MWL8K_A2H_INT_DUMMY | \ 88#define MWL8K_A2H_EVENTS (MWL8K_A2H_INT_DUMMY | \
86 MWL8K_A2H_INT_CHNL_SWITCHED | \ 89 MWL8K_A2H_INT_CHNL_SWITCHED | \
@@ -112,6 +115,8 @@ MODULE_PARM_DESC(ap_mode_default,
112 */ 115 */
113#define MWL8K_NUM_AMPDU_STREAMS (TOTAL_HW_TX_QUEUES - 1) 116#define MWL8K_NUM_AMPDU_STREAMS (TOTAL_HW_TX_QUEUES - 1)
114 117
118#define MWL8K_NUM_CHANS 18
119
115struct rxd_ops { 120struct rxd_ops {
116 int rxd_size; 121 int rxd_size;
117 void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr); 122 void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr);
@@ -289,6 +294,12 @@ struct mwl8k_priv {
289 294
290 /* bitmap of running BSSes */ 295 /* bitmap of running BSSes */
291 u32 running_bsses; 296 u32 running_bsses;
297
298 /* ACS related */
299 bool sw_scan_start;
300 struct ieee80211_channel *acs_chan;
301 unsigned long channel_time;
302 struct survey_info survey[MWL8K_NUM_CHANS];
292}; 303};
293 304
294#define MAX_WEP_KEY_LEN 13 305#define MAX_WEP_KEY_LEN 13
@@ -396,6 +407,7 @@ static const struct ieee80211_rate mwl8k_rates_50[] = {
396#define MWL8K_CMD_SET_HW_SPEC 0x0004 407#define MWL8K_CMD_SET_HW_SPEC 0x0004
397#define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010 408#define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010
398#define MWL8K_CMD_GET_STAT 0x0014 409#define MWL8K_CMD_GET_STAT 0x0014
410#define MWL8K_CMD_BBP_REG_ACCESS 0x001a
399#define MWL8K_CMD_RADIO_CONTROL 0x001c 411#define MWL8K_CMD_RADIO_CONTROL 0x001c
400#define MWL8K_CMD_RF_TX_POWER 0x001e 412#define MWL8K_CMD_RF_TX_POWER 0x001e
401#define MWL8K_CMD_TX_POWER 0x001f 413#define MWL8K_CMD_TX_POWER 0x001f
@@ -2987,6 +2999,47 @@ static int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw)
2987} 2999}
2988 3000
2989/* 3001/*
3002 * CMD_BBP_REG_ACCESS.
3003 */
3004struct mwl8k_cmd_bbp_reg_access {
3005 struct mwl8k_cmd_pkt header;
3006 __le16 action;
3007 __le16 offset;
3008 u8 value;
3009 u8 rsrv[3];
3010} __packed;
3011
3012static int
3013mwl8k_cmd_bbp_reg_access(struct ieee80211_hw *hw,
3014 u16 action,
3015 u16 offset,
3016 u8 *value)
3017{
3018 struct mwl8k_cmd_bbp_reg_access *cmd;
3019 int rc;
3020
3021 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3022 if (cmd == NULL)
3023 return -ENOMEM;
3024
3025 cmd->header.code = cpu_to_le16(MWL8K_CMD_BBP_REG_ACCESS);
3026 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3027 cmd->action = cpu_to_le16(action);
3028 cmd->offset = cpu_to_le16(offset);
3029
3030 rc = mwl8k_post_cmd(hw, &cmd->header);
3031
3032 if (!rc)
3033 *value = cmd->value;
3034 else
3035 *value = 0;
3036
3037 kfree(cmd);
3038
3039 return rc;
3040}
3041
3042/*
2990 * CMD_SET_POST_SCAN. 3043 * CMD_SET_POST_SCAN.
2991 */ 3044 */
2992struct mwl8k_cmd_set_post_scan { 3045struct mwl8k_cmd_set_post_scan {
@@ -3016,6 +3069,64 @@ mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac)
3016 return rc; 3069 return rc;
3017} 3070}
3018 3071
3072static int freq_to_idx(struct mwl8k_priv *priv, int freq)
3073{
3074 struct ieee80211_supported_band *sband;
3075 int band, ch, idx = 0;
3076
3077 for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
3078 sband = priv->hw->wiphy->bands[band];
3079 if (!sband)
3080 continue;
3081
3082 for (ch = 0; ch < sband->n_channels; ch++, idx++)
3083 if (sband->channels[ch].center_freq == freq)
3084 goto exit;
3085 }
3086
3087exit:
3088 return idx;
3089}
3090
3091static void mwl8k_update_survey(struct mwl8k_priv *priv,
3092 struct ieee80211_channel *channel)
3093{
3094 u32 cca_cnt, rx_rdy;
3095 s8 nf = 0, idx;
3096 struct survey_info *survey;
3097
3098 idx = freq_to_idx(priv, priv->acs_chan->center_freq);
3099 if (idx >= MWL8K_NUM_CHANS) {
3100 wiphy_err(priv->hw->wiphy, "Failed to update survey\n");
3101 return;
3102 }
3103
3104 survey = &priv->survey[idx];
3105
3106 cca_cnt = ioread32(priv->regs + NOK_CCA_CNT_REG);
3107 cca_cnt /= 1000; /* uSecs to mSecs */
3108 survey->channel_time_busy = (u64) cca_cnt;
3109
3110 rx_rdy = ioread32(priv->regs + BBU_RXRDY_CNT_REG);
3111 rx_rdy /= 1000; /* uSecs to mSecs */
3112 survey->channel_time_rx = (u64) rx_rdy;
3113
3114 priv->channel_time = jiffies - priv->channel_time;
3115 survey->channel_time = jiffies_to_msecs(priv->channel_time);
3116
3117 survey->channel = channel;
3118
3119 mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &nf);
3120
3121 /* Make sure sign is negative else ACS at hostapd fails */
3122 survey->noise = nf * -1;
3123
3124 survey->filled = SURVEY_INFO_NOISE_DBM |
3125 SURVEY_INFO_CHANNEL_TIME |
3126 SURVEY_INFO_CHANNEL_TIME_BUSY |
3127 SURVEY_INFO_CHANNEL_TIME_RX;
3128}
3129
3019/* 3130/*
3020 * CMD_SET_RF_CHANNEL. 3131 * CMD_SET_RF_CHANNEL.
3021 */ 3132 */
@@ -3033,6 +3144,7 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,
3033 enum nl80211_channel_type channel_type = 3144 enum nl80211_channel_type channel_type =
3034 cfg80211_get_chandef_type(&conf->chandef); 3145 cfg80211_get_chandef_type(&conf->chandef);
3035 struct mwl8k_cmd_set_rf_channel *cmd; 3146 struct mwl8k_cmd_set_rf_channel *cmd;
3147 struct mwl8k_priv *priv = hw->priv;
3036 int rc; 3148 int rc;
3037 3149
3038 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 3150 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -3049,13 +3161,29 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,
3049 else if (channel->band == IEEE80211_BAND_5GHZ) 3161 else if (channel->band == IEEE80211_BAND_5GHZ)
3050 cmd->channel_flags |= cpu_to_le32(0x00000004); 3162 cmd->channel_flags |= cpu_to_le32(0x00000004);
3051 3163
3052 if (channel_type == NL80211_CHAN_NO_HT || 3164 if (!priv->sw_scan_start) {
3053 channel_type == NL80211_CHAN_HT20) 3165 if (channel_type == NL80211_CHAN_NO_HT ||
3166 channel_type == NL80211_CHAN_HT20)
3167 cmd->channel_flags |= cpu_to_le32(0x00000080);
3168 else if (channel_type == NL80211_CHAN_HT40MINUS)
3169 cmd->channel_flags |= cpu_to_le32(0x000001900);
3170 else if (channel_type == NL80211_CHAN_HT40PLUS)
3171 cmd->channel_flags |= cpu_to_le32(0x000000900);
3172 } else {
3054 cmd->channel_flags |= cpu_to_le32(0x00000080); 3173 cmd->channel_flags |= cpu_to_le32(0x00000080);
3055 else if (channel_type == NL80211_CHAN_HT40MINUS) 3174 }
3056 cmd->channel_flags |= cpu_to_le32(0x000001900); 3175
3057 else if (channel_type == NL80211_CHAN_HT40PLUS) 3176 if (priv->sw_scan_start) {
3058 cmd->channel_flags |= cpu_to_le32(0x000000900); 3177 /* Store current channel stats
3178 * before switching to newer one.
3179 * This will be processed only for AP fw.
3180 */
3181 if (priv->channel_time != 0)
3182 mwl8k_update_survey(priv, priv->acs_chan);
3183
3184 priv->channel_time = jiffies;
3185 priv->acs_chan = channel;
3186 }
3059 3187
3060 rc = mwl8k_post_cmd(hw, &cmd->header); 3188 rc = mwl8k_post_cmd(hw, &cmd->header);
3061 kfree(cmd); 3189 kfree(cmd);
@@ -5263,6 +5391,27 @@ static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx,
5263{ 5391{
5264 struct mwl8k_priv *priv = hw->priv; 5392 struct mwl8k_priv *priv = hw->priv;
5265 struct ieee80211_conf *conf = &hw->conf; 5393 struct ieee80211_conf *conf = &hw->conf;
5394 struct ieee80211_supported_band *sband;
5395
5396 if (priv->ap_fw) {
5397 sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ];
5398
5399 if (sband && idx >= sband->n_channels) {
5400 idx -= sband->n_channels;
5401 sband = NULL;
5402 }
5403
5404 if (!sband)
5405 sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ];
5406
5407 if (!sband || idx >= sband->n_channels)
5408 return -ENOENT;
5409
5410 memcpy(survey, &priv->survey[idx], sizeof(*survey));
5411 survey->channel = &sband->channels[idx];
5412
5413 return 0;
5414 }
5266 5415
5267 if (idx != 0) 5416 if (idx != 0)
5268 return -ENOENT; 5417 return -ENOENT;
@@ -5406,6 +5555,40 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
5406 return rc; 5555 return rc;
5407} 5556}
5408 5557
5558static void mwl8k_sw_scan_start(struct ieee80211_hw *hw)
5559{
5560 struct mwl8k_priv *priv = hw->priv;
5561 u8 tmp;
5562
5563 if (!priv->ap_fw)
5564 return;
5565
5566 /* clear all stats */
5567 priv->channel_time = 0;
5568 ioread32(priv->regs + BBU_RXRDY_CNT_REG);
5569 ioread32(priv->regs + NOK_CCA_CNT_REG);
5570 mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp);
5571
5572 priv->sw_scan_start = true;
5573}
5574
5575static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw)
5576{
5577 struct mwl8k_priv *priv = hw->priv;
5578 u8 tmp;
5579
5580 if (!priv->ap_fw)
5581 return;
5582
5583 priv->sw_scan_start = false;
5584
5585 /* clear all stats */
5586 priv->channel_time = 0;
5587 ioread32(priv->regs + BBU_RXRDY_CNT_REG);
5588 ioread32(priv->regs + NOK_CCA_CNT_REG);
5589 mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp);
5590}
5591
5409static const struct ieee80211_ops mwl8k_ops = { 5592static const struct ieee80211_ops mwl8k_ops = {
5410 .tx = mwl8k_tx, 5593 .tx = mwl8k_tx,
5411 .start = mwl8k_start, 5594 .start = mwl8k_start,
@@ -5424,6 +5607,8 @@ static const struct ieee80211_ops mwl8k_ops = {
5424 .get_stats = mwl8k_get_stats, 5607 .get_stats = mwl8k_get_stats,
5425 .get_survey = mwl8k_get_survey, 5608 .get_survey = mwl8k_get_survey,
5426 .ampdu_action = mwl8k_ampdu_action, 5609 .ampdu_action = mwl8k_ampdu_action,
5610 .sw_scan_start = mwl8k_sw_scan_start,
5611 .sw_scan_complete = mwl8k_sw_scan_complete,
5427}; 5612};
5428 5613
5429static void mwl8k_finalize_join_worker(struct work_struct *work) 5614static void mwl8k_finalize_join_worker(struct work_struct *work)