diff options
author | David Lin <dlin@marvell.com> | 2016-08-10 09:13:06 +0800 |
---|---|---|
committer | David Lin <dlin@marvell.com> | 2016-08-10 09:13:06 +0800 |
commit | 5857f88d0a5ed792eccf7af593b41620cf3c8035 (patch) | |
tree | 8744f89419e0a90d914b32cf09473029a84b2844 | |
parent | b7dfe8abebf111745de6de666a2c57a2e174dd6a (diff) |
Added support for the 7.2.9.26 firmware API.
Signed-off-by: David Lin <dlin@marvell.com>
-rw-r--r-- | debugfs.c | 50 | ||||
-rw-r--r-- | dev.h | 16 | ||||
-rw-r--r--[-rwxr-xr-x] | fwcmd.c | 94 | ||||
-rw-r--r-- | fwcmd.h | 9 | ||||
-rw-r--r-- | hostcmd.h | 30 | ||||
-rw-r--r-- | main.c | 72 |
6 files changed, 265 insertions, 6 deletions
@@ -99,6 +99,11 @@ static ssize_t mwl_debugfs_info_read(struct file *file, char __user *ubuf, len += scnprintf(p + len, size - len, "firmware version: 0x%08x\n", priv->hw_data.fw_release_num); len += scnprintf(p + len, size - len, + "power table loaded from dts: %s\n", + priv->forbidden_setting ? "no" : "yes"); + len += scnprintf(p + len, size - len, "firmware region code: 0x%x\n", + priv->fw_region_code); + len += scnprintf(p + len, size - len, "mac address: %pM\n", priv->hw_data.mac_addr); len += scnprintf(p + len, size - len, "2g: %s\n", priv->disable_2g ? "disable" : "enable"); @@ -328,6 +333,49 @@ static ssize_t mwl_debugfs_ampdu_read(struct file *file, char __user *ubuf, return ret; } +static ssize_t mwl_debugfs_device_pwrtbl_read(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwl_priv *priv = (struct mwl_priv *)file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *)page; + int len = 0, size = PAGE_SIZE; + int i, j; + ssize_t ret; + + if (!p) + return -ENOMEM; + + len += scnprintf(p + len, size - len, "\n"); + len += scnprintf(p + len, size - len, + "power table loaded from dts: %s\n", + priv->forbidden_setting ? "no" : "yes"); + len += scnprintf(p + len, size - len, "firmware region code: 0x%x\n", + priv->fw_region_code); + len += scnprintf(p + len, size - len, "number of channel: %d\n", + priv->number_of_channels); + for (i = 0; i < priv->number_of_channels; i++) { + len += scnprintf(p + len, size - len, "%3d ", + priv->device_pwr_tbl[i].channel); + for (j = 0; j < SYSADPT_TX_POWER_LEVEL_TOTAL; j++) + len += scnprintf(p + len, size - len, "%3d ", + priv->device_pwr_tbl[i].tx_pwr[j]); + len += scnprintf(p + len, size - len, "%3d ", + priv->device_pwr_tbl[i].dfs_capable); + len += scnprintf(p + len, size - len, "%3d ", + priv->device_pwr_tbl[i].ax_ant); + len += scnprintf(p + len, size - len, "%3d\n", + priv->device_pwr_tbl[i].cdd); + } + len += scnprintf(p + len, size - len, "\n"); + + ret = simple_read_from_buffer(ubuf, count, ppos, p, len); + free_page(page); + + return ret; +} + static ssize_t mwl_debugfs_tx_desc_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) @@ -746,6 +794,7 @@ MWLWIFI_DEBUGFS_FILE_READ_OPS(info); MWLWIFI_DEBUGFS_FILE_READ_OPS(vif); MWLWIFI_DEBUGFS_FILE_READ_OPS(sta); MWLWIFI_DEBUGFS_FILE_READ_OPS(ampdu); +MWLWIFI_DEBUGFS_FILE_READ_OPS(device_pwrtbl); MWLWIFI_DEBUGFS_FILE_OPS(tx_desc); MWLWIFI_DEBUGFS_FILE_OPS(dfs_channel); MWLWIFI_DEBUGFS_FILE_OPS(dfs_radar); @@ -767,6 +816,7 @@ void mwl_debugfs_init(struct ieee80211_hw *hw) MWLWIFI_DEBUGFS_ADD_FILE(vif); MWLWIFI_DEBUGFS_ADD_FILE(sta); MWLWIFI_DEBUGFS_ADD_FILE(ampdu); + MWLWIFI_DEBUGFS_ADD_FILE(device_pwrtbl); MWLWIFI_DEBUGFS_ADD_FILE(tx_desc); MWLWIFI_DEBUGFS_ADD_FILE(dfs_channel); MWLWIFI_DEBUGFS_ADD_FILE(dfs_radar); @@ -147,6 +147,14 @@ struct mwl_chip_info { int antenna_rx; }; +struct mwl_device_pwr_tbl { + u8 channel; + u8 tx_pwr[SYSADPT_TX_POWER_LEVEL_TOTAL]; + u8 dfs_capable; + u8 ax_ant; + u8 cdd; +}; + struct mwl_tx_pwr_tbl { u8 channel; u8 setcap; @@ -278,6 +286,13 @@ struct mwl_ampdu_stream { struct mwl_priv { struct ieee80211_hw *hw; struct firmware *fw_ucode; + bool fw_device_pwrtbl; + bool forbidden_setting; + bool regulatory_set; + u32 fw_region_code; + char fw_alpha2[2]; + u8 number_of_channels; + struct mwl_device_pwr_tbl device_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS]; int chip_type; struct device_node *dt_node; @@ -293,7 +308,6 @@ struct mwl_priv { u8 powinited; u16 max_tx_pow[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* max tx power (dBm) */ u16 target_powers[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* target powers */ - u8 cal_tbl[200]; struct pci_dev *pdev; struct device *dev; @@ -101,6 +101,8 @@ static char *mwl_fwcmd_get_cmd_string(unsigned short cmd) { HOSTCMD_CMD_FW_FLUSH_TIMER, "FwFlushTimer" }, { HOSTCMD_CMD_SET_CDD, "SetCDD" }, { HOSTCMD_CMD_GET_TEMP, "GetTemp" }, + { HOSTCMD_CMD_GET_FW_REGION_CODE, "GetFwRegionCode" }, + { HOSTCMD_CMD_GET_DEVICE_PWR_TBL, "GetDevicePwrTbl" }, { HOSTCMD_CMD_QUIET_MODE, "QuietMode" }, }; @@ -1012,7 +1014,10 @@ int mwl_fwcmd_max_tx_power(struct ieee80211_hw *hw, u16 band = 0, width = 0, sub_ch = 0; u16 maxtxpow[SYSADPT_TX_POWER_LEVEL_TOTAL]; int i, tmp; - int rc; + int rc = 0; + + if (priv->forbidden_setting) + return rc; switch (fraction) { case 0: @@ -1098,7 +1103,10 @@ int mwl_fwcmd_tx_power(struct ieee80211_hw *hw, u16 txpow[SYSADPT_TX_POWER_LEVEL_TOTAL]; int index, found = 0; int i, tmp; - int rc; + int rc = 0; + + if (priv->forbidden_setting) + return rc; switch (fraction) { case 0: @@ -1353,6 +1361,11 @@ int mwl_fwcmd_set_rf_channel(struct ieee80211_hw *hw, return -EIO; } + if (pcmd->cmd_hdr.result != 0) { + mutex_unlock(&priv->fwcmd_mutex); + return -EINVAL; + } + mutex_unlock(&priv->fwcmd_mutex); return 0; @@ -2686,6 +2699,83 @@ int mwl_fwcmd_get_temp(struct ieee80211_hw *hw, u32 *temp) return 0; } +int mwl_fwcmd_get_fw_region_code(struct ieee80211_hw *hw, + u32 *fw_region_code) +{ + struct mwl_priv *priv = hw->priv; + struct hostcmd_cmd_get_fw_region_code *pcmd; + int status; + + pcmd = (struct hostcmd_cmd_get_fw_region_code *)&priv->pcmd_buf[0]; + + mutex_lock(&priv->fwcmd_mutex); + + memset(pcmd, 0x00, sizeof(*pcmd)); + pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_FW_REGION_CODE); + pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); + + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_FW_REGION_CODE)) { + mutex_unlock(&priv->fwcmd_mutex); + wiphy_err(hw->wiphy, "failed execution\n"); + return -EIO; + } + + if (pcmd->cmd_hdr.result != 0) { + mutex_unlock(&priv->fwcmd_mutex); + return -EINVAL; + } + + status = le32_to_cpu(pcmd->status); + + if (!status) + *fw_region_code = le32_to_cpu(pcmd->fw_region_code); + + mutex_unlock(&priv->fwcmd_mutex); + + return 0; +} + +int mwl_fwcmd_get_device_pwr_tbl(struct ieee80211_hw *hw, + struct mwl_device_pwr_tbl *device_ch_pwrtbl, + u8 *region_code, + u8 *number_of_channels, + u32 channel_index) +{ + struct mwl_priv *priv = hw->priv; + struct hostcmd_cmd_get_device_pwr_tbl *pcmd; + int status; + + pcmd = (struct hostcmd_cmd_get_device_pwr_tbl *)&priv->pcmd_buf[0]; + + mutex_lock(&priv->fwcmd_mutex); + + memset(pcmd, 0x00, sizeof(*pcmd)); + pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_DEVICE_PWR_TBL); + pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); + pcmd->status = cpu_to_le16(HOSTCMD_CMD_GET_DEVICE_PWR_TBL); + pcmd->current_channel_index = cpu_to_le32(channel_index); + + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_DEVICE_PWR_TBL)) { + mutex_unlock(&priv->fwcmd_mutex); + wiphy_err(hw->wiphy, "failed execution\n"); + return -EIO; + } + + device_ch_pwrtbl->channel = pcmd->channel_pwr_tbl.channel; + memcpy(device_ch_pwrtbl->tx_pwr, pcmd->channel_pwr_tbl.tx_pwr, + SYSADPT_TX_POWER_LEVEL_TOTAL); + device_ch_pwrtbl->dfs_capable = pcmd->channel_pwr_tbl.dfs_capable; + device_ch_pwrtbl->ax_ant = pcmd->channel_pwr_tbl.ax_ant; + device_ch_pwrtbl->cdd = pcmd->channel_pwr_tbl.cdd; + *region_code = pcmd->region_code; + *number_of_channels = pcmd->number_of_channels; + status = le16_to_cpu(pcmd->status); + + mutex_unlock(&priv->fwcmd_mutex); + + return status; +} + int mwl_fwcmd_quiet_mode(struct ieee80211_hw *hw, bool enable, u32 period, u32 duration, u32 next_offset) { @@ -206,6 +206,15 @@ int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw); int mwl_fwcmd_get_temp(struct ieee80211_hw *hw, u32 *temp); +int mwl_fwcmd_get_fw_region_code(struct ieee80211_hw *hw, + u32 *fw_region_code); + +int mwl_fwcmd_get_device_pwr_tbl(struct ieee80211_hw *hw, + struct mwl_device_pwr_tbl *device_ch_pwrtbl, + u8 *region_code, + u8 *number_of_channels, + u32 channel_index); + int mwl_fwcmd_quiet_mode(struct ieee80211_hw *hw, bool enable, u32 period, u32 duration, u32 next_offset); @@ -60,6 +60,8 @@ #define HOSTCMD_CMD_FW_FLUSH_TIMER 0x1148 #define HOSTCMD_CMD_SET_CDD 0x1150 #define HOSTCMD_CMD_GET_TEMP 0x1159 +#define HOSTCMD_CMD_GET_FW_REGION_CODE 0x116A +#define HOSTCMD_CMD_GET_DEVICE_PWR_TBL 0x116B #define HOSTCMD_CMD_QUIET_MODE 0x1201 /* Define general result code for each command */ @@ -870,6 +872,34 @@ struct hostcmd_cmd_get_temp { __le32 raw_data; } __packed; +/* HOSTCMD_CMD_GET_FW_REGION_CODE */ +struct hostcmd_cmd_get_fw_region_code { + struct hostcmd_header cmd_hdr; + __le32 status; /* 0 = Found, 1 = Error */ + __le32 fw_region_code; +} __packed; + +/* HOSTCMD_CMD_GET_DEVICE_PWR_TBL */ +#define HAL_TRPC_ID_MAX 16 + +struct channel_power_tbl { + u8 channel; + u8 tx_pwr[HAL_TRPC_ID_MAX]; + u8 dfs_capable; + u8 ax_ant; + u8 cdd; +} __packed; + +struct hostcmd_cmd_get_device_pwr_tbl { + struct hostcmd_header cmd_hdr; + __le16 status; /* 0 = Found, 1 = Error */ + u8 region_code; + u8 number_of_channels; + __le32 current_channel_index; + /* Only for 1 channel, so, 1 channel at a time */ + struct channel_power_tbl channel_pwr_tbl; +} __packed; + /* HOSTCMD_CMD_QUIET_MODE */ struct hostcmd_cmd_quiet_mode { struct hostcmd_header cmd_hdr; @@ -146,6 +146,23 @@ static const struct ieee80211_iface_combination ap_if_comb = { BIT(NL80211_CHAN_WIDTH_80), }; +struct region_code_mapping { + const char *alpha2; + u32 region_code; +}; + +static const struct region_code_mapping regmap[] = { + {"US", 0x10}, /* US FCC */ + {"CA", 0x20}, /* Canada */ + {"EU", 0x30}, /* ETSI */ + {"ES", 0x31}, /* Spain */ + {"FR", 0x32}, /* France */ + {"JP", 0x40}, /* Japan */ + {"TW", 0x80}, /* Taiwan */ + {"AU", 0x81}, /* Australia */ + {"CN", 0x90}, /* China (Asia) */ +}; + static int mwl_alloc_pci_resource(struct mwl_priv *priv) { struct pci_dev *pdev = priv->pdev; @@ -253,6 +270,17 @@ static void mwl_reg_notifier(struct wiphy *wiphy, hw = (struct ieee80211_hw *)wiphy_priv(wiphy); priv = hw->priv; + if (priv->forbidden_setting) { + if (!priv->regulatory_set) { + regulatory_hint(wiphy, priv->fw_alpha2); + priv->regulatory_set = true; + } else { + if (memcmp(priv->fw_alpha2, request->alpha2, 2)) + regulatory_hint(wiphy, priv->fw_alpha2); + } + return; + } + priv->dfs_region = request->dfs_region; #ifdef CONFIG_OF @@ -492,6 +520,37 @@ static void mwl_set_caps(struct mwl_priv *priv) } } +static void mwl_regd_init(struct mwl_priv *priv) +{ + u8 region_code; + int i; + + /* hook regulatory domain change notification */ + priv->hw->wiphy->reg_notifier = mwl_reg_notifier; + + if (mwl_fwcmd_get_device_pwr_tbl(priv->hw, + &priv->device_pwr_tbl[0], + ®ion_code, + &priv->number_of_channels, + 0)) + return; + + priv->forbidden_setting = true; + + for (i = 0; i < priv->number_of_channels; i++) + mwl_fwcmd_get_device_pwr_tbl(priv->hw, + &priv->device_pwr_tbl[i], + ®ion_code, + &priv->number_of_channels, + i); + + for (i = 0; i < ARRAY_SIZE(regmap); i++) + if (regmap[i].region_code == priv->fw_region_code) { + memcpy(priv->fw_alpha2, regmap[i].alpha2, 2); + break; + } +} + static void timer_routine(unsigned long data) { struct mwl_priv *priv = (struct mwl_priv *)data; @@ -655,6 +714,13 @@ static int mwl_wl_init(struct mwl_priv *priv) wiphy_info(hw->wiphy, "firmware version: 0x%x\n", priv->hw_data.fw_release_num); + if (!mwl_fwcmd_get_fw_region_code(hw, &priv->fw_region_code)) { + priv->fw_device_pwrtbl = true; + mwl_regd_init(priv); + wiphy_info(hw->wiphy, + "firmware region code: %x\n", priv->fw_region_code); + } + mwl_fwcmd_radio_disable(hw); mwl_fwcmd_rf_antenna(hw, WL_ANTENNATYPE_TX, priv->antenna_tx); @@ -774,9 +840,6 @@ static int mwl_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_pci_disable_device; } - /* hook regulatory domain change notification */ - hw->wiphy->reg_notifier = mwl_reg_notifier; - pci_set_drvdata(pdev, hw); priv = hw->priv; @@ -784,6 +847,9 @@ static int mwl_probe(struct pci_dev *pdev, const struct pci_device_id *id) priv->pdev = pdev; priv->dev = &pdev->dev; priv->chip_type = id->driver_data; + priv->fw_device_pwrtbl = false; + priv->forbidden_setting = false; + priv->regulatory_set = false; priv->disable_2g = false; priv->disable_5g = false; priv->antenna_tx = mwl_chip_tbl[priv->chip_type].antenna_tx; |