Some Intel Wi-Fi hardware has 80211N disabled by firmware.
Patch EEPROM shadow copy to enable it on the 6xxx series.
This code based on http://forum.ixbt.com/topic.cgi?id=14:51214:326#326
and https://github.com/0x90/iwleeprom
Signed-off-by: Sergei A. Trusov <sergei.a.trusov@yandex.ru>
---
drivers/net/wireless/intel/iwlwifi/cfg/6000.c | 30 ++--
.../net/wireless/intel/iwlwifi/iwl-eeprom-read.c | 169 +++++++++++++++++++++
2 files changed, 189 insertions(+), 10 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/6000.c b/drivers/net/wireless/intel/iwlwifi/cfg/6000.c
index 39335b7b0c16..2e37877d5643 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/6000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/6000.c
@@ -144,13 +144,15 @@ const struct iwl_cfg iwl6005_2agn_cfg = {
};
const struct iwl_cfg iwl6005_2abg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6205 ABG",
+ .name = "Intel(R) Centrino(R) Advanced-N 6205 ABG+N",
IWL_DEVICE_6005,
+ .ht_params = &iwl6000_ht_params,
};
const struct iwl_cfg iwl6005_2bg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6205 BG",
+ .name = "Intel(R) Centrino(R) Advanced-N 6205 BG+N",
IWL_DEVICE_6005,
+ .ht_params = &iwl6000_ht_params,
};
const struct iwl_cfg iwl6005_2agn_sff_cfg = {
@@ -198,8 +200,9 @@ const struct iwl_cfg iwl6030_2agn_cfg = {
};
const struct iwl_cfg iwl6030_2abg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6230 ABG",
+ .name = "Intel(R) Centrino(R) Advanced-N 6230 ABG+N",
IWL_DEVICE_6030,
+ .ht_params = &iwl6000_ht_params,
};
const struct iwl_cfg iwl6030_2bgn_cfg = {
@@ -209,8 +212,9 @@ const struct iwl_cfg iwl6030_2bgn_cfg = {
};
const struct iwl_cfg iwl6030_2bg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6230 BG",
+ .name = "Intel(R) Centrino(R) Advanced-N 6230 BG+N",
IWL_DEVICE_6030,
+ .ht_params = &iwl6000_ht_params,
};
#define IWL_DEVICE_6035 \
@@ -246,8 +250,9 @@ const struct iwl_cfg iwl1030_bgn_cfg = {
};
const struct iwl_cfg iwl1030_bg_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 1030 BG",
+ .name = "Intel(R) Centrino(R) Wireless-N 1030 BG+N",
IWL_DEVICE_6030,
+ .ht_params = &iwl6000_ht_params,
};
const struct iwl_cfg iwl130_bgn_cfg = {
@@ -258,8 +263,9 @@ const struct iwl_cfg iwl130_bgn_cfg = {
};
const struct iwl_cfg iwl130_bg_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N 130 BG",
+ .name = "Intel(R) Centrino(R) Wireless-N 130 BG+N",
IWL_DEVICE_6030,
+ .ht_params = &iwl6000_ht_params,
.rx_with_siso_diversity = true,
};
@@ -289,13 +295,15 @@ const struct iwl_cfg iwl6000i_2agn_cfg = {
};
const struct iwl_cfg iwl6000i_2abg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6200 ABG",
+ .name = "Intel(R) Centrino(R) Advanced-N 6200 ABG+N",
IWL_DEVICE_6000i,
+ .ht_params = &iwl6000_ht_params,
};
const struct iwl_cfg iwl6000i_2bg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N 6200 BG",
+ .name = "Intel(R) Centrino(R) Advanced-N 6200 BG+N",
IWL_DEVICE_6000i,
+ .ht_params = &iwl6000_ht_params,
};
#define IWL_DEVICE_6050 \
@@ -322,8 +330,9 @@ const struct iwl_cfg iwl6050_2agn_cfg = {
};
const struct iwl_cfg iwl6050_2abg_cfg = {
- .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG",
+ .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG+N",
IWL_DEVICE_6050,
+ .ht_params = &iwl6000_ht_params,
};
#define IWL_DEVICE_6150 \
@@ -348,8 +357,9 @@ const struct iwl_cfg iwl6150_bgn_cfg = {
};
const struct iwl_cfg iwl6150_bg_cfg = {
- .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BG",
+ .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BG+N",
IWL_DEVICE_6150,
+ .ht_params = &iwl6000_ht_params,
};
const struct iwl_cfg iwl6000_3agn_cfg = {
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c
index f2cea1c7befc..bb72f4e6ed87 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c
@@ -347,6 +347,160 @@ static int iwl_find_otp_image(struct iwl_trans *trans,
return -EINVAL;
}
+/*
+ * The following regulatory items and supporting code are from:
+ * iwleeprom - EEPROM reader/writer for intel wifi cards.
+ */
+struct regulatory_item
+{
+ unsigned addr;
+ u16 data;
+ u16 chn;
+};
+
+#define HT40 0x100
+struct regulatory_item regulatory[] =
+{
+ /*
+ * BAND 2.4GHz (@15e-179 with regulatory base @156)
+ * enabling channels 12-14 (1-11 should be enabled on all cards)
+ */
+ { 0x1E, 0x0f21, 12 },
+ { 0x20, 0x0f21, 13 },
+ { 0x22, 0x0f21, 14 },
+
+ /* BAND 5GHz */
+
+ /* subband 5170-5320 MHz (@198-1af) */
+// { 0x42, 0x0fe1, 34 },
+ { 0x44, 0x0fe1, 36 },
+// { 0x46, 0x0fe1, 38 },
+ { 0x48, 0x0fe1, 40 },
+// { 0x4a, 0x0fe1, 42 },
+ { 0x4c, 0x0fe1, 44 },
+// { 0x4e, 0x0fe1, 46 },
+ { 0x50, 0x0fe1, 48 },
+ { 0x52, 0x0f31, 52 },
+ { 0x54, 0x0f31, 56 },
+ { 0x56, 0x0f31, 60 },
+ { 0x58, 0x0f31, 64 },
+
+ /* subband 5500-5700 MHz (@1b2-1c7) */
+ { 0x5c, 0x0f31, 100 },
+ { 0x5e, 0x0f31, 104 },
+ { 0x60, 0x0f31, 108 },
+ { 0x62, 0x0f31, 112 },
+ { 0x64, 0x0f31, 116 },
+ { 0x66, 0x0f31, 120 },
+ { 0x68, 0x0f31, 124 },
+ { 0x6a, 0x0f31, 128 },
+ { 0x6c, 0x0f31, 132 },
+ { 0x6e, 0x0f31, 136 },
+ { 0x70, 0x0f31, 140 },
+
+ /* subband 5725-5825 MHz (@1ca-1d5) */
+// { 0x74, 0x0fa1, 145 },
+ { 0x76, 0x0fa1, 149 },
+ { 0x78, 0x0fa1, 153 },
+ { 0x7a, 0x0fa1, 157 },
+ { 0x7c, 0x0fa1, 161 },
+ { 0x7e, 0x0fa1, 165 },
+
+ /* BAND 2.4GHz, HT40 channels (@1d8-1e5) */
+ { 0x82, 0x0e6f, HT40 + 1 },
+ { 0x84, 0x0f6f, HT40 + 2 },
+ { 0x86, 0x0f6f, HT40 + 3 },
+ { 0x88, 0x0f6f, HT40 + 4 },
+ { 0x8a, 0x0f6f, HT40 + 5 },
+ { 0x8c, 0x0f6f, HT40 + 6 },
+ { 0x8e, 0x0f6f, HT40 + 7 },
+
+ /* BAND 5GHz, HT40 channels (@1e8-1fd) */
+ { 0x92, 0x0fe1, HT40 + 36 },
+ { 0x94, 0x0fe1, HT40 + 44 },
+ { 0x96, 0x0f31, HT40 + 52 },
+ { 0x98, 0x0f31, HT40 + 60 },
+ { 0x9a, 0x0f31, HT40 + 100 },
+ { 0x9c, 0x0f31, HT40 + 108 },
+ { 0x9e, 0x0f31, HT40 + 116 },
+ { 0xa0, 0x0f31, HT40 + 124 },
+ { 0xa2, 0x0f31, HT40 + 132 },
+ { 0xa4, 0x0f61, HT40 + 149 },
+ { 0xa6, 0x0f61, HT40 + 157 },
+
+ { 0, 0}
+};
+
+/* This and bellow one are an EEPROM copy acessors actually */
+u16 iwl_eeprom_read16(__le16 *base, unsigned addr)
+{
+ return base[addr >> 1];
+}
+
+void iwl_eeprom_write16(__le16 *base, unsigned addr, u16 value)
+{
+ base[addr >> 1] = value;
+}
+
+/* We get here whith EEPROM semaphore aquired */
+void iwl_eeprom_patch11n(struct iwl_trans *trans, __le16 *eeprom)
+{
+ uint16_t value;
+ unsigned reg_offs;
+ int idx;
+
+ IWL_DEBUG_EEPROM(trans->dev, "Patching card EEPROM shadow RAM...\n");
+
+ /* This seems redundand but let's be intact with the original code */
+ IWL_DEBUG_EEPROM(trans->dev, "-> Changing subdev ID\n");
+ /* The magic number is EEPROM_SUBSYSTEM_ID from iwl-eeprom-parse.c */
+ value = iwl_eeprom_read16(eeprom, 0x14);
+ if ((value & 0x000F) == 0x0006) {
+ iwl_eeprom_write16(eeprom, 0x14, (value & 0xFFF0) | 0x0001);
+ }
+ /*
+ * enabling .11n
+ * W @8A << 00F0 (00B0) <- xxxx xxxx x1xx xxxx
+ * W @8C << 103E (603F) <- x001 xxxx xxxx xxx0
+ */
+ IWL_DEBUG_EEPROM(trans->dev, "-> Enabling 11n mode\n");
+
+ /* SKU_CAP */
+ value = iwl_eeprom_read16(eeprom, 0x8A);
+ if ((value & 0x0040) != 0x0040) {
+ IWL_DEBUG_EEPROM(trans->dev, " SKU CAP\n");
+ iwl_eeprom_write16(eeprom, 0x8A, value | 0x0040);
+ }
+
+ /* OEM_MODE */
+ value = iwl_eeprom_read16(eeprom, 0x8C);
+ if ((value & 0x7001) != 0x1000) {
+ IWL_DEBUG_EEPROM(trans->dev, " OEM MODE\n");
+ iwl_eeprom_write16(eeprom, 0x8C, (value & 0x9FFE) | 0x1000);
+ }
+
+ /* writing SKU ID - 'MoW' signature */
+ if (iwl_eeprom_read16(eeprom, 0x158) != 0x6f4d)
+ iwl_eeprom_write16(eeprom, 0x158, 0x6f4d);
+ if (iwl_eeprom_read16(eeprom, 0x15A) != 0x0057)
+ iwl_eeprom_write16(eeprom, 0x15A, 0x0057);
+
+ IWL_DEBUG_EEPROM(trans->dev, "-> Checking and adding channels...\n");
+ /* reading regulatory offset */
+ reg_offs = 2 * iwl_eeprom_read16(eeprom, 0xCC);
+ IWL_DEBUG_EEPROM(trans->dev, "Regulatory base: %04x\n", reg_offs);
+
+ /*writing channels regulatory.*/
+ for (idx=0; regulatory[idx].addr; idx++) {
+ if (iwl_eeprom_read16(eeprom, reg_offs + regulatory[idx].addr) != regulatory[idx].data) {
+ IWL_DEBUG_EEPROM(trans->dev, " %d%s\n", regulatory[idx].chn & ~HT40, (regulatory[idx].chn & HT40) ? " (HT40)" : "");
+ iwl_eeprom_write16(eeprom, reg_offs + regulatory[idx].addr, regulatory[idx].data);
+ }
+ }
+
+ IWL_DEBUG_EEPROM(trans->dev, "\nCard EEPROM shadow RAM patched successfully\n");
+}
+
/**
* iwl_read_eeprom - read EEPROM contents
*
@@ -448,6 +602,21 @@ int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size)
IWL_DEBUG_EEPROM(trans->dev, "NVM Type: %s\n",
nvm_is_otp ? "OTP" : "EEPROM");
+#if IS_ENABLED(CONFIG_IWLDVM)
+ /* Hack EEPROM shadow to enable N mode on disabled hardware */
+ if (trans->cfg == &iwl6005_2abg_cfg ||
+ trans->cfg == &iwl6005_2bg_cfg ||
+ trans->cfg == &iwl6030_2abg_cfg ||
+ trans->cfg == &iwl6030_2bg_cfg ||
+ trans->cfg == &iwl1030_bg_cfg ||
+ trans->cfg == &iwl130_bg_cfg ||
+ trans->cfg == &iwl6000i_2abg_cfg ||
+ trans->cfg == &iwl6000i_2bg_cfg ||
+ trans->cfg == &iwl6050_2abg_cfg ||
+ trans->cfg == &iwl6150_bg_cfg)
+ iwl_eeprom_patch11n(trans, e);
+#endif
+
iwl_eeprom_release_semaphore(trans);
*eeprom_size = sz;