summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lin <dlin@marvell.com>2016-08-10 09:13:06 +0800
committerDavid Lin <dlin@marvell.com>2016-08-10 09:13:06 +0800
commit5857f88d0a5ed792eccf7af593b41620cf3c8035 (patch)
tree8744f89419e0a90d914b32cf09473029a84b2844
parentb7dfe8abebf111745de6de666a2c57a2e174dd6a (diff)
Added support for the 7.2.9.26 firmware API.
Signed-off-by: David Lin <dlin@marvell.com>
-rw-r--r--debugfs.c50
-rw-r--r--dev.h16
-rw-r--r--[-rwxr-xr-x]fwcmd.c94
-rw-r--r--fwcmd.h9
-rw-r--r--hostcmd.h30
-rw-r--r--main.c72
6 files changed, 265 insertions, 6 deletions
diff --git a/debugfs.c b/debugfs.c
index 71ee4e0..1ded461 100644
--- a/debugfs.c
+++ b/debugfs.c
@@ -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);
diff --git a/dev.h b/dev.h
index 2c2f3ab..4c32297 100644
--- a/dev.h
+++ b/dev.h
@@ -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;
diff --git a/fwcmd.c b/fwcmd.c
index 524ddfa..9c3ccf9 100755..100644
--- a/fwcmd.c
+++ b/fwcmd.c
@@ -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)
{
diff --git a/fwcmd.h b/fwcmd.h
index fdb9fe8..74a21d3 100644
--- a/fwcmd.h
+++ b/fwcmd.h
@@ -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);
diff --git a/hostcmd.h b/hostcmd.h
index c209114..b163a94 100644
--- a/hostcmd.h
+++ b/hostcmd.h
@@ -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;
diff --git a/main.c b/main.c
index 71701a2..4ece819 100644
--- a/main.c
+++ b/main.c
@@ -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],
+ &region_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],
+ &region_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;