Commit e55034e9 authored by John W. Linville's avatar John W. Linville
parents 73b48099 cf27d867
...@@ -3,7 +3,7 @@ menuconfig WL12XX_MENU ...@@ -3,7 +3,7 @@ menuconfig WL12XX_MENU
depends on MAC80211 && EXPERIMENTAL depends on MAC80211 && EXPERIMENTAL
---help--- ---help---
This will enable TI wl12xx driver support for the following chips: This will enable TI wl12xx driver support for the following chips:
wl1271 and wl1273. wl1271, wl1273, wl1281 and wl1283.
The drivers make use of the mac80211 stack. The drivers make use of the mac80211 stack.
config WL12XX config WL12XX
......
...@@ -965,10 +965,13 @@ int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) ...@@ -965,10 +965,13 @@ int wl1271_acx_ap_mem_cfg(struct wl1271 *wl)
} }
/* memory config */ /* memory config */
mem_conf->num_stations = wl->conf.mem.num_stations; /* FIXME: for now we always use mem_wl127x for AP, because it
mem_conf->rx_mem_block_num = wl->conf.mem.rx_block_num; * doesn't support dynamic memory and we don't have the
mem_conf->tx_min_mem_block_num = wl->conf.mem.tx_min_block_num; * optimal values for wl128x without dynamic memory yet */
mem_conf->num_ssid_profiles = wl->conf.mem.ssid_profiles; mem_conf->num_stations = wl->conf.mem_wl127x.num_stations;
mem_conf->rx_mem_block_num = wl->conf.mem_wl127x.rx_block_num;
mem_conf->tx_min_mem_block_num = wl->conf.mem_wl127x.tx_min_block_num;
mem_conf->num_ssid_profiles = wl->conf.mem_wl127x.ssid_profiles;
mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS);
ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf,
...@@ -986,6 +989,7 @@ int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) ...@@ -986,6 +989,7 @@ int wl1271_acx_ap_mem_cfg(struct wl1271 *wl)
int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) int wl1271_acx_sta_mem_cfg(struct wl1271 *wl)
{ {
struct wl1271_acx_sta_config_memory *mem_conf; struct wl1271_acx_sta_config_memory *mem_conf;
struct conf_memory_settings *mem;
int ret; int ret;
wl1271_debug(DEBUG_ACX, "wl1271 mem cfg"); wl1271_debug(DEBUG_ACX, "wl1271 mem cfg");
...@@ -996,16 +1000,21 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) ...@@ -996,16 +1000,21 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl)
goto out; goto out;
} }
if (wl->chip.id == CHIP_ID_1283_PG20)
mem = &wl->conf.mem_wl128x;
else
mem = &wl->conf.mem_wl127x;
/* memory config */ /* memory config */
mem_conf->num_stations = wl->conf.mem.num_stations; mem_conf->num_stations = mem->num_stations;
mem_conf->rx_mem_block_num = wl->conf.mem.rx_block_num; mem_conf->rx_mem_block_num = mem->rx_block_num;
mem_conf->tx_min_mem_block_num = wl->conf.mem.tx_min_block_num; mem_conf->tx_min_mem_block_num = mem->tx_min_block_num;
mem_conf->num_ssid_profiles = wl->conf.mem.ssid_profiles; mem_conf->num_ssid_profiles = mem->ssid_profiles;
mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS);
mem_conf->dyn_mem_enable = wl->conf.mem.dynamic_memory; mem_conf->dyn_mem_enable = mem->dynamic_memory;
mem_conf->tx_free_req = wl->conf.mem.min_req_tx_blocks; mem_conf->tx_free_req = mem->min_req_tx_blocks;
mem_conf->rx_free_req = wl->conf.mem.min_req_rx_blocks; mem_conf->rx_free_req = mem->min_req_rx_blocks;
mem_conf->tx_min = wl->conf.mem.tx_min; mem_conf->tx_min = mem->tx_min;
ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf,
sizeof(*mem_conf)); sizeof(*mem_conf));
...@@ -1019,6 +1028,32 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) ...@@ -1019,6 +1028,32 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl)
return ret; return ret;
} }
int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap)
{
struct wl1271_acx_host_config_bitmap *bitmap_conf;
int ret;
bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL);
if (!bitmap_conf) {
ret = -ENOMEM;
goto out;
}
bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap);
ret = wl1271_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP,
bitmap_conf, sizeof(*bitmap_conf));
if (ret < 0) {
wl1271_warning("wl1271 bitmap config opt failed: %d", ret);
goto out;
}
out:
kfree(bitmap_conf);
return ret;
}
int wl1271_acx_init_mem_config(struct wl1271 *wl) int wl1271_acx_init_mem_config(struct wl1271 *wl)
{ {
int ret; int ret;
......
...@@ -939,6 +939,16 @@ struct wl1271_acx_keep_alive_config { ...@@ -939,6 +939,16 @@ struct wl1271_acx_keep_alive_config {
u8 padding; u8 padding;
} __packed; } __packed;
#define HOST_IF_CFG_RX_FIFO_ENABLE BIT(0)
#define HOST_IF_CFG_TX_EXTRA_BLKS_SWAP BIT(1)
#define HOST_IF_CFG_TX_PAD_TO_SDIO_BLK BIT(3)
struct wl1271_acx_host_config_bitmap {
struct acx_header header;
__le32 host_cfg_bitmap;
} __packed;
enum { enum {
WL1271_ACX_TRIG_TYPE_LEVEL = 0, WL1271_ACX_TRIG_TYPE_LEVEL = 0,
WL1271_ACX_TRIG_TYPE_EDGE, WL1271_ACX_TRIG_TYPE_EDGE,
...@@ -1275,6 +1285,7 @@ int wl1271_acx_tx_config_options(struct wl1271 *wl); ...@@ -1275,6 +1285,7 @@ int wl1271_acx_tx_config_options(struct wl1271 *wl);
int wl1271_acx_ap_mem_cfg(struct wl1271 *wl); int wl1271_acx_ap_mem_cfg(struct wl1271 *wl);
int wl1271_acx_sta_mem_cfg(struct wl1271 *wl); int wl1271_acx_sta_mem_cfg(struct wl1271 *wl);
int wl1271_acx_init_mem_config(struct wl1271 *wl); int wl1271_acx_init_mem_config(struct wl1271 *wl);
int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap);
int wl1271_acx_init_rx_interrupt(struct wl1271 *wl); int wl1271_acx_init_rx_interrupt(struct wl1271 *wl);
int wl1271_acx_smart_reflex(struct wl1271 *wl); int wl1271_acx_smart_reflex(struct wl1271 *wl);
int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable); int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable);
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
*/ */
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/wl12xx.h>
#include "acx.h" #include "acx.h"
#include "reg.h" #include "reg.h"
...@@ -243,33 +244,57 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) ...@@ -243,33 +244,57 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
if (wl->nvs == NULL) if (wl->nvs == NULL)
return -ENODEV; return -ENODEV;
/* if (wl->chip.id == CHIP_ID_1283_PG20) {
* FIXME: the LEGACY NVS image support (NVS's missing the 5GHz band struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs;
* configurations) can be removed when those NVS files stop floating
* around. if (wl->nvs_len == sizeof(struct wl128x_nvs_file)) {
*/ if (nvs->general_params.dual_mode_select)
if (wl->nvs_len == sizeof(struct wl1271_nvs_file) || wl->enable_11a = true;
wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) { } else {
/* for now 11a is unsupported in AP mode */ wl1271_error("nvs size is not as expected: %zu != %zu",
if (wl->bss_type != BSS_TYPE_AP_BSS && wl->nvs_len,
wl->nvs->general_params.dual_mode_select) sizeof(struct wl128x_nvs_file));
wl->enable_11a = true; kfree(wl->nvs);
} wl->nvs = NULL;
wl->nvs_len = 0;
return -EILSEQ;
}
if (wl->nvs_len != sizeof(struct wl1271_nvs_file) && /* only the first part of the NVS needs to be uploaded */
(wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE || nvs_len = sizeof(nvs->nvs);
wl->enable_11a)) { nvs_ptr = (u8 *)nvs->nvs;
wl1271_error("nvs size is not as expected: %zu != %zu",
wl->nvs_len, sizeof(struct wl1271_nvs_file)); } else {
kfree(wl->nvs); struct wl1271_nvs_file *nvs =
wl->nvs = NULL; (struct wl1271_nvs_file *)wl->nvs;
wl->nvs_len = 0; /*
return -EILSEQ; * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz
} * band configurations) can be removed when those NVS files stop
* floating around.
*/
if (wl->nvs_len == sizeof(struct wl1271_nvs_file) ||
wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) {
/* for now 11a is unsupported in AP mode */
if (wl->bss_type != BSS_TYPE_AP_BSS &&
nvs->general_params.dual_mode_select)
wl->enable_11a = true;
}
/* only the first part of the NVS needs to be uploaded */ if (wl->nvs_len != sizeof(struct wl1271_nvs_file) &&
nvs_len = sizeof(wl->nvs->nvs); (wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE ||
nvs_ptr = (u8 *)wl->nvs->nvs; wl->enable_11a)) {
wl1271_error("nvs size is not as expected: %zu != %zu",
wl->nvs_len, sizeof(struct wl1271_nvs_file));
kfree(wl->nvs);
wl->nvs = NULL;
wl->nvs_len = 0;
return -EILSEQ;
}
/* only the first part of the NVS needs to be uploaded */
nvs_len = sizeof(nvs->nvs);
nvs_ptr = (u8 *) nvs->nvs;
}
/* update current MAC address to NVS */ /* update current MAC address to NVS */
nvs_ptr[11] = wl->mac_addr[0]; nvs_ptr[11] = wl->mac_addr[0];
...@@ -319,10 +344,13 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) ...@@ -319,10 +344,13 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
/* /*
* We've reached the first zero length, the first NVS table * We've reached the first zero length, the first NVS table
* is located at an aligned offset which is at least 7 bytes further. * is located at an aligned offset which is at least 7 bytes further.
* NOTE: The wl->nvs->nvs element must be first, in order to
* simplify the casting, we assume it is at the beginning of
* the wl->nvs structure.
*/ */
nvs_ptr = (u8 *)wl->nvs->nvs + nvs_ptr = (u8 *)wl->nvs +
ALIGN(nvs_ptr - (u8 *)wl->nvs->nvs + 7, 4); ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4);
nvs_len -= nvs_ptr - (u8 *)wl->nvs->nvs; nvs_len -= nvs_ptr - (u8 *)wl->nvs;
/* Now we must set the partition correctly */ /* Now we must set the partition correctly */
wl1271_set_partition(wl, &part_table[PART_WORK]); wl1271_set_partition(wl, &part_table[PART_WORK]);
...@@ -454,6 +482,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) ...@@ -454,6 +482,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
if (wl->bss_type == BSS_TYPE_AP_BSS) if (wl->bss_type == BSS_TYPE_AP_BSS)
wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID; wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID;
else
wl->event_mask |= DUMMY_PACKET_EVENT_ID;
ret = wl1271_event_unmask(wl); ret = wl1271_event_unmask(wl);
if (ret < 0) { if (ret < 0) {
...@@ -493,24 +523,159 @@ static void wl1271_boot_hw_version(struct wl1271 *wl) ...@@ -493,24 +523,159 @@ static void wl1271_boot_hw_version(struct wl1271 *wl)
wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION;
} }
/* uploads NVS and firmware */ static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl)
int wl1271_load_firmware(struct wl1271 *wl)
{ {
int ret = 0; u16 spare_reg;
u32 tmp, clk, pause;
/* Mask bits [2] & [8:4] in the sys_clk_cfg register */
spare_reg = wl1271_top_reg_read(wl, WL_SPARE_REG);
if (spare_reg == 0xFFFF)
return -EFAULT;
spare_reg |= (BIT(3) | BIT(5) | BIT(6));
wl1271_top_reg_write(wl, WL_SPARE_REG, spare_reg);
/* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */
wl1271_top_reg_write(wl, SYS_CLK_CFG_REG,
WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF);
/* Delay execution for 15msec, to let the HW settle */
mdelay(15);
return 0;
}
static bool wl128x_is_tcxo_valid(struct wl1271 *wl)
{
u16 tcxo_detection;
tcxo_detection = wl1271_top_reg_read(wl, TCXO_CLK_DETECT_REG);
if (tcxo_detection & TCXO_DET_FAILED)
return false;
return true;
}
static bool wl128x_is_fref_valid(struct wl1271 *wl)
{
u16 fref_detection;
fref_detection = wl1271_top_reg_read(wl, FREF_CLK_DETECT_REG);
if (fref_detection & FREF_CLK_DETECT_FAIL)
return false;
return true;
}
static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl)
{
wl1271_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL);
wl1271_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL);
wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, MCS_PLL_CONFIG_REG_VAL);
return 0;
}
static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
{
u16 spare_reg;
u16 pll_config;
u8 input_freq;
/* Mask bits [3:1] in the sys_clk_cfg register */
spare_reg = wl1271_top_reg_read(wl, WL_SPARE_REG);
if (spare_reg == 0xFFFF)
return -EFAULT;
spare_reg |= BIT(2);
wl1271_top_reg_write(wl, WL_SPARE_REG, spare_reg);
/* Handle special cases of the TCXO clock */
if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6)
return wl128x_manually_configure_mcs_pll(wl);
/* Set the input frequency according to the selected clock source */
input_freq = (clk & 1) + 1;
pll_config = wl1271_top_reg_read(wl, MCS_PLL_CONFIG_REG);
if (pll_config == 0xFFFF)
return -EFAULT;
pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT);
pll_config |= MCS_PLL_ENABLE_HP;
wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config);
return 0;
}
/*
* WL128x has two clocks input - TCXO and FREF.
* TCXO is the main clock of the device, while FREF is used to sync
* between the GPS and the cellular modem.
* In cases where TCXO is 32.736MHz or 16.368MHz, the FREF will be used
* as the WLAN/BT main clock.
*/
static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
{
u16 sys_clk_cfg;
/* For XTAL-only modes, FREF will be used after switching from TCXO */
if (wl->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
wl->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
if (!wl128x_switch_tcxo_to_fref(wl))
return -EINVAL;
goto fref_clk;
}
/* Query the HW, to determine which clock source we should use */
sys_clk_cfg = wl1271_top_reg_read(wl, SYS_CLK_CFG_REG);
if (sys_clk_cfg == 0xFFFF)
return -EINVAL;
if (sys_clk_cfg & PRCM_CM_EN_MUX_WLAN_FREF)
goto fref_clk;
/* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */
if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_368 ||
wl->tcxo_clock == WL12XX_TCXOCLOCK_32_736) {
if (!wl128x_switch_tcxo_to_fref(wl))
return -EINVAL;
goto fref_clk;
}
/* TCXO clock is selected */
if (!wl128x_is_tcxo_valid(wl))
return -EINVAL;
*selected_clock = wl->tcxo_clock;
goto config_mcs_pll;
fref_clk:
/* FREF clock is selected */
if (!wl128x_is_fref_valid(wl))
return -EINVAL;
*selected_clock = wl->ref_clock;
config_mcs_pll:
return wl128x_configure_mcs_pll(wl, *selected_clock);
}
static int wl127x_boot_clk(struct wl1271 *wl)
{
u32 pause;
u32 clk;
wl1271_boot_hw_version(wl); wl1271_boot_hw_version(wl);
if (wl->ref_clock == 0 || wl->ref_clock == 2 || wl->ref_clock == 4) if (wl->ref_clock == CONF_REF_CLK_19_2_E ||
wl->ref_clock == CONF_REF_CLK_38_4_E ||
wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL)
/* ref clk: 19.2/38.4/38.4-XTAL */ /* ref clk: 19.2/38.4/38.4-XTAL */
clk = 0x3; clk = 0x3;
else if (wl->ref_clock == 1 || wl->ref_clock == 3) else if (wl->ref_clock == CONF_REF_CLK_26_E ||
wl->ref_clock == CONF_REF_CLK_52_E)
/* ref clk: 26/52 */ /* ref clk: 26/52 */
clk = 0x5; clk = 0x5;
else else
return -EINVAL; return -EINVAL;
if (wl->ref_clock != 0) { if (wl->ref_clock != CONF_REF_CLK_19_2_E) {
u16 val; u16 val;
/* Set clock type (open drain) */ /* Set clock type (open drain) */
val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE); val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE);
...@@ -540,6 +705,26 @@ int wl1271_load_firmware(struct wl1271 *wl) ...@@ -540,6 +705,26 @@ int wl1271_load_firmware(struct wl1271 *wl)
pause |= WU_COUNTER_PAUSE_VAL; pause |= WU_COUNTER_PAUSE_VAL;
wl1271_write32(wl, WU_COUNTER_PAUSE, pause); wl1271_write32(wl, WU_COUNTER_PAUSE, pause);
return 0;
}
/* uploads NVS and firmware */
int wl1271_load_firmware(struct wl1271 *wl)
{
int ret = 0;
u32 tmp, clk;
int selected_clock = -1;
if (wl->chip.id == CHIP_ID_1283_PG20) {
ret = wl128x_boot_clk(wl, &selected_clock);
if (ret < 0)
goto out;
} else {
ret = wl127x_boot_clk(wl);
if (ret < 0)
goto out;
}
/* Continue the ELP wake up sequence */ /* Continue the ELP wake up sequence */
wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
udelay(500); udelay(500);
...@@ -555,7 +740,12 @@ int wl1271_load_firmware(struct wl1271 *wl) ...@@ -555,7 +740,12 @@ int wl1271_load_firmware(struct wl1271 *wl)
wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
clk |= (wl->ref_clock << 1) << 4; if (wl->chip.id == CHIP_ID_1283_PG20) {
clk |= ((selected_clock & 0x3) << 1) << 4;
} else {
clk |= (wl->ref_clock << 1) << 4;
}
wl1271_write32(wl, DRPW_SCRATCH_START, clk); wl1271_write32(wl, DRPW_SCRATCH_START, clk);
wl1271_set_partition(wl, &part_table[PART_WORK]); wl1271_set_partition(wl, &part_table[PART_WORK]);
...@@ -585,16 +775,12 @@ int wl1271_load_firmware(struct wl1271 *wl) ...@@ -585,16 +775,12 @@ int wl1271_load_firmware(struct wl1271 *wl)
/* 6. read the EEPROM parameters */ /* 6. read the EEPROM parameters */
tmp = wl1271_read32(wl, SCR_PAD2); tmp = wl1271_read32(wl, SCR_PAD2);
ret = wl1271_boot_write_irq_polarity(wl);
if (ret < 0)
goto out;
wl1271_write32(wl, ACX_REG_INTERRUPT_MASK,
WL1271_ACX_ALL_EVENTS_VECTOR);
/* WL1271: The reference driver skips steps 7 to 10 (jumps directly /* WL1271: The reference driver skips steps 7 to 10 (jumps directly
* to upload_fw) */ * to upload_fw) */
if (wl->chip.id == CHIP_ID_1283_PG20)
wl1271_top_reg_write(wl, SDIO_IO_DS, wl->conf.hci_io_ds);
ret = wl1271_boot_upload_firmware(wl); ret = wl1271_boot_upload_firmware(wl);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -618,6 +804,13 @@ int wl1271_boot(struct wl1271 *wl) ...@@ -618,6 +804,13 @@ int wl1271_boot(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
goto out; goto out;
ret = wl1271_boot_write_irq_polarity(wl);
if (ret < 0)
goto out;
wl1271_write32(wl, ACX_REG_INTERRUPT_MASK,
WL1271_ACX_ALL_EVENTS_VECTOR);
/* Enable firmware interrupts now */ /* Enable firmware interrupts now */
wl1271_boot_enable_interrupts(wl); wl1271_boot_enable_interrupts(wl);
......
...@@ -74,4 +74,56 @@ struct wl1271_static_data { ...@@ -74,4 +74,56 @@ struct wl1271_static_data {
#define FREF_CLK_POLARITY_BITS 0xfffff8ff #define FREF_CLK_POLARITY_BITS 0xfffff8ff
#define CLK_REQ_OUTN_SEL 0x700 #define CLK_REQ_OUTN_SEL 0x700
/* PLL configuration algorithm for wl128x */
#define SYS_CLK_CFG_REG 0x2200
/* Bit[0] - 0-TCXO, 1-FREF */
#define MCS_PLL_CLK_SEL_FREF BIT(0)
/* Bit[3:2] - 01-TCXO, 10-FREF */
#define WL_CLK_REQ_TYPE_FREF BIT(3)
#define WL_CLK_REQ_TYPE_PG2 (BIT(3) | BIT(2))
/* Bit[4] - 0-TCXO, 1-FREF */
#define PRCM_CM_EN_MUX_WLAN_FREF BIT(4)
#define TCXO_ILOAD_INT_REG 0x2264
#define TCXO_CLK_DETECT_REG 0x2266
#define TCXO_DET_FAILED BIT(4)
#define FREF_ILOAD_INT_REG 0x2084
#define FREF_CLK_DETECT_REG 0x2086
#define FREF_CLK_DETECT_FAIL BIT(4)
/* Use this reg for masking during driver access */
#define WL_SPARE_REG 0x2320
#define WL_SPARE_VAL BIT(2)
/* Bit[6:5:3] - mask wl write SYS_CLK_CFG[8:5:2:4] */
#define WL_SPARE_MASK_8526 (BIT(6) | BIT(5) | BIT(3))
#define PLL_LOCK_COUNTERS_REG 0xD8C
#define PLL_LOCK_COUNTERS_COEX 0x0F
#define PLL_LOCK_COUNTERS_MCS 0xF0
#define MCS_PLL_OVERRIDE_REG 0xD90
#define MCS_PLL_CONFIG_REG 0xD92
#define MCS_SEL_IN_FREQ_MASK 0x0070
#define MCS_SEL_IN_FREQ_SHIFT 4
#define MCS_PLL_CONFIG_REG_VAL 0x73
#define MCS_PLL_ENABLE_HP (BIT(0) | BIT(1))
#define MCS_PLL_M_REG 0xD94
#define MCS_PLL_N_REG 0xD96
#define MCS_PLL_M_REG_VAL 0xC8
#define MCS_PLL_N_REG_VAL 0x07
#define SDIO_IO_DS 0xd14
/* SDIO/wSPI DS configuration values */
enum {
HCI_IO_DS_8MA = 0,
HCI_IO_DS_4MA = 1, /* default */
HCI_IO_DS_6MA = 2,
HCI_IO_DS_2MA = 3,
};
/* end PLL configuration algorithm for wl128x */
#endif #endif
...@@ -110,7 +110,47 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, ...@@ -110,7 +110,47 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
int wl1271_cmd_general_parms(struct wl1271 *wl) int wl1271_cmd_general_parms(struct wl1271 *wl)
{ {
struct wl1271_general_parms_cmd *gen_parms; struct wl1271_general_parms_cmd *gen_parms;
struct wl1271_ini_general_params *gp = &wl->nvs->general_params; struct wl1271_ini_general_params *gp =
&((struct wl1271_nvs_file *)wl->nvs)->general_params;
bool answer = false;
int ret;
if (!wl->nvs)
return -ENODEV;
gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL);
if (!gen_parms)
return -ENOMEM;
gen_parms->test.id = TEST_CMD_INI_FILE_GENERAL_PARAM;
memcpy(&gen_parms->general_params, gp, sizeof(*gp));
if (gp->tx_bip_fem_auto_detect)
answer = true;
ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer);
if (ret < 0) {
wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed");
goto out;
}
gp->tx_bip_fem_manufacturer =
gen_parms->general_params.tx_bip_fem_manufacturer;
wl1271_debug(DEBUG_CMD, "FEM autodetect: %s, manufacturer: %d\n",
answer ? "auto" : "manual", gp->tx_bip_fem_manufacturer);
out:
kfree(gen_parms);
return ret;
}
int wl128x_cmd_general_parms(struct wl1271 *wl)
{
struct wl128x_general_parms_cmd *gen_parms;
struct wl128x_ini_general_params *gp =
&((struct wl128x_nvs_file *)wl->nvs)->general_params;
bool answer = false; bool answer = false;
int ret; int ret;
...@@ -147,8 +187,9 @@ int wl1271_cmd_general_parms(struct wl1271 *wl) ...@@ -147,8 +187,9 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
int wl1271_cmd_radio_parms(struct wl1271 *wl) int wl1271_cmd_radio_parms(struct wl1271 *wl)
{ {
struct wl1271_nvs_file *nvs = (struct wl1271_nvs_file *)wl->nvs;
struct wl1271_radio_parms_cmd *radio_parms; struct wl1271_radio_parms_cmd *radio_parms;
struct wl1271_ini_general_params *gp = &wl->nvs->general_params; struct wl1271_ini_general_params *gp = &nvs->general_params;
int ret; int ret;
if (!wl->nvs) if (!wl->nvs)
...@@ -161,18 +202,18 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl) ...@@ -161,18 +202,18 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM; radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM;
/* 2.4GHz parameters */ /* 2.4GHz parameters */
memcpy(&radio_parms->static_params_2, &wl->nvs->stat_radio_params_2, memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2,
sizeof(struct wl1271_ini_band_params_2)); sizeof(struct wl1271_ini_band_params_2));
memcpy(&radio_parms->dyn_params_2, memcpy(&radio_parms->dyn_params_2,
&wl->nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params, &nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params,
sizeof(struct wl1271_ini_fem_params_2)); sizeof(struct wl1271_ini_fem_params_2));
/* 5GHz parameters */ /* 5GHz parameters */
memcpy(&radio_parms->static_params_5, memcpy(&radio_parms->static_params_5,
&wl->nvs->stat_radio_params_5, &nvs->stat_radio_params_5,
sizeof(struct wl1271_ini_band_params_5)); sizeof(struct wl1271_ini_band_params_5));
memcpy(&radio_parms->dyn_params_5, memcpy(&radio_parms->dyn_params_5,
&wl->nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params, &nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params,
sizeof(struct wl1271_ini_fem_params_5)); sizeof(struct wl1271_ini_fem_params_5));
wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ", wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ",
...@@ -186,6 +227,50 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl) ...@@ -186,6 +227,50 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
return ret; return ret;
} }
int wl128x_cmd_radio_parms(struct wl1271 *wl)
{
struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs;
struct wl128x_radio_parms_cmd *radio_parms;
struct wl128x_ini_general_params *gp = &nvs->general_params;
int ret;
if (!wl->nvs)
return -ENODEV;
radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL);
if (!radio_parms)
return -ENOMEM;
radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM;
/* 2.4GHz parameters */
memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2,
sizeof(struct wl128x_ini_band_params_2));
memcpy(&radio_parms->dyn_params_2,
&nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params,
sizeof(struct wl128x_ini_fem_params_2));
/* 5GHz parameters */
memcpy(&radio_parms->static_params_5,
&nvs->stat_radio_params_5,
sizeof(struct wl128x_ini_band_params_5));
memcpy(&radio_parms->dyn_params_5,
&nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params,
sizeof(struct wl128x_ini_fem_params_5));
radio_parms->fem_vendor_and_options = nvs->fem_vendor_and_options;
wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ",
radio_parms, sizeof(*radio_parms));
ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0);
if (ret < 0)
wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed");
kfree(radio_parms);
return ret;
}
int wl1271_cmd_ext_radio_parms(struct wl1271 *wl) int wl1271_cmd_ext_radio_parms(struct wl1271 *wl)
{ {
struct wl1271_ext_radio_parms_cmd *ext_radio_parms; struct wl1271_ext_radio_parms_cmd *ext_radio_parms;
......
...@@ -32,7 +32,9 @@ struct acx_header; ...@@ -32,7 +32,9 @@ struct acx_header;
int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
size_t res_len); size_t res_len);
int wl1271_cmd_general_parms(struct wl1271 *wl); int wl1271_cmd_general_parms(struct wl1271 *wl);
int wl128x_cmd_general_parms(struct wl1271 *wl);
int wl1271_cmd_radio_parms(struct wl1271 *wl); int wl1271_cmd_radio_parms(struct wl1271 *wl);
int wl128x_cmd_radio_parms(struct wl1271 *wl);
int wl1271_cmd_ext_radio_parms(struct wl1271 *wl); int wl1271_cmd_ext_radio_parms(struct wl1271 *wl);
int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type); int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type);
int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
...@@ -415,6 +417,21 @@ struct wl1271_general_parms_cmd { ...@@ -415,6 +417,21 @@ struct wl1271_general_parms_cmd {
u8 padding[3]; u8 padding[3];
} __packed; } __packed;
struct wl128x_general_parms_cmd {
struct wl1271_cmd_header header;
struct wl1271_cmd_test_header test;
struct wl128x_ini_general_params general_params;
u8 sr_debug_table[WL1271_INI_MAX_SMART_REFLEX_PARAM];
u8 sr_sen_n_p;
u8 sr_sen_n_p_gain;
u8 sr_sen_nrn;
u8 sr_sen_prn;
u8 padding[3];
} __packed;
struct wl1271_radio_parms_cmd { struct wl1271_radio_parms_cmd {
struct wl1271_cmd_header header; struct wl1271_cmd_header header;
...@@ -431,6 +448,23 @@ struct wl1271_radio_parms_cmd { ...@@ -431,6 +448,23 @@ struct wl1271_radio_parms_cmd {
u8 padding3[2]; u8 padding3[2];
} __packed; } __packed;
struct wl128x_radio_parms_cmd {
struct wl1271_cmd_header header;
struct wl1271_cmd_test_header test;
/* Static radio parameters */
struct wl128x_ini_band_params_2 static_params_2;
struct wl128x_ini_band_params_5 static_params_5;
u8 fem_vendor_and_options;
/* Dynamic radio parameters */
struct wl128x_ini_fem_params_2 dyn_params_2;
u8 padding2;
struct wl128x_ini_fem_params_5 dyn_params_5;
} __packed;
struct wl1271_ext_radio_parms_cmd { struct wl1271_ext_radio_parms_cmd {
struct wl1271_cmd_header header; struct wl1271_cmd_header header;
......
...@@ -1004,7 +1004,9 @@ enum { ...@@ -1004,7 +1004,9 @@ enum {
CONF_REF_CLK_19_2_E, CONF_REF_CLK_19_2_E,
CONF_REF_CLK_26_E, CONF_REF_CLK_26_E,
CONF_REF_CLK_38_4_E, CONF_REF_CLK_38_4_E,
CONF_REF_CLK_52_E CONF_REF_CLK_52_E,
CONF_REF_CLK_38_4_M_XTAL,
CONF_REF_CLK_26_M_XTAL,
}; };
enum single_dual_band_enum { enum single_dual_band_enum {
...@@ -1018,15 +1020,6 @@ enum single_dual_band_enum { ...@@ -1018,15 +1020,6 @@ enum single_dual_band_enum {
#define CONF_NUMBER_OF_CHANNELS_2_4 14 #define CONF_NUMBER_OF_CHANNELS_2_4 14
#define CONF_NUMBER_OF_CHANNELS_5 35 #define CONF_NUMBER_OF_CHANNELS_5 35
struct conf_radio_parms {
/*
* FEM parameter set to use
*
* Range: 0 or 1
*/
u8 fem;
};
struct conf_itrim_settings { struct conf_itrim_settings {
/* enable dco itrim */ /* enable dco itrim */
u8 enable; u8 enable;
...@@ -1202,7 +1195,9 @@ struct conf_drv_settings { ...@@ -1202,7 +1195,9 @@ struct conf_drv_settings {
struct conf_scan_settings scan; struct conf_scan_settings scan;
struct conf_rf_settings rf; struct conf_rf_settings rf;
struct conf_ht_setting ht; struct conf_ht_setting ht;
struct conf_memory_settings mem; struct conf_memory_settings mem_wl127x;
struct conf_memory_settings mem_wl128x;
u8 hci_io_ds;
}; };
#endif #endif
...@@ -267,7 +267,7 @@ static ssize_t gpio_power_write(struct file *file, ...@@ -267,7 +267,7 @@ static ssize_t gpio_power_write(struct file *file,
} }
buf[len] = '\0'; buf[len] = '\0';
ret = strict_strtoul(buf, 0, &value); ret = kstrtoul(buf, 0, &value);
if (ret < 0) { if (ret < 0) {
wl1271_warning("illegal value in gpio_power"); wl1271_warning("illegal value in gpio_power");
return -EINVAL; return -EINVAL;
......
...@@ -33,6 +33,7 @@ void wl1271_pspoll_work(struct work_struct *work) ...@@ -33,6 +33,7 @@ void wl1271_pspoll_work(struct work_struct *work)
{ {
struct delayed_work *dwork; struct delayed_work *dwork;
struct wl1271 *wl; struct wl1271 *wl;
int ret;
dwork = container_of(work, struct delayed_work, work); dwork = container_of(work, struct delayed_work, work);
wl = container_of(dwork, struct wl1271, pspoll_work); wl = container_of(dwork, struct wl1271, pspoll_work);
...@@ -55,8 +56,13 @@ void wl1271_pspoll_work(struct work_struct *work) ...@@ -55,8 +56,13 @@ void wl1271_pspoll_work(struct work_struct *work)
* delivery failure occurred, and no-one changed state since, so * delivery failure occurred, and no-one changed state since, so
* we should go back to powersave. * we should go back to powersave.
*/ */
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, wl->basic_rate, true); wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, wl->basic_rate, true);
wl1271_ps_elp_sleep(wl);
out: out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
}; };
...@@ -129,11 +135,6 @@ static int wl1271_event_ps_report(struct wl1271 *wl, ...@@ -129,11 +135,6 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
/* enable beacon early termination */ /* enable beacon early termination */
ret = wl1271_acx_bet_enable(wl, true); ret = wl1271_acx_bet_enable(wl, true);
if (ret < 0)
break;
/* go to extremely low power mode */
wl1271_ps_elp_sleep(wl);
break; break;
default: default:
break; break;
...@@ -228,6 +229,12 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) ...@@ -228,6 +229,12 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
wl1271_event_rssi_trigger(wl, mbox); wl1271_event_rssi_trigger(wl, mbox);
} }
if ((vector & DUMMY_PACKET_EVENT_ID) && !is_ap) {
wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
if (wl->vif)
wl1271_tx_dummy_packet(wl);
}
if (wl->vif && beacon_loss) if (wl->vif && beacon_loss)
ieee80211_connection_loss(wl->vif); ieee80211_connection_loss(wl->vif);
......
...@@ -59,7 +59,10 @@ enum { ...@@ -59,7 +59,10 @@ enum {
BSS_LOSE_EVENT_ID = BIT(18), BSS_LOSE_EVENT_ID = BIT(18),
REGAINED_BSS_EVENT_ID = BIT(19), REGAINED_BSS_EVENT_ID = BIT(19),
ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(20), ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(20),
STA_REMOVE_COMPLETE_EVENT_ID = BIT(21), /* AP */ /* STA: dummy paket for dynamic mem blocks */
DUMMY_PACKET_EVENT_ID = BIT(21),
/* AP: STA remove complete */
STA_REMOVE_COMPLETE_EVENT_ID = BIT(21),
SOFT_GEMINI_SENSE_EVENT_ID = BIT(22), SOFT_GEMINI_SENSE_EVENT_ID = BIT(22),
SOFT_GEMINI_PREDICTION_EVENT_ID = BIT(23), SOFT_GEMINI_PREDICTION_EVENT_ID = BIT(23),
SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24), SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24),
......
...@@ -41,6 +41,28 @@ struct wl1271_ini_general_params { ...@@ -41,6 +41,28 @@ struct wl1271_ini_general_params {
u8 srf3[WL1271_INI_MAX_SMART_REFLEX_PARAM]; u8 srf3[WL1271_INI_MAX_SMART_REFLEX_PARAM];
} __packed; } __packed;
#define WL128X_INI_MAX_SETTINGS_PARAM 4
struct wl128x_ini_general_params {
u8 ref_clock;
u8 settling_time;
u8 clk_valid_on_wakeup;
u8 tcxo_ref_clock;
u8 tcxo_settling_time;
u8 tcxo_valid_on_wakeup;
u8 tcxo_ldo_voltage;
u8 xtal_itrim_val;
u8 platform_conf;
u8 dual_mode_select;
u8 tx_bip_fem_auto_detect;
u8 tx_bip_fem_manufacturer;
u8 general_settings[WL128X_INI_MAX_SETTINGS_PARAM];
u8 sr_state;
u8 srf1[WL1271_INI_MAX_SMART_REFLEX_PARAM];
u8 srf2[WL1271_INI_MAX_SMART_REFLEX_PARAM];
u8 srf3[WL1271_INI_MAX_SMART_REFLEX_PARAM];
} __packed;
#define WL1271_INI_RSSI_PROCESS_COMPENS_SIZE 15 #define WL1271_INI_RSSI_PROCESS_COMPENS_SIZE 15
struct wl1271_ini_band_params_2 { struct wl1271_ini_band_params_2 {
...@@ -49,9 +71,16 @@ struct wl1271_ini_band_params_2 { ...@@ -49,9 +71,16 @@ struct wl1271_ini_band_params_2 {
u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE]; u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE];
} __packed; } __packed;
#define WL1271_INI_RATE_GROUP_COUNT 6
#define WL1271_INI_CHANNEL_COUNT_2 14 #define WL1271_INI_CHANNEL_COUNT_2 14
struct wl128x_ini_band_params_2 {
u8 rx_trace_insertion_loss;
u8 tx_trace_loss[WL1271_INI_CHANNEL_COUNT_2];
u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE];
} __packed;
#define WL1271_INI_RATE_GROUP_COUNT 6
struct wl1271_ini_fem_params_2 { struct wl1271_ini_fem_params_2 {
__le16 tx_bip_ref_pd_voltage; __le16 tx_bip_ref_pd_voltage;
u8 tx_bip_ref_power; u8 tx_bip_ref_power;
...@@ -68,6 +97,28 @@ struct wl1271_ini_fem_params_2 { ...@@ -68,6 +97,28 @@ struct wl1271_ini_fem_params_2 {
u8 normal_to_degraded_high_thr; u8 normal_to_degraded_high_thr;
} __packed; } __packed;
#define WL128X_INI_RATE_GROUP_COUNT 7
/* low and high temperatures */
#define WL128X_INI_PD_VS_TEMPERATURE_RANGES 2
struct wl128x_ini_fem_params_2 {
__le16 tx_bip_ref_pd_voltage;
u8 tx_bip_ref_power;
u8 tx_bip_ref_offset;
u8 tx_per_rate_pwr_limits_normal[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_per_rate_pwr_limits_degraded[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_per_rate_pwr_limits_extreme[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_per_chan_pwr_limits_11b[WL1271_INI_CHANNEL_COUNT_2];
u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_2];
u8 tx_pd_vs_rate_offsets[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_ibias[WL128X_INI_RATE_GROUP_COUNT + 1];
u8 tx_pd_vs_chan_offsets[WL1271_INI_CHANNEL_COUNT_2];
u8 tx_pd_vs_temperature[WL128X_INI_PD_VS_TEMPERATURE_RANGES];
u8 rx_fem_insertion_loss;
u8 degraded_low_to_normal_thr;
u8 normal_to_degraded_high_thr;
} __packed;
#define WL1271_INI_CHANNEL_COUNT_5 35 #define WL1271_INI_CHANNEL_COUNT_5 35
#define WL1271_INI_SUB_BAND_COUNT_5 7 #define WL1271_INI_SUB_BAND_COUNT_5 7
...@@ -77,6 +128,12 @@ struct wl1271_ini_band_params_5 { ...@@ -77,6 +128,12 @@ struct wl1271_ini_band_params_5 {
u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE]; u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE];
} __packed; } __packed;
struct wl128x_ini_band_params_5 {
u8 rx_trace_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5];
u8 tx_trace_loss[WL1271_INI_CHANNEL_COUNT_5];
u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE];
} __packed;
struct wl1271_ini_fem_params_5 { struct wl1271_ini_fem_params_5 {
__le16 tx_bip_ref_pd_voltage[WL1271_INI_SUB_BAND_COUNT_5]; __le16 tx_bip_ref_pd_voltage[WL1271_INI_SUB_BAND_COUNT_5];
u8 tx_bip_ref_power[WL1271_INI_SUB_BAND_COUNT_5]; u8 tx_bip_ref_power[WL1271_INI_SUB_BAND_COUNT_5];
...@@ -92,6 +149,23 @@ struct wl1271_ini_fem_params_5 { ...@@ -92,6 +149,23 @@ struct wl1271_ini_fem_params_5 {
u8 normal_to_degraded_high_thr; u8 normal_to_degraded_high_thr;
} __packed; } __packed;
struct wl128x_ini_fem_params_5 {
__le16 tx_bip_ref_pd_voltage[WL1271_INI_SUB_BAND_COUNT_5];
u8 tx_bip_ref_power[WL1271_INI_SUB_BAND_COUNT_5];
u8 tx_bip_ref_offset[WL1271_INI_SUB_BAND_COUNT_5];
u8 tx_per_rate_pwr_limits_normal[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_per_rate_pwr_limits_degraded[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_per_rate_pwr_limits_extreme[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_5];
u8 tx_pd_vs_rate_offsets[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_ibias[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_pd_vs_chan_offsets[WL1271_INI_CHANNEL_COUNT_5];
u8 tx_pd_vs_temperature[WL1271_INI_SUB_BAND_COUNT_5 *
WL128X_INI_PD_VS_TEMPERATURE_RANGES];
u8 rx_fem_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5];
u8 degraded_low_to_normal_thr;
u8 normal_to_degraded_high_thr;
} __packed;
/* NVS data structure */ /* NVS data structure */
#define WL1271_INI_NVS_SECTION_SIZE 468 #define WL1271_INI_NVS_SECTION_SIZE 468
...@@ -100,7 +174,7 @@ struct wl1271_ini_fem_params_5 { ...@@ -100,7 +174,7 @@ struct wl1271_ini_fem_params_5 {
#define WL1271_INI_LEGACY_NVS_FILE_SIZE 800 #define WL1271_INI_LEGACY_NVS_FILE_SIZE 800
struct wl1271_nvs_file { struct wl1271_nvs_file {
/* NVS section */ /* NVS section - must be first! */
u8 nvs[WL1271_INI_NVS_SECTION_SIZE]; u8 nvs[WL1271_INI_NVS_SECTION_SIZE];
/* INI section */ /* INI section */
...@@ -120,4 +194,24 @@ struct wl1271_nvs_file { ...@@ -120,4 +194,24 @@ struct wl1271_nvs_file {
} dyn_radio_params_5[WL1271_INI_FEM_MODULE_COUNT]; } dyn_radio_params_5[WL1271_INI_FEM_MODULE_COUNT];
} __packed; } __packed;
struct wl128x_nvs_file {
/* NVS section - must be first! */
u8 nvs[WL1271_INI_NVS_SECTION_SIZE];
/* INI section */
struct wl128x_ini_general_params general_params;
u8 fem_vendor_and_options;
struct wl128x_ini_band_params_2 stat_radio_params_2;
u8 padding2;
struct {
struct wl128x_ini_fem_params_2 params;
u8 padding;
} dyn_radio_params_2[WL1271_INI_FEM_MODULE_COUNT];
struct wl128x_ini_band_params_5 stat_radio_params_5;
u8 padding3;
struct {
struct wl128x_ini_fem_params_5 params;
u8 padding;
} dyn_radio_params_5[WL1271_INI_FEM_MODULE_COUNT];
} __packed;
#endif #endif
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "cmd.h" #include "cmd.h"
#include "reg.h" #include "reg.h"
#include "tx.h" #include "tx.h"
#include "io.h"
int wl1271_sta_init_templates_config(struct wl1271 *wl) int wl1271_sta_init_templates_config(struct wl1271 *wl)
{ {
...@@ -321,9 +322,11 @@ static int wl1271_sta_hw_init(struct wl1271 *wl) ...@@ -321,9 +322,11 @@ static int wl1271_sta_hw_init(struct wl1271 *wl)
{ {
int ret; int ret;
ret = wl1271_cmd_ext_radio_parms(wl); if (wl->chip.id != CHIP_ID_1283_PG20) {
if (ret < 0) ret = wl1271_cmd_ext_radio_parms(wl);
return ret; if (ret < 0)
return ret;
}
/* PS config */ /* PS config */
ret = wl1271_acx_config_ps(wl); ret = wl1271_acx_config_ps(wl);
...@@ -504,6 +507,27 @@ static int wl1271_set_ba_policies(struct wl1271 *wl) ...@@ -504,6 +507,27 @@ static int wl1271_set_ba_policies(struct wl1271 *wl)
return ret; return ret;
} }
int wl1271_chip_specific_init(struct wl1271 *wl)
{
int ret = 0;
if (wl->chip.id == CHIP_ID_1283_PG20) {
u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;
if (wl->quirks & WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT)
/* Enable SDIO padding */
host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK;
/* Must be before wl1271_acx_init_mem_config() */
ret = wl1271_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap);
if (ret < 0)
goto out;
}
out:
return ret;
}
int wl1271_hw_init(struct wl1271 *wl) int wl1271_hw_init(struct wl1271 *wl)
{ {
struct conf_tx_ac_category *conf_ac; struct conf_tx_ac_category *conf_ac;
...@@ -511,11 +535,22 @@ int wl1271_hw_init(struct wl1271 *wl) ...@@ -511,11 +535,22 @@ int wl1271_hw_init(struct wl1271 *wl)
int ret, i; int ret, i;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
ret = wl1271_cmd_general_parms(wl); if (wl->chip.id == CHIP_ID_1283_PG20)
ret = wl128x_cmd_general_parms(wl);
else
ret = wl1271_cmd_general_parms(wl);
if (ret < 0)
return ret;
if (wl->chip.id == CHIP_ID_1283_PG20)
ret = wl128x_cmd_radio_parms(wl);
else
ret = wl1271_cmd_radio_parms(wl);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = wl1271_cmd_radio_parms(wl); /* Chip-specific init */
ret = wl1271_chip_specific_init(wl);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
...@@ -31,6 +31,7 @@ int wl1271_sta_init_templates_config(struct wl1271 *wl); ...@@ -31,6 +31,7 @@ int wl1271_sta_init_templates_config(struct wl1271 *wl);
int wl1271_init_phy_config(struct wl1271 *wl); int wl1271_init_phy_config(struct wl1271 *wl);
int wl1271_init_pta(struct wl1271 *wl); int wl1271_init_pta(struct wl1271 *wl);
int wl1271_init_energy_detection(struct wl1271 *wl); int wl1271_init_energy_detection(struct wl1271 *wl);
int wl1271_chip_specific_init(struct wl1271 *wl);
int wl1271_hw_init(struct wl1271 *wl); int wl1271_hw_init(struct wl1271 *wl);
#endif #endif
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "wl12xx.h" #include "wl12xx.h"
#include "wl12xx_80211.h" #include "wl12xx_80211.h"
#include "io.h" #include "io.h"
#include "tx.h"
#define OCP_CMD_LOOP 32 #define OCP_CMD_LOOP 32
...@@ -43,6 +44,16 @@ ...@@ -43,6 +44,16 @@
#define OCP_STATUS_REQ_FAILED 0x20000 #define OCP_STATUS_REQ_FAILED 0x20000
#define OCP_STATUS_RESP_ERROR 0x30000 #define OCP_STATUS_RESP_ERROR 0x30000
bool wl1271_set_block_size(struct wl1271 *wl)
{
if (wl->if_ops->set_block_size) {
wl->if_ops->set_block_size(wl, WL12XX_BUS_BLOCK_SIZE);
return true;
}
return false;
}
void wl1271_disable_interrupts(struct wl1271 *wl) void wl1271_disable_interrupts(struct wl1271 *wl)
{ {
wl->if_ops->disable_irq(wl); wl->if_ops->disable_irq(wl);
......
...@@ -169,5 +169,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl); ...@@ -169,5 +169,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl);
struct ieee80211_hw *wl1271_alloc_hw(void); struct ieee80211_hw *wl1271_alloc_hw(void);
int wl1271_free_hw(struct wl1271 *wl); int wl1271_free_hw(struct wl1271 *wl);
irqreturn_t wl1271_irq(int irq, void *data); irqreturn_t wl1271_irq(int irq, void *data);
bool wl1271_set_block_size(struct wl1271 *wl);
int wl1271_tx_dummy_packet(struct wl1271 *wl);
void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters);
#endif #endif
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/wl12xx.h>
#include "wl12xx.h" #include "wl12xx.h"
#include "wl12xx_80211.h" #include "wl12xx_80211.h"
...@@ -54,7 +55,7 @@ static struct conf_drv_settings default_conf = { ...@@ -54,7 +55,7 @@ static struct conf_drv_settings default_conf = {
[CONF_SG_BT_PER_THRESHOLD] = 7500, [CONF_SG_BT_PER_THRESHOLD] = 7500,
[CONF_SG_HV3_MAX_OVERRIDE] = 0, [CONF_SG_HV3_MAX_OVERRIDE] = 0,
[CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400, [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400,
[CONF_SG_BT_LOAD_RATIO] = 50, [CONF_SG_BT_LOAD_RATIO] = 200,
[CONF_SG_AUTO_PS_MODE] = 1, [CONF_SG_AUTO_PS_MODE] = 1,
[CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
...@@ -254,7 +255,7 @@ static struct conf_drv_settings default_conf = { ...@@ -254,7 +255,7 @@ static struct conf_drv_settings default_conf = {
.ps_poll_threshold = 10, .ps_poll_threshold = 10,
.ps_poll_recovery_period = 700, .ps_poll_recovery_period = 700,
.bet_enable = CONF_BET_MODE_ENABLE, .bet_enable = CONF_BET_MODE_ENABLE,
.bet_max_consecutive = 10, .bet_max_consecutive = 50,
.psm_entry_retries = 5, .psm_entry_retries = 5,
.psm_exit_retries = 255, .psm_exit_retries = 255,
.psm_entry_nullfunc_retries = 3, .psm_entry_nullfunc_retries = 3,
...@@ -298,7 +299,7 @@ static struct conf_drv_settings default_conf = { ...@@ -298,7 +299,7 @@ static struct conf_drv_settings default_conf = {
.tx_ba_win_size = 64, .tx_ba_win_size = 64,
.inactivity_timeout = 10000, .inactivity_timeout = 10000,
}, },
.mem = { .mem_wl127x = {
.num_stations = 1, .num_stations = 1,
.ssid_profiles = 1, .ssid_profiles = 1,
.rx_block_num = 70, .rx_block_num = 70,
...@@ -307,7 +308,18 @@ static struct conf_drv_settings default_conf = { ...@@ -307,7 +308,18 @@ static struct conf_drv_settings default_conf = {
.min_req_tx_blocks = 100, .min_req_tx_blocks = 100,
.min_req_rx_blocks = 22, .min_req_rx_blocks = 22,
.tx_min = 27, .tx_min = 27,
} },
.mem_wl128x = {
.num_stations = 1,
.ssid_profiles = 1,
.rx_block_num = 40,
.tx_min_block_num = 40,
.dynamic_memory = 1,
.min_req_tx_blocks = 45,
.min_req_rx_blocks = 22,
.tx_min = 27,
},
.hci_io_ds = HCI_IO_DS_6MA,
}; };
static void __wl1271_op_remove_interface(struct wl1271 *wl); static void __wl1271_op_remove_interface(struct wl1271 *wl);
...@@ -329,6 +341,7 @@ static struct platform_device wl1271_device = { ...@@ -329,6 +341,7 @@ static struct platform_device wl1271_device = {
}, },
}; };
static DEFINE_MUTEX(wl_list_mutex);
static LIST_HEAD(wl_list); static LIST_HEAD(wl_list);
static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
...@@ -359,10 +372,12 @@ static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, ...@@ -359,10 +372,12 @@ static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
return NOTIFY_DONE; return NOTIFY_DONE;
wl_temp = hw->priv; wl_temp = hw->priv;
mutex_lock(&wl_list_mutex);
list_for_each_entry(wl, &wl_list, list) { list_for_each_entry(wl, &wl_list, list) {
if (wl == wl_temp) if (wl == wl_temp)
break; break;
} }
mutex_unlock(&wl_list_mutex);
if (wl != wl_temp) if (wl != wl_temp)
return NOTIFY_DONE; return NOTIFY_DONE;
...@@ -438,15 +453,30 @@ static int wl1271_plt_init(struct wl1271 *wl) ...@@ -438,15 +453,30 @@ static int wl1271_plt_init(struct wl1271 *wl)
struct conf_tx_tid *conf_tid; struct conf_tx_tid *conf_tid;
int ret, i; int ret, i;
ret = wl1271_cmd_general_parms(wl); if (wl->chip.id == CHIP_ID_1283_PG20)
ret = wl128x_cmd_general_parms(wl);
else
ret = wl1271_cmd_general_parms(wl);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = wl1271_cmd_radio_parms(wl); if (wl->chip.id == CHIP_ID_1283_PG20)
ret = wl128x_cmd_radio_parms(wl);
else
ret = wl1271_cmd_radio_parms(wl);
if (ret < 0)
return ret;
if (wl->chip.id != CHIP_ID_1283_PG20) {
ret = wl1271_cmd_ext_radio_parms(wl);
if (ret < 0)
return ret;
}
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = wl1271_cmd_ext_radio_parms(wl); /* Chip-specific initializations */
ret = wl1271_chip_specific_init(wl);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -593,15 +623,17 @@ static void wl1271_fw_status(struct wl1271 *wl, ...@@ -593,15 +623,17 @@ static void wl1271_fw_status(struct wl1271 *wl,
{ {
struct wl1271_fw_common_status *status = &full_status->common; struct wl1271_fw_common_status *status = &full_status->common;
struct timespec ts; struct timespec ts;
u32 total = 0; u32 old_tx_blk_count = wl->tx_blocks_available;
u32 freed_blocks = 0;
int i; int i;
if (wl->bss_type == BSS_TYPE_AP_BSS) if (wl->bss_type == BSS_TYPE_AP_BSS) {
wl1271_raw_read(wl, FW_STATUS_ADDR, status, wl1271_raw_read(wl, FW_STATUS_ADDR, status,
sizeof(struct wl1271_fw_ap_status), false); sizeof(struct wl1271_fw_ap_status), false);
else } else {
wl1271_raw_read(wl, FW_STATUS_ADDR, status, wl1271_raw_read(wl, FW_STATUS_ADDR, status,
sizeof(struct wl1271_fw_sta_status), false); sizeof(struct wl1271_fw_sta_status), false);
}
wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
"drv_rx_counter = %d, tx_results_counter = %d)", "drv_rx_counter = %d, tx_results_counter = %d)",
...@@ -612,22 +644,37 @@ static void wl1271_fw_status(struct wl1271 *wl, ...@@ -612,22 +644,37 @@ static void wl1271_fw_status(struct wl1271 *wl,
/* update number of available TX blocks */ /* update number of available TX blocks */
for (i = 0; i < NUM_TX_QUEUES; i++) { for (i = 0; i < NUM_TX_QUEUES; i++) {
u32 cnt = le32_to_cpu(status->tx_released_blks[i]) - freed_blocks += le32_to_cpu(status->tx_released_blks[i]) -
wl->tx_blocks_freed[i]; wl->tx_blocks_freed[i];
wl->tx_blocks_freed[i] = wl->tx_blocks_freed[i] =
le32_to_cpu(status->tx_released_blks[i]); le32_to_cpu(status->tx_released_blks[i]);
wl->tx_blocks_available += cnt;
total += cnt;
} }
/* if more blocks are available now, tx work can be scheduled */ wl->tx_allocated_blocks -= freed_blocks;
if (total)
clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
/* for AP update num of allocated TX blocks per link and ps status */ if (wl->bss_type == BSS_TYPE_AP_BSS) {
if (wl->bss_type == BSS_TYPE_AP_BSS) /* Update num of allocated TX blocks per link and ps status */
wl1271_irq_update_links_status(wl, &full_status->ap); wl1271_irq_update_links_status(wl, &full_status->ap);
wl->tx_blocks_available += freed_blocks;
} else {
int avail = full_status->sta.tx_total - wl->tx_allocated_blocks;
/*
* The FW might change the total number of TX memblocks before
* we get a notification about blocks being released. Thus, the
* available blocks calculation might yield a temporary result
* which is lower than the actual available blocks. Keeping in
* mind that only blocks that were allocated can be moved from
* TX to RX, tx_blocks_available should never decrease here.
*/
wl->tx_blocks_available = max((int)wl->tx_blocks_available,
avail);
}
/* if more blocks are available now, tx work can be scheduled */
if (wl->tx_blocks_available > old_tx_blk_count)
clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
/* update the host-chipset time offset */ /* update the host-chipset time offset */
getnstimeofday(&ts); getnstimeofday(&ts);
...@@ -674,6 +721,13 @@ irqreturn_t wl1271_irq(int irq, void *cookie) ...@@ -674,6 +721,13 @@ irqreturn_t wl1271_irq(int irq, void *cookie)
set_bit(WL1271_FLAG_TX_PENDING, &wl->flags); set_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
cancel_work_sync(&wl->tx_work); cancel_work_sync(&wl->tx_work);
/*
* In case edge triggered interrupt must be used, we cannot iterate
* more than once without introducing race conditions with the hardirq.
*/
if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
loopcount = 1;
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
wl1271_debug(DEBUG_IRQ, "IRQ work"); wl1271_debug(DEBUG_IRQ, "IRQ work");
...@@ -785,11 +839,17 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) ...@@ -785,11 +839,17 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
switch (wl->bss_type) { switch (wl->bss_type) {
case BSS_TYPE_AP_BSS: case BSS_TYPE_AP_BSS:
fw_name = WL1271_AP_FW_NAME; if (wl->chip.id == CHIP_ID_1283_PG20)
fw_name = WL128X_AP_FW_NAME;
else
fw_name = WL127X_AP_FW_NAME;
break; break;
case BSS_TYPE_IBSS: case BSS_TYPE_IBSS:
case BSS_TYPE_STA_BSS: case BSS_TYPE_STA_BSS:
fw_name = WL1271_FW_NAME; if (wl->chip.id == CHIP_ID_1283_PG20)
fw_name = WL128X_FW_NAME;
else
fw_name = WL1271_FW_NAME;
break; break;
default: default:
wl1271_error("no compatible firmware for bss_type %d", wl1271_error("no compatible firmware for bss_type %d",
...@@ -838,14 +898,14 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) ...@@ -838,14 +898,14 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
const struct firmware *fw; const struct firmware *fw;
int ret; int ret;
ret = request_firmware(&fw, WL1271_NVS_NAME, wl1271_wl_to_dev(wl)); ret = request_firmware(&fw, WL12XX_NVS_NAME, wl1271_wl_to_dev(wl));
if (ret < 0) { if (ret < 0) {
wl1271_error("could not get nvs file: %d", ret); wl1271_error("could not get nvs file: %d", ret);
return ret; return ret;
} }
wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL); wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
if (!wl->nvs) { if (!wl->nvs) {
wl1271_error("could not allocate memory for the nvs file"); wl1271_error("could not allocate memory for the nvs file");
...@@ -954,6 +1014,17 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ...@@ -954,6 +1014,17 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
goto out; goto out;
break; break;
case CHIP_ID_1283_PG20:
wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
wl->chip.id);
ret = wl1271_setup(wl);
if (ret < 0)
goto out;
if (wl1271_set_block_size(wl))
wl->quirks |= WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT;
break;
case CHIP_ID_1283_PG10:
default: default:
wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
ret = -ENODEV; ret = -ENODEV;
...@@ -978,6 +1049,24 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ...@@ -978,6 +1049,24 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
return ret; return ret;
} }
static unsigned int wl1271_get_fw_ver_quirks(struct wl1271 *wl)
{
unsigned int quirks = 0;
unsigned int *fw_ver = wl->chip.fw_ver;
/* Only for wl127x */
if ((fw_ver[FW_VER_CHIP] == FW_VER_CHIP_WL127X) &&
/* Check STA version */
(((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
(fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_STA_MIN)) ||
/* Check AP version */
((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) &&
(fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_AP_MIN))))
quirks |= WL12XX_QUIRK_USE_2_SPARE_BLOCKS;
return quirks;
}
int wl1271_plt_start(struct wl1271 *wl) int wl1271_plt_start(struct wl1271 *wl)
{ {
int retries = WL1271_BOOT_RETRIES; int retries = WL1271_BOOT_RETRIES;
...@@ -1013,6 +1102,9 @@ int wl1271_plt_start(struct wl1271 *wl) ...@@ -1013,6 +1102,9 @@ int wl1271_plt_start(struct wl1271 *wl)
wl->state = WL1271_STATE_PLT; wl->state = WL1271_STATE_PLT;
wl1271_notice("firmware booted in PLT mode (%s)", wl1271_notice("firmware booted in PLT mode (%s)",
wl->chip.fw_ver_str); wl->chip.fw_ver_str);
/* Check if any quirks are needed with older fw versions */
wl->quirks |= wl1271_get_fw_ver_quirks(wl);
goto out; goto out;
irq_disable: irq_disable:
...@@ -1040,7 +1132,7 @@ int wl1271_plt_start(struct wl1271 *wl) ...@@ -1040,7 +1132,7 @@ int wl1271_plt_start(struct wl1271 *wl)
return ret; return ret;
} }
int __wl1271_plt_stop(struct wl1271 *wl) static int __wl1271_plt_stop(struct wl1271 *wl)
{ {
int ret = 0; int ret = 0;
...@@ -1124,6 +1216,69 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) ...@@ -1124,6 +1216,69 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
spin_unlock_irqrestore(&wl->wl_lock, flags); spin_unlock_irqrestore(&wl->wl_lock, flags);
} }
int wl1271_tx_dummy_packet(struct wl1271 *wl)
{
unsigned long flags;
spin_lock_irqsave(&wl->wl_lock, flags);
set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
wl->tx_queue_count++;
spin_unlock_irqrestore(&wl->wl_lock, flags);
/* The FW is low on RX memory blocks, so send the dummy packet asap */
if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
wl1271_tx_work_locked(wl);
/*
* If the FW TX is busy, TX work will be scheduled by the threaded
* interrupt handler function
*/
return 0;
}
/*
* The size of the dummy packet should be at least 1400 bytes. However, in
* order to minimize the number of bus transactions, aligning it to 512 bytes
* boundaries could be beneficial, performance wise
*/
#define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512))
static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
{
struct sk_buff *skb;
struct ieee80211_hdr_3addr *hdr;
unsigned int dummy_packet_size;
dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE -
sizeof(struct wl1271_tx_hw_descr) - sizeof(*hdr);
skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE);
if (!skb) {
wl1271_warning("Failed to allocate a dummy packet skb");
return NULL;
}
skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr));
hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr));
memset(hdr, 0, sizeof(*hdr));
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_NULLFUNC |
IEEE80211_FCTL_TODS);
memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size);
/* Dummy packets require the TID to be management */
skb->priority = WL1271_TID_MGMT;
/* Initialize all fields that might be used */
skb->queue_mapping = 0;
memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
return skb;
}
static struct notifier_block wl1271_dev_notifier = { static struct notifier_block wl1271_dev_notifier = {
.notifier_call = wl1271_dev_notify, .notifier_call = wl1271_dev_notify,
}; };
...@@ -1174,6 +1329,16 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, ...@@ -1174,6 +1329,16 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
goto out; goto out;
} }
/*
* in some very corner case HW recovery scenarios its possible to
* get here before __wl1271_op_remove_interface is complete, so
* opt out if that is the case.
*/
if (test_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags)) {
ret = -EBUSY;
goto out;
}
switch (vif->type) { switch (vif->type) {
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
wl->bss_type = BSS_TYPE_STA_BSS; wl->bss_type = BSS_TYPE_STA_BSS;
...@@ -1242,6 +1407,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, ...@@ -1242,6 +1407,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
wl->vif = vif; wl->vif = vif;
wl->state = WL1271_STATE_ON; wl->state = WL1271_STATE_ON;
set_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags);
wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str); wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str);
/* update hw/fw version info in wiphy struct */ /* update hw/fw version info in wiphy struct */
...@@ -1249,6 +1415,9 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, ...@@ -1249,6 +1415,9 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
strncpy(wiphy->fw_version, wl->chip.fw_ver_str, strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
sizeof(wiphy->fw_version)); sizeof(wiphy->fw_version));
/* Check if any quirks are needed with older fw versions */
wl->quirks |= wl1271_get_fw_ver_quirks(wl);
/* /*
* Now we know if 11a is supported (info from the NVS), so disable * Now we know if 11a is supported (info from the NVS), so disable
* 11a channels if not supported * 11a channels if not supported
...@@ -1262,8 +1431,10 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, ...@@ -1262,8 +1431,10 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
out: out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
mutex_lock(&wl_list_mutex);
if (!ret) if (!ret)
list_add(&wl->list, &wl_list); list_add(&wl->list, &wl_list);
mutex_unlock(&wl_list_mutex);
return ret; return ret;
} }
...@@ -1274,11 +1445,15 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) ...@@ -1274,11 +1445,15 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
/* because of hardware recovery, we may get here twice */
if (wl->state != WL1271_STATE_ON)
return;
wl1271_info("down"); wl1271_info("down");
mutex_lock(&wl_list_mutex);
list_del(&wl->list); list_del(&wl->list);
mutex_unlock(&wl_list_mutex);
WARN_ON(wl->state != WL1271_STATE_ON);
/* enable dyn ps just in case (if left on due to fw crash etc) */ /* enable dyn ps just in case (if left on due to fw crash etc) */
if (wl->bss_type == BSS_TYPE_STA_BSS) if (wl->bss_type == BSS_TYPE_STA_BSS)
...@@ -1286,12 +1461,15 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) ...@@ -1286,12 +1461,15 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
if (wl->scan.state != WL1271_SCAN_STATE_IDLE) { if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
wl->scan.state = WL1271_SCAN_STATE_IDLE; wl->scan.state = WL1271_SCAN_STATE_IDLE;
kfree(wl->scan.scanned_ch); memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
wl->scan.scanned_ch = NULL;
wl->scan.req = NULL; wl->scan.req = NULL;
ieee80211_scan_completed(wl->hw, true); ieee80211_scan_completed(wl->hw, true);
} }
/*
* this must be before the cancel_work calls below, so that the work
* functions don't perform further work.
*/
wl->state = WL1271_STATE_OFF; wl->state = WL1271_STATE_OFF;
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
...@@ -1321,6 +1499,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) ...@@ -1321,6 +1499,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
wl->psm_entry_retry = 0; wl->psm_entry_retry = 0;
wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
wl->tx_blocks_available = 0; wl->tx_blocks_available = 0;
wl->tx_allocated_blocks = 0;
wl->tx_results_count = 0; wl->tx_results_count = 0;
wl->tx_packets_count = 0; wl->tx_packets_count = 0;
wl->tx_security_last_seq = 0; wl->tx_security_last_seq = 0;
...@@ -1328,7 +1507,6 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) ...@@ -1328,7 +1507,6 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
wl->time_offset = 0; wl->time_offset = 0;
wl->session_counter = 0; wl->session_counter = 0;
wl->rate_set = CONF_TX_RATE_MASK_BASIC; wl->rate_set = CONF_TX_RATE_MASK_BASIC;
wl->flags = 0;
wl->vif = NULL; wl->vif = NULL;
wl->filters = 0; wl->filters = 0;
wl1271_free_ap_keys(wl); wl1271_free_ap_keys(wl);
...@@ -1336,6 +1514,13 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) ...@@ -1336,6 +1514,13 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
wl->ap_fw_ps_map = 0; wl->ap_fw_ps_map = 0;
wl->ap_ps_map = 0; wl->ap_ps_map = 0;
/*
* this is performed after the cancel_work calls and the associated
* mutex_lock, so that wl1271_op_add_interface does not accidentally
* get executed before all these vars have been reset.
*/
wl->flags = 0;
for (i = 0; i < NUM_TX_QUEUES; i++) for (i = 0; i < NUM_TX_QUEUES; i++)
wl->tx_blocks_freed[i] = 0; wl->tx_blocks_freed[i] = 0;
...@@ -1368,7 +1553,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, ...@@ -1368,7 +1553,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
cancel_work_sync(&wl->recovery_work); cancel_work_sync(&wl->recovery_work);
} }
static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters) void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
{ {
wl1271_set_default_filters(wl); wl1271_set_default_filters(wl);
...@@ -1431,10 +1616,10 @@ static int wl1271_join(struct wl1271 *wl, bool set_assoc) ...@@ -1431,10 +1616,10 @@ static int wl1271_join(struct wl1271 *wl, bool set_assoc)
* One of the side effects of the JOIN command is that is clears * One of the side effects of the JOIN command is that is clears
* WPA/WPA2 keys from the chipset. Performing a JOIN while associated * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
* to a WPA/WPA2 access point will therefore kill the data-path. * to a WPA/WPA2 access point will therefore kill the data-path.
* Currently there is no supported scenario for JOIN during * Currently the only valid scenario for JOIN during association
* association - if it becomes a supported scenario, the WPA/WPA2 keys * is on roaming, in which case we will also be given new keys.
* must be handled somehow. * Keep the below message for now, unless it starts bothering
* * users who really like to roam a lot :)
*/ */
if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
wl1271_info("JOIN while associated."); wl1271_info("JOIN while associated.");
...@@ -1490,7 +1675,7 @@ static int wl1271_unjoin(struct wl1271 *wl) ...@@ -1490,7 +1675,7 @@ static int wl1271_unjoin(struct wl1271 *wl)
clear_bit(WL1271_FLAG_JOINED, &wl->flags); clear_bit(WL1271_FLAG_JOINED, &wl->flags);
memset(wl->bssid, 0, ETH_ALEN); memset(wl->bssid, 0, ETH_ALEN);
/* stop filterting packets based on bssid */ /* stop filtering packets based on bssid */
wl1271_configure_filters(wl, FIF_OTHER_BSS); wl1271_configure_filters(wl, FIF_OTHER_BSS);
out: out:
...@@ -1569,7 +1754,12 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) ...@@ -1569,7 +1754,12 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF)) { if (unlikely(wl->state == WL1271_STATE_OFF)) {
ret = -EAGAIN; /* we support configuring the channel and band while off */
if ((changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
wl->band = conf->channel->band;
wl->channel = channel;
}
goto out; goto out;
} }
...@@ -2650,32 +2840,31 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, ...@@ -2650,32 +2840,31 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY; conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY;
conf_tid->apsd_conf[0] = 0; conf_tid->apsd_conf[0] = 0;
conf_tid->apsd_conf[1] = 0; conf_tid->apsd_conf[1] = 0;
} else { goto out;
ret = wl1271_ps_elp_wakeup(wl); }
if (ret < 0)
goto out;
/* ret = wl1271_ps_elp_wakeup(wl);
* the txop is confed in units of 32us by the mac80211, if (ret < 0)
* we need us goto out;
*/
ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
params->cw_min, params->cw_max,
params->aifs, params->txop << 5);
if (ret < 0)
goto out_sleep;
ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue), /*
CONF_CHANNEL_TYPE_EDCF, * the txop is confed in units of 32us by the mac80211,
wl1271_tx_get_queue(queue), * we need us
ps_scheme, CONF_ACK_POLICY_LEGACY, */
0, 0); ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
if (ret < 0) params->cw_min, params->cw_max,
goto out_sleep; params->aifs, params->txop << 5);
if (ret < 0)
goto out_sleep;
ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
CONF_CHANNEL_TYPE_EDCF,
wl1271_tx_get_queue(queue),
ps_scheme, CONF_ACK_POLICY_LEGACY,
0, 0);
out_sleep: out_sleep:
wl1271_ps_elp_sleep(wl); wl1271_ps_elp_sleep(wl);
}
out: out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
...@@ -2847,10 +3036,11 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw, ...@@ -2847,10 +3036,11 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
return ret; return ret;
} }
int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
enum ieee80211_ampdu_mlme_action action, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid, u16 *ssn, enum ieee80211_ampdu_mlme_action action,
u8 buf_size) struct ieee80211_sta *sta, u16 tid, u16 *ssn,
u8 buf_size)
{ {
struct wl1271 *wl = hw->priv; struct wl1271 *wl = hw->priv;
int ret; int ret;
...@@ -3003,7 +3193,8 @@ static const u8 wl1271_rate_to_idx_2ghz[] = { ...@@ -3003,7 +3193,8 @@ static const u8 wl1271_rate_to_idx_2ghz[] = {
#ifdef CONFIG_WL12XX_HT #ifdef CONFIG_WL12XX_HT
#define WL12XX_HT_CAP { \ #define WL12XX_HT_CAP { \
.cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20, \ .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | \
(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), \
.ht_supported = true, \ .ht_supported = true, \
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, \ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, \
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \
...@@ -3207,8 +3398,7 @@ static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev, ...@@ -3207,8 +3398,7 @@ static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
unsigned long res; unsigned long res;
int ret; int ret;
ret = strict_strtoul(buf, 10, &res); ret = kstrtoul(buf, 10, &res);
if (ret < 0) { if (ret < 0) {
wl1271_warning("incorrect value written to bt_coex_mode"); wl1271_warning("incorrect value written to bt_coex_mode");
return count; return count;
...@@ -3273,7 +3463,11 @@ int wl1271_register_hw(struct wl1271 *wl) ...@@ -3273,7 +3463,11 @@ int wl1271_register_hw(struct wl1271 *wl)
ret = wl1271_fetch_nvs(wl); ret = wl1271_fetch_nvs(wl);
if (ret == 0) { if (ret == 0) {
u8 *nvs_ptr = (u8 *)wl->nvs->nvs; /* NOTE: The wl->nvs->nvs element must be first, in
* order to simplify the casting, we assume it is at
* the beginning of the wl->nvs structure.
*/
u8 *nvs_ptr = (u8 *)wl->nvs;
wl->mac_addr[0] = nvs_ptr[11]; wl->mac_addr[0] = nvs_ptr[11];
wl->mac_addr[1] = nvs_ptr[10]; wl->mac_addr[1] = nvs_ptr[10];
...@@ -3358,6 +3552,10 @@ int wl1271_init_ieee80211(struct wl1271 *wl) ...@@ -3358,6 +3552,10 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
sizeof(struct ieee80211_header); sizeof(struct ieee80211_header);
/* make sure all our channels fit in the scanned_ch bitmask */
BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
ARRAY_SIZE(wl1271_channels_5ghz) >
WL1271_MAX_CHANNELS);
/* /*
* We keep local copies of the band structs because we need to * We keep local copies of the band structs because we need to
* modify them on a per-device basis. * modify them on a per-device basis.
...@@ -3458,6 +3656,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) ...@@ -3458,6 +3656,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->ap_ps_map = 0; wl->ap_ps_map = 0;
wl->ap_fw_ps_map = 0; wl->ap_fw_ps_map = 0;
wl->quirks = 0; wl->quirks = 0;
wl->platform_quirks = 0;
memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
for (i = 0; i < ACX_TX_DESCRIPTORS; i++) for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
...@@ -3478,11 +3677,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void) ...@@ -3478,11 +3677,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
goto err_hw; goto err_hw;
} }
wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
if (!wl->dummy_packet) {
ret = -ENOMEM;
goto err_aggr;
}
/* Register platform device */ /* Register platform device */
ret = platform_device_register(wl->plat_dev); ret = platform_device_register(wl->plat_dev);
if (ret) { if (ret) {
wl1271_error("couldn't register platform device"); wl1271_error("couldn't register platform device");
goto err_aggr; goto err_dummy_packet;
} }
dev_set_drvdata(&wl->plat_dev->dev, wl); dev_set_drvdata(&wl->plat_dev->dev, wl);
...@@ -3508,6 +3713,9 @@ struct ieee80211_hw *wl1271_alloc_hw(void) ...@@ -3508,6 +3713,9 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
err_platform: err_platform:
platform_device_unregister(wl->plat_dev); platform_device_unregister(wl->plat_dev);
err_dummy_packet:
dev_kfree_skb(wl->dummy_packet);
err_aggr: err_aggr:
free_pages((unsigned long)wl->aggr_buf, order); free_pages((unsigned long)wl->aggr_buf, order);
...@@ -3527,6 +3735,7 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw); ...@@ -3527,6 +3735,7 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
int wl1271_free_hw(struct wl1271 *wl) int wl1271_free_hw(struct wl1271 *wl)
{ {
platform_device_unregister(wl->plat_dev); platform_device_unregister(wl->plat_dev);
dev_kfree_skb(wl->dummy_packet);
free_pages((unsigned long)wl->aggr_buf, free_pages((unsigned long)wl->aggr_buf,
get_order(WL1271_AGGR_BUFFER_SIZE)); get_order(WL1271_AGGR_BUFFER_SIZE));
kfree(wl->plat_dev); kfree(wl->plat_dev);
......
...@@ -149,9 +149,6 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, ...@@ -149,9 +149,6 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
case STATION_ACTIVE_MODE: case STATION_ACTIVE_MODE:
default: default:
wl1271_debug(DEBUG_PSM, "leaving psm"); wl1271_debug(DEBUG_PSM, "leaving psm");
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
return ret;
/* disable beacon early termination */ /* disable beacon early termination */
ret = wl1271_acx_bet_enable(wl, false); ret = wl1271_acx_bet_enable(wl, false);
......
...@@ -207,6 +207,8 @@ ...@@ -207,6 +207,8 @@
#define CHIP_ID_1271_PG10 (0x4030101) #define CHIP_ID_1271_PG10 (0x4030101)
#define CHIP_ID_1271_PG20 (0x4030111) #define CHIP_ID_1271_PG20 (0x4030111)
#define CHIP_ID_1283_PG10 (0x05030101)
#define CHIP_ID_1283_PG20 (0x05030111)
#define ENABLE (REGISTERS_BASE + 0x5450) #define ENABLE (REGISTERS_BASE + 0x5450)
...@@ -452,24 +454,11 @@ ...@@ -452,24 +454,11 @@
#define HI_CFG_UART_TX_OUT_GPIO_14 0x00000200 #define HI_CFG_UART_TX_OUT_GPIO_14 0x00000200
#define HI_CFG_UART_TX_OUT_GPIO_7 0x00000400 #define HI_CFG_UART_TX_OUT_GPIO_7 0x00000400
/*
* NOTE: USE_ACTIVE_HIGH compilation flag should be defined in makefile
* for platforms using active high interrupt level
*/
#ifdef USE_ACTIVE_HIGH
#define HI_CFG_DEF_VAL \ #define HI_CFG_DEF_VAL \
(HI_CFG_UART_ENABLE | \ (HI_CFG_UART_ENABLE | \
HI_CFG_RST232_ENABLE | \ HI_CFG_RST232_ENABLE | \
HI_CFG_CLOCK_REQ_SELECT | \ HI_CFG_CLOCK_REQ_SELECT | \
HI_CFG_HOST_INT_ENABLE) HI_CFG_HOST_INT_ENABLE)
#else
#define HI_CFG_DEF_VAL \
(HI_CFG_UART_ENABLE | \
HI_CFG_RST232_ENABLE | \
HI_CFG_CLOCK_REQ_SELECT | \
HI_CFG_HOST_INT_ENABLE)
#endif
#define REF_FREQ_19_2 0 #define REF_FREQ_19_2 0
#define REF_FREQ_26_0 1 #define REF_FREQ_26_0 1
......
...@@ -48,18 +48,14 @@ static void wl1271_rx_status(struct wl1271 *wl, ...@@ -48,18 +48,14 @@ static void wl1271_rx_status(struct wl1271 *wl,
struct ieee80211_rx_status *status, struct ieee80211_rx_status *status,
u8 beacon) u8 beacon)
{ {
enum ieee80211_band desc_band;
memset(status, 0, sizeof(struct ieee80211_rx_status)); memset(status, 0, sizeof(struct ieee80211_rx_status));
status->band = wl->band;
if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == WL1271_RX_DESC_BAND_BG) if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == WL1271_RX_DESC_BAND_BG)
desc_band = IEEE80211_BAND_2GHZ; status->band = IEEE80211_BAND_2GHZ;
else else
desc_band = IEEE80211_BAND_5GHZ; status->band = IEEE80211_BAND_5GHZ;
status->rate_idx = wl1271_rate_to_idx(desc->rate, desc_band); status->rate_idx = wl1271_rate_to_idx(desc->rate, status->band);
#ifdef CONFIG_WL12XX_HT #ifdef CONFIG_WL12XX_HT
/* 11n support */ /* 11n support */
...@@ -76,7 +72,8 @@ static void wl1271_rx_status(struct wl1271 *wl, ...@@ -76,7 +72,8 @@ static void wl1271_rx_status(struct wl1271 *wl,
*/ */
wl->noise = desc->rssi - (desc->snr >> 1); wl->noise = desc->rssi - (desc->snr >> 1);
status->freq = ieee80211_channel_to_frequency(desc->channel, desc_band); status->freq = ieee80211_channel_to_frequency(desc->channel,
status->band);
if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) { if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) {
status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED;
...@@ -163,18 +160,25 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) ...@@ -163,18 +160,25 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status)
break; break;
} }
/* if (wl->chip.id != CHIP_ID_1283_PG20) {
* Choose the block we want to read /*
* For aggregated packets, only the first memory block should * Choose the block we want to read
* be retrieved. The FW takes care of the rest. * For aggregated packets, only the first memory block
*/ * should be retrieved. The FW takes care of the rest.
mem_block = wl1271_rx_get_mem_block(status, drv_rx_counter); */
wl->rx_mem_pool_addr.addr = (mem_block << 8) + mem_block = wl1271_rx_get_mem_block(status,
le32_to_cpu(wl_mem_map->packet_memory_pool_start); drv_rx_counter);
wl->rx_mem_pool_addr.addr_extra =
wl->rx_mem_pool_addr.addr + 4; wl->rx_mem_pool_addr.addr = (mem_block << 8) +
wl1271_write(wl, WL1271_SLV_REG_DATA, &wl->rx_mem_pool_addr, le32_to_cpu(wl_mem_map->packet_memory_pool_start);
sizeof(wl->rx_mem_pool_addr), false);
wl->rx_mem_pool_addr.addr_extra =
wl->rx_mem_pool_addr.addr + 4;
wl1271_write(wl, WL1271_SLV_REG_DATA,
&wl->rx_mem_pool_addr,
sizeof(wl->rx_mem_pool_addr), false);
}
/* Read all available packets at once */ /* Read all available packets at once */
wl1271_read(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf, wl1271_read(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf,
......
...@@ -48,8 +48,7 @@ void wl1271_scan_complete_work(struct work_struct *work) ...@@ -48,8 +48,7 @@ void wl1271_scan_complete_work(struct work_struct *work)
goto out; goto out;
wl->scan.state = WL1271_SCAN_STATE_IDLE; wl->scan.state = WL1271_SCAN_STATE_IDLE;
kfree(wl->scan.scanned_ch); memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
wl->scan.scanned_ch = NULL;
wl->scan.req = NULL; wl->scan.req = NULL;
ieee80211_scan_completed(wl->hw, false); ieee80211_scan_completed(wl->hw, false);
...@@ -87,7 +86,7 @@ static int wl1271_get_scan_channels(struct wl1271 *wl, ...@@ -87,7 +86,7 @@ static int wl1271_get_scan_channels(struct wl1271 *wl,
flags = req->channels[i]->flags; flags = req->channels[i]->flags;
if (!wl->scan.scanned_ch[i] && if (!test_bit(i, wl->scan.scanned_ch) &&
!(flags & IEEE80211_CHAN_DISABLED) && !(flags & IEEE80211_CHAN_DISABLED) &&
((!!(flags & IEEE80211_CHAN_PASSIVE_SCAN)) == passive) && ((!!(flags & IEEE80211_CHAN_PASSIVE_SCAN)) == passive) &&
(req->channels[i]->band == band)) { (req->channels[i]->band == band)) {
...@@ -124,7 +123,7 @@ static int wl1271_get_scan_channels(struct wl1271 *wl, ...@@ -124,7 +123,7 @@ static int wl1271_get_scan_channels(struct wl1271 *wl,
memset(&channels[j].bssid_msb, 0xff, 2); memset(&channels[j].bssid_msb, 0xff, 2);
/* Mark the channels we already used */ /* Mark the channels we already used */
wl->scan.scanned_ch[i] = true; set_bit(i, wl->scan.scanned_ch);
j++; j++;
} }
...@@ -291,6 +290,12 @@ void wl1271_scan_stm(struct wl1271 *wl) ...@@ -291,6 +290,12 @@ void wl1271_scan_stm(struct wl1271 *wl)
int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
struct cfg80211_scan_request *req) struct cfg80211_scan_request *req)
{ {
/*
* cfg80211 should guarantee that we don't get more channels
* than what we have registered.
*/
BUG_ON(req->n_channels > WL1271_MAX_CHANNELS);
if (wl->scan.state != WL1271_SCAN_STATE_IDLE) if (wl->scan.state != WL1271_SCAN_STATE_IDLE)
return -EBUSY; return -EBUSY;
...@@ -304,10 +309,8 @@ int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, ...@@ -304,10 +309,8 @@ int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
} }
wl->scan.req = req; wl->scan.req = req;
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
wl->scan.scanned_ch = kcalloc(req->n_channels,
sizeof(*wl->scan.scanned_ch),
GFP_KERNEL);
/* we assume failure so that timeout scenarios are handled correctly */ /* we assume failure so that timeout scenarios are handled correctly */
wl->scan.failed = true; wl->scan.failed = true;
ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
......
...@@ -51,6 +51,13 @@ static const struct sdio_device_id wl1271_devices[] = { ...@@ -51,6 +51,13 @@ static const struct sdio_device_id wl1271_devices[] = {
}; };
MODULE_DEVICE_TABLE(sdio, wl1271_devices); MODULE_DEVICE_TABLE(sdio, wl1271_devices);
static void wl1271_sdio_set_block_size(struct wl1271 *wl, unsigned int blksz)
{
sdio_claim_host(wl->if_priv);
sdio_set_block_size(wl->if_priv, blksz);
sdio_release_host(wl->if_priv);
}
static inline struct sdio_func *wl_to_func(struct wl1271 *wl) static inline struct sdio_func *wl_to_func(struct wl1271 *wl)
{ {
return wl->if_priv; return wl->if_priv;
...@@ -203,7 +210,8 @@ static struct wl1271_if_operations sdio_ops = { ...@@ -203,7 +210,8 @@ static struct wl1271_if_operations sdio_ops = {
.power = wl1271_sdio_set_power, .power = wl1271_sdio_set_power,
.dev = wl1271_sdio_wl_to_dev, .dev = wl1271_sdio_wl_to_dev,
.enable_irq = wl1271_sdio_enable_interrupts, .enable_irq = wl1271_sdio_enable_interrupts,
.disable_irq = wl1271_sdio_disable_interrupts .disable_irq = wl1271_sdio_disable_interrupts,
.set_block_size = wl1271_sdio_set_block_size,
}; };
static int __devinit wl1271_probe(struct sdio_func *func, static int __devinit wl1271_probe(struct sdio_func *func,
...@@ -212,6 +220,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, ...@@ -212,6 +220,7 @@ static int __devinit wl1271_probe(struct sdio_func *func,
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
const struct wl12xx_platform_data *wlan_data; const struct wl12xx_platform_data *wlan_data;
struct wl1271 *wl; struct wl1271 *wl;
unsigned long irqflags;
int ret; int ret;
/* We are only able to handle the wlan function */ /* We are only able to handle the wlan function */
...@@ -230,6 +239,9 @@ static int __devinit wl1271_probe(struct sdio_func *func, ...@@ -230,6 +239,9 @@ static int __devinit wl1271_probe(struct sdio_func *func,
/* Grab access to FN0 for ELP reg. */ /* Grab access to FN0 for ELP reg. */
func->card->quirks |= MMC_QUIRK_LENIENT_FN0; func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
/* Use block mode for transferring over one block size of data */
func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
wlan_data = wl12xx_get_platform_data(); wlan_data = wl12xx_get_platform_data();
if (IS_ERR(wlan_data)) { if (IS_ERR(wlan_data)) {
ret = PTR_ERR(wlan_data); ret = PTR_ERR(wlan_data);
...@@ -239,9 +251,16 @@ static int __devinit wl1271_probe(struct sdio_func *func, ...@@ -239,9 +251,16 @@ static int __devinit wl1271_probe(struct sdio_func *func,
wl->irq = wlan_data->irq; wl->irq = wlan_data->irq;
wl->ref_clock = wlan_data->board_ref_clock; wl->ref_clock = wlan_data->board_ref_clock;
wl->tcxo_clock = wlan_data->board_tcxo_clock;
wl->platform_quirks = wlan_data->platform_quirks;
if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
irqflags = IRQF_TRIGGER_RISING;
else
irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq, ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, irqflags,
DRIVER_NAME, wl); DRIVER_NAME, wl);
if (ret < 0) { if (ret < 0) {
wl1271_error("request_irq() failed: %d", ret); wl1271_error("request_irq() failed: %d", ret);
...@@ -343,4 +362,6 @@ MODULE_LICENSE("GPL"); ...@@ -343,4 +362,6 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
MODULE_FIRMWARE(WL1271_FW_NAME); MODULE_FIRMWARE(WL1271_FW_NAME);
MODULE_FIRMWARE(WL1271_AP_FW_NAME); MODULE_FIRMWARE(WL128X_FW_NAME);
MODULE_FIRMWARE(WL127X_AP_FW_NAME);
MODULE_FIRMWARE(WL128X_AP_FW_NAME);
...@@ -189,7 +189,12 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) ...@@ -189,7 +189,12 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
const struct firmware *fw; const struct firmware *fw;
int ret; int ret;
ret = request_firmware(&fw, WL1271_FW_NAME, wl1271_wl_to_dev(wl)); if (wl->chip.id == CHIP_ID_1283_PG20)
ret = request_firmware(&fw, WL128X_FW_NAME,
wl1271_wl_to_dev(wl));
else
ret = request_firmware(&fw, WL1271_FW_NAME,
wl1271_wl_to_dev(wl));
if (ret < 0) { if (ret < 0) {
wl1271_error("could not get firmware: %d", ret); wl1271_error("could not get firmware: %d", ret);
...@@ -227,14 +232,14 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) ...@@ -227,14 +232,14 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
const struct firmware *fw; const struct firmware *fw;
int ret; int ret;
ret = request_firmware(&fw, WL1271_NVS_NAME, wl1271_wl_to_dev(wl)); ret = request_firmware(&fw, WL12XX_NVS_NAME, wl1271_wl_to_dev(wl));
if (ret < 0) { if (ret < 0) {
wl1271_error("could not get nvs file: %d", ret); wl1271_error("could not get nvs file: %d", ret);
return ret; return ret;
} }
wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL); wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
if (!wl->nvs) { if (!wl->nvs) {
wl1271_error("could not allocate memory for the nvs file"); wl1271_error("could not allocate memory for the nvs file");
...@@ -288,6 +293,11 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ...@@ -288,6 +293,11 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
wl1271_notice("chip id 0x%x (1271 PG20)", wl1271_notice("chip id 0x%x (1271 PG20)",
wl->chip.id); wl->chip.id);
break; break;
case CHIP_ID_1283_PG20:
wl1271_notice("chip id 0x%x (1283 PG20)",
wl->chip.id);
break;
case CHIP_ID_1283_PG10:
default: default:
wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
return -ENODEV; return -ENODEV;
...@@ -407,6 +417,9 @@ static int __devinit wl1271_probe(struct sdio_func *func, ...@@ -407,6 +417,9 @@ static int __devinit wl1271_probe(struct sdio_func *func,
/* Grab access to FN0 for ELP reg. */ /* Grab access to FN0 for ELP reg. */
func->card->quirks |= MMC_QUIRK_LENIENT_FN0; func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
/* Use block mode for transferring over one block size of data */
func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
wlan_data = wl12xx_get_platform_data(); wlan_data = wl12xx_get_platform_data();
if (IS_ERR(wlan_data)) { if (IS_ERR(wlan_data)) {
ret = PTR_ERR(wlan_data); ret = PTR_ERR(wlan_data);
...@@ -416,6 +429,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, ...@@ -416,6 +429,7 @@ static int __devinit wl1271_probe(struct sdio_func *func,
wl->irq = wlan_data->irq; wl->irq = wlan_data->irq;
wl->ref_clock = wlan_data->board_ref_clock; wl->ref_clock = wlan_data->board_ref_clock;
wl->tcxo_clock = wlan_data->board_tcxo_clock;
sdio_set_drvdata(func, wl_test); sdio_set_drvdata(func, wl_test);
......
...@@ -355,7 +355,8 @@ static struct wl1271_if_operations spi_ops = { ...@@ -355,7 +355,8 @@ static struct wl1271_if_operations spi_ops = {
.power = wl1271_spi_set_power, .power = wl1271_spi_set_power,
.dev = wl1271_spi_wl_to_dev, .dev = wl1271_spi_wl_to_dev,
.enable_irq = wl1271_spi_enable_interrupts, .enable_irq = wl1271_spi_enable_interrupts,
.disable_irq = wl1271_spi_disable_interrupts .disable_irq = wl1271_spi_disable_interrupts,
.set_block_size = NULL,
}; };
static int __devinit wl1271_probe(struct spi_device *spi) static int __devinit wl1271_probe(struct spi_device *spi)
...@@ -363,6 +364,7 @@ static int __devinit wl1271_probe(struct spi_device *spi) ...@@ -363,6 +364,7 @@ static int __devinit wl1271_probe(struct spi_device *spi)
struct wl12xx_platform_data *pdata; struct wl12xx_platform_data *pdata;
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
struct wl1271 *wl; struct wl1271 *wl;
unsigned long irqflags;
int ret; int ret;
pdata = spi->dev.platform_data; pdata = spi->dev.platform_data;
...@@ -400,6 +402,13 @@ static int __devinit wl1271_probe(struct spi_device *spi) ...@@ -400,6 +402,13 @@ static int __devinit wl1271_probe(struct spi_device *spi)
} }
wl->ref_clock = pdata->board_ref_clock; wl->ref_clock = pdata->board_ref_clock;
wl->tcxo_clock = pdata->board_tcxo_clock;
wl->platform_quirks = pdata->platform_quirks;
if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
irqflags = IRQF_TRIGGER_RISING;
else
irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
wl->irq = spi->irq; wl->irq = spi->irq;
if (wl->irq < 0) { if (wl->irq < 0) {
...@@ -409,7 +418,7 @@ static int __devinit wl1271_probe(struct spi_device *spi) ...@@ -409,7 +418,7 @@ static int __devinit wl1271_probe(struct spi_device *spi)
} }
ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq, ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, irqflags,
DRIVER_NAME, wl); DRIVER_NAME, wl);
if (ret < 0) { if (ret < 0) {
wl1271_error("request_irq() failed: %d", ret); wl1271_error("request_irq() failed: %d", ret);
...@@ -490,5 +499,7 @@ MODULE_LICENSE("GPL"); ...@@ -490,5 +499,7 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
MODULE_FIRMWARE(WL1271_FW_NAME); MODULE_FIRMWARE(WL1271_FW_NAME);
MODULE_FIRMWARE(WL1271_AP_FW_NAME); MODULE_FIRMWARE(WL128X_FW_NAME);
MODULE_FIRMWARE(WL127X_AP_FW_NAME);
MODULE_FIRMWARE(WL128X_AP_FW_NAME);
MODULE_ALIAS("spi:wl1271"); MODULE_ALIAS("spi:wl1271");
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "wl12xx.h" #include "wl12xx.h"
#include "acx.h" #include "acx.h"
#include "reg.h"
#define WL1271_TM_MAX_DATA_LENGTH 1024 #define WL1271_TM_MAX_DATA_LENGTH 1024
...@@ -204,7 +205,10 @@ static int wl1271_tm_cmd_nvs_push(struct wl1271 *wl, struct nlattr *tb[]) ...@@ -204,7 +205,10 @@ static int wl1271_tm_cmd_nvs_push(struct wl1271 *wl, struct nlattr *tb[])
kfree(wl->nvs); kfree(wl->nvs);
if (len != sizeof(struct wl1271_nvs_file)) if ((wl->chip.id == CHIP_ID_1283_PG20) &&
(len != sizeof(struct wl128x_nvs_file)))
return -EINVAL;
else if (len != sizeof(struct wl1271_nvs_file))
return -EINVAL; return -EINVAL;
wl->nvs = kzalloc(len, GFP_KERNEL); wl->nvs = kzalloc(len, GFP_KERNEL);
......
...@@ -70,6 +70,28 @@ static void wl1271_free_tx_id(struct wl1271 *wl, int id) ...@@ -70,6 +70,28 @@ static void wl1271_free_tx_id(struct wl1271 *wl, int id)
} }
} }
static int wl1271_tx_update_filters(struct wl1271 *wl,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr;
hdr = (struct ieee80211_hdr *)(skb->data +
sizeof(struct wl1271_tx_hw_descr));
/*
* stop bssid-based filtering before transmitting authentication
* requests. this way the hw will never drop authentication
* responses coming from BSSIDs it isn't familiar with (e.g. on
* roaming)
*/
if (!ieee80211_is_auth(hdr->frame_control))
return 0;
wl1271_configure_filters(wl, FIF_OTHER_BSS);
return wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
}
static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl, static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
struct sk_buff *skb) struct sk_buff *skb)
{ {
...@@ -127,13 +149,29 @@ u8 wl1271_tx_get_hlid(struct sk_buff *skb) ...@@ -127,13 +149,29 @@ u8 wl1271_tx_get_hlid(struct sk_buff *skb)
} }
} }
static unsigned int wl12xx_calc_packet_alignment(struct wl1271 *wl,
unsigned int packet_length)
{
if (wl->quirks & WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT)
return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE);
else
return ALIGN(packet_length, WL1271_TX_ALIGN_TO);
}
static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
u32 buf_offset, u8 hlid) u32 buf_offset, u8 hlid)
{ {
struct wl1271_tx_hw_descr *desc; struct wl1271_tx_hw_descr *desc;
u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra; u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra;
u32 len;
u32 total_blocks; u32 total_blocks;
int id, ret = -EBUSY; int id, ret = -EBUSY;
u32 spare_blocks;
if (unlikely(wl->quirks & WL12XX_QUIRK_USE_2_SPARE_BLOCKS))
spare_blocks = 2;
else
spare_blocks = 1;
if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE) if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE)
return -EAGAIN; return -EAGAIN;
...@@ -145,17 +183,27 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, ...@@ -145,17 +183,27 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
/* approximate the number of blocks required for this packet /* approximate the number of blocks required for this packet
in the firmware */ in the firmware */
total_blocks = total_len + TX_HW_BLOCK_SIZE - 1; len = wl12xx_calc_packet_alignment(wl, total_len);
total_blocks = total_blocks / TX_HW_BLOCK_SIZE + TX_HW_BLOCK_SPARE;
total_blocks = (len + TX_HW_BLOCK_SIZE - 1) / TX_HW_BLOCK_SIZE +
spare_blocks;
if (total_blocks <= wl->tx_blocks_available) { if (total_blocks <= wl->tx_blocks_available) {
desc = (struct wl1271_tx_hw_descr *)skb_push( desc = (struct wl1271_tx_hw_descr *)skb_push(
skb, total_len - skb->len); skb, total_len - skb->len);
desc->extra_mem_blocks = TX_HW_BLOCK_SPARE; /* HW descriptor fields change between wl127x and wl128x */
desc->total_mem_blocks = total_blocks; if (wl->chip.id == CHIP_ID_1283_PG20) {
desc->wl128x_mem.total_mem_blocks = total_blocks;
} else {
desc->wl127x_mem.extra_blocks = spare_blocks;
desc->wl127x_mem.total_mem_blocks = total_blocks;
}
desc->id = id; desc->id = id;
wl->tx_blocks_available -= total_blocks; wl->tx_blocks_available -= total_blocks;
wl->tx_allocated_blocks += total_blocks;
if (wl->bss_type == BSS_TYPE_AP_BSS) if (wl->bss_type == BSS_TYPE_AP_BSS)
wl->links[hlid].allocated_blks += total_blocks; wl->links[hlid].allocated_blks += total_blocks;
...@@ -172,13 +220,18 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, ...@@ -172,13 +220,18 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
return ret; return ret;
} }
static bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb)
{
return wl->dummy_packet == skb;
}
static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
u32 extra, struct ieee80211_tx_info *control, u32 extra, struct ieee80211_tx_info *control,
u8 hlid) u8 hlid)
{ {
struct timespec ts; struct timespec ts;
struct wl1271_tx_hw_descr *desc; struct wl1271_tx_hw_descr *desc;
int pad, ac, rate_idx; int aligned_len, ac, rate_idx;
s64 hosttime; s64 hosttime;
u16 tx_attr; u16 tx_attr;
...@@ -202,12 +255,25 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, ...@@ -202,12 +255,25 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
else else
desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU); desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU);
/* configure the tx attributes */ /* queue */
tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER;
/* queue (we use same identifiers for tid's and ac's */
ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
desc->tid = ac; desc->tid = skb->priority;
if (wl12xx_is_dummy_packet(wl, skb)) {
/*
* FW expects the dummy packet to have an invalid session id -
* any session id that is different than the one set in the join
*/
tx_attr = ((~wl->session_counter) <<
TX_HW_ATTR_OFST_SESSION_COUNTER) &
TX_HW_ATTR_SESSION_COUNTER;
tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ;
} else {
/* configure the tx attributes */
tx_attr =
wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER;
}
if (wl->bss_type != BSS_TYPE_AP_BSS) { if (wl->bss_type != BSS_TYPE_AP_BSS) {
desc->aid = hlid; desc->aid = hlid;
...@@ -237,20 +303,37 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, ...@@ -237,20 +303,37 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY; tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY;
desc->reserved = 0; desc->reserved = 0;
/* align the length (and store in terms of words) */ aligned_len = wl12xx_calc_packet_alignment(wl, skb->len);
pad = ALIGN(skb->len, WL1271_TX_ALIGN_TO);
desc->length = cpu_to_le16(pad >> 2);
/* calculate number of padding bytes */ if (wl->chip.id == CHIP_ID_1283_PG20) {
pad = pad - skb->len; desc->wl128x_mem.extra_bytes = aligned_len - skb->len;
tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; desc->length = cpu_to_le16(aligned_len >> 2);
desc->tx_attr = cpu_to_le16(tx_attr); wl1271_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d "
"tx_attr: 0x%x len: %d life: %d mem: %d",
desc->hlid, tx_attr,
le16_to_cpu(desc->length),
le16_to_cpu(desc->life_time),
desc->wl128x_mem.total_mem_blocks);
} else {
int pad;
/* Store the aligned length in terms of words */
desc->length = cpu_to_le16(aligned_len >> 2);
/* calculate number of padding bytes */
pad = aligned_len - skb->len;
tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD;
wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d hlid: %d " wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d hlid: %d "
"tx_attr: 0x%x len: %d life: %d mem: %d", pad, desc->hlid, "tx_attr: 0x%x len: %d life: %d mem: %d", pad,
le16_to_cpu(desc->tx_attr), le16_to_cpu(desc->length), desc->hlid, tx_attr,
le16_to_cpu(desc->life_time), desc->total_mem_blocks); le16_to_cpu(desc->length),
le16_to_cpu(desc->life_time),
desc->wl127x_mem.total_mem_blocks);
}
desc->tx_attr = cpu_to_le16(tx_attr);
} }
/* caller must hold wl->mutex */ /* caller must hold wl->mutex */
...@@ -300,19 +383,29 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, ...@@ -300,19 +383,29 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
if (wl->bss_type == BSS_TYPE_AP_BSS) { if (wl->bss_type == BSS_TYPE_AP_BSS) {
wl1271_tx_ap_update_inconnection_sta(wl, skb); wl1271_tx_ap_update_inconnection_sta(wl, skb);
wl1271_tx_regulate_link(wl, hlid); wl1271_tx_regulate_link(wl, hlid);
} else {
wl1271_tx_update_filters(wl, skb);
} }
wl1271_tx_fill_hdr(wl, skb, extra, info, hlid); wl1271_tx_fill_hdr(wl, skb, extra, info, hlid);
/* /*
* The length of each packet is stored in terms of words. Thus, we must * The length of each packet is stored in terms of
* pad the skb data to make sure its length is aligned. * words. Thus, we must pad the skb data to make sure its
* The number of padding bytes is computed and set in wl1271_tx_fill_hdr * length is aligned. The number of padding bytes is computed
* and set in wl1271_tx_fill_hdr.
* In special cases, we want to align to a specific block size
* (eg. for wl128x with SDIO we align to 256).
*/ */
total_len = ALIGN(skb->len, WL1271_TX_ALIGN_TO); total_len = wl12xx_calc_packet_alignment(wl, skb->len);
memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len); memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len);
memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len); memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len);
/* Revert side effects in the dummy packet skb, so it can be reused */
if (wl12xx_is_dummy_packet(wl, skb))
skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
return total_len; return total_len;
} }
...@@ -425,10 +518,23 @@ static struct sk_buff *wl1271_ap_skb_dequeue(struct wl1271 *wl) ...@@ -425,10 +518,23 @@ static struct sk_buff *wl1271_ap_skb_dequeue(struct wl1271 *wl)
static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
{ {
unsigned long flags;
struct sk_buff *skb = NULL;
if (wl->bss_type == BSS_TYPE_AP_BSS) if (wl->bss_type == BSS_TYPE_AP_BSS)
return wl1271_ap_skb_dequeue(wl); skb = wl1271_ap_skb_dequeue(wl);
else
skb = wl1271_sta_skb_dequeue(wl);
if (!skb &&
test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) {
skb = wl->dummy_packet;
spin_lock_irqsave(&wl->wl_lock, flags);
wl->tx_queue_count--;
spin_unlock_irqrestore(&wl->wl_lock, flags);
}
return wl1271_sta_skb_dequeue(wl); return skb;
} }
static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb)
...@@ -436,7 +542,9 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) ...@@ -436,7 +542,9 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb)
unsigned long flags; unsigned long flags;
int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
if (wl->bss_type == BSS_TYPE_AP_BSS) { if (wl12xx_is_dummy_packet(wl, skb)) {
set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
} else if (wl->bss_type == BSS_TYPE_AP_BSS) {
u8 hlid = wl1271_tx_get_hlid(skb); u8 hlid = wl1271_tx_get_hlid(skb);
skb_queue_head(&wl->links[hlid].tx_queue[q], skb); skb_queue_head(&wl->links[hlid].tx_queue[q], skb);
...@@ -454,22 +562,14 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) ...@@ -454,22 +562,14 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb)
void wl1271_tx_work_locked(struct wl1271 *wl) void wl1271_tx_work_locked(struct wl1271 *wl)
{ {
struct sk_buff *skb; struct sk_buff *skb;
bool woken_up = false;
u32 buf_offset = 0; u32 buf_offset = 0;
bool sent_packets = false; bool sent_packets = false;
int ret; int ret;
if (unlikely(wl->state == WL1271_STATE_OFF)) if (unlikely(wl->state == WL1271_STATE_OFF))
goto out; return;
while ((skb = wl1271_skb_dequeue(wl))) { while ((skb = wl1271_skb_dequeue(wl))) {
if (!woken_up) {
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out_ack;
woken_up = true;
}
ret = wl1271_prepare_tx_frame(wl, skb, buf_offset); ret = wl1271_prepare_tx_frame(wl, skb, buf_offset);
if (ret == -EAGAIN) { if (ret == -EAGAIN) {
/* /*
...@@ -516,18 +616,22 @@ void wl1271_tx_work_locked(struct wl1271 *wl) ...@@ -516,18 +616,22 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
wl1271_handle_tx_low_watermark(wl); wl1271_handle_tx_low_watermark(wl);
} }
out:
if (woken_up)
wl1271_ps_elp_sleep(wl);
} }
void wl1271_tx_work(struct work_struct *work) void wl1271_tx_work(struct work_struct *work)
{ {
struct wl1271 *wl = container_of(work, struct wl1271, tx_work); struct wl1271 *wl = container_of(work, struct wl1271, tx_work);
int ret;
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
wl1271_tx_work_locked(wl); wl1271_tx_work_locked(wl);
wl1271_ps_elp_wakeup(wl);
out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
} }
...@@ -549,6 +653,11 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, ...@@ -549,6 +653,11 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
skb = wl->tx_frames[id]; skb = wl->tx_frames[id];
info = IEEE80211_SKB_CB(skb); info = IEEE80211_SKB_CB(skb);
if (wl12xx_is_dummy_packet(wl, skb)) {
wl1271_free_tx_id(wl, id);
return;
}
/* update the TX status info */ /* update the TX status info */
if (result->status == TX_SUCCESS) { if (result->status == TX_SUCCESS) {
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
...@@ -678,10 +787,13 @@ void wl1271_tx_reset(struct wl1271 *wl) ...@@ -678,10 +787,13 @@ void wl1271_tx_reset(struct wl1271 *wl)
while ((skb = skb_dequeue(&wl->tx_queue[i]))) { while ((skb = skb_dequeue(&wl->tx_queue[i]))) {
wl1271_debug(DEBUG_TX, "freeing skb 0x%p", wl1271_debug(DEBUG_TX, "freeing skb 0x%p",
skb); skb);
info = IEEE80211_SKB_CB(skb);
info->status.rates[0].idx = -1; if (!wl12xx_is_dummy_packet(wl, skb)) {
info->status.rates[0].count = 0; info = IEEE80211_SKB_CB(skb);
ieee80211_tx_status(wl->hw, skb); info->status.rates[0].idx = -1;
info->status.rates[0].count = 0;
ieee80211_tx_status(wl->hw, skb);
}
} }
} }
} }
...@@ -702,21 +814,27 @@ void wl1271_tx_reset(struct wl1271 *wl) ...@@ -702,21 +814,27 @@ void wl1271_tx_reset(struct wl1271 *wl)
wl1271_free_tx_id(wl, i); wl1271_free_tx_id(wl, i);
wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb);
/* Remove private headers before passing the skb to mac80211 */ if (!wl12xx_is_dummy_packet(wl, skb)) {
info = IEEE80211_SKB_CB(skb); /*
skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); * Remove private headers before passing the skb to
if (info->control.hw_key && * mac80211
info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { */
int hdrlen = ieee80211_get_hdrlen_from_skb(skb); info = IEEE80211_SKB_CB(skb);
memmove(skb->data + WL1271_TKIP_IV_SPACE, skb->data, skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
hdrlen); if (info->control.hw_key &&
skb_pull(skb, WL1271_TKIP_IV_SPACE); info->control.hw_key->cipher ==
} WLAN_CIPHER_SUITE_TKIP) {
int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
memmove(skb->data + WL1271_TKIP_IV_SPACE,
skb->data, hdrlen);
skb_pull(skb, WL1271_TKIP_IV_SPACE);
}
info->status.rates[0].idx = -1; info->status.rates[0].idx = -1;
info->status.rates[0].count = 0; info->status.rates[0].count = 0;
ieee80211_tx_status(wl->hw, skb); ieee80211_tx_status(wl->hw, skb);
}
} }
} }
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#ifndef __TX_H__ #ifndef __TX_H__
#define __TX_H__ #define __TX_H__
#define TX_HW_BLOCK_SPARE 2
#define TX_HW_BLOCK_SIZE 252 #define TX_HW_BLOCK_SIZE 252
#define TX_HW_MGMT_PKT_LIFETIME_TU 2000 #define TX_HW_MGMT_PKT_LIFETIME_TU 2000
...@@ -41,6 +40,7 @@ ...@@ -41,6 +40,7 @@
BIT(8) | BIT(9)) BIT(8) | BIT(9))
#define TX_HW_ATTR_LAST_WORD_PAD (BIT(10) | BIT(11)) #define TX_HW_ATTR_LAST_WORD_PAD (BIT(10) | BIT(11))
#define TX_HW_ATTR_TX_CMPLT_REQ BIT(12) #define TX_HW_ATTR_TX_CMPLT_REQ BIT(12)
#define TX_HW_ATTR_TX_DUMMY_REQ BIT(13)
#define TX_HW_ATTR_OFST_SAVE_RETRIES 0 #define TX_HW_ATTR_OFST_SAVE_RETRIES 0
#define TX_HW_ATTR_OFST_HEADER_PAD 1 #define TX_HW_ATTR_OFST_HEADER_PAD 1
...@@ -55,20 +55,60 @@ ...@@ -55,20 +55,60 @@
#define WL1271_TX_ALIGN_TO 4 #define WL1271_TX_ALIGN_TO 4
#define WL1271_TKIP_IV_SPACE 4 #define WL1271_TKIP_IV_SPACE 4
/* Used for management frames and dummy packets */
#define WL1271_TID_MGMT 7
struct wl127x_tx_mem {
/*
* Number of extra memory blocks to allocate for this packet
* in addition to the number of blocks derived from the packet
* length.
*/
u8 extra_blocks;
/*
* Total number of memory blocks allocated by the host for
* this packet. Must be equal or greater than the actual
* blocks number allocated by HW.
*/
u8 total_mem_blocks;
} __packed;
struct wl128x_tx_mem {
/*
* Total number of memory blocks allocated by the host for
* this packet.
*/
u8 total_mem_blocks;
/*
* Number of extra bytes, at the end of the frame. the host
* uses this padding to complete each frame to integer number
* of SDIO blocks.
*/
u8 extra_bytes;
} __packed;
/*
* On wl128x based devices, when TX packets are aggregated, each packet
* size must be aligned to the SDIO block size. The maximum block size
* is bounded by the type of the padded bytes field that is sent to the
* FW. Currently the type is u8, so the maximum block size is 256 bytes.
*/
#define WL12XX_BUS_BLOCK_SIZE min(512u, \
(1u << (8 * sizeof(((struct wl128x_tx_mem *) 0)->extra_bytes))))
struct wl1271_tx_hw_descr { struct wl1271_tx_hw_descr {
/* Length of packet in words, including descriptor+header+data */ /* Length of packet in words, including descriptor+header+data */
__le16 length; __le16 length;
/* Number of extra memory blocks to allocate for this packet in union {
addition to the number of blocks derived from the packet length */ struct wl127x_tx_mem wl127x_mem;
u8 extra_mem_blocks; struct wl128x_tx_mem wl128x_mem;
/* Total number of memory blocks allocated by the host for this packet. } __packed;
Must be equal or greater than the actual blocks number allocated by
HW!! */
u8 total_mem_blocks;
/* Device time (in us) when the packet arrived to the driver */ /* Device time (in us) when the packet arrived to the driver */
__le32 start_time; __le32 start_time;
/* Max delay in TUs until transmission. The last device time the /*
packet can be transmitted is: startTime+(1024*LifeTime) */ * Max delay in TUs until transmission. The last device time the
* packet can be transmitted is: start_time + (1024 * life_time)
*/
__le16 life_time; __le16 life_time;
/* Bitwise fields - see TX_ATTR... definitions above. */ /* Bitwise fields - see TX_ATTR... definitions above. */
__le16 tx_attr; __le16 tx_attr;
......
...@@ -131,9 +131,16 @@ extern u32 wl12xx_debug_level; ...@@ -131,9 +131,16 @@ extern u32 wl12xx_debug_level;
#define WL1271_FW_NAME "ti-connectivity/wl1271-fw-2.bin" #define WL1271_FW_NAME "ti-connectivity/wl1271-fw-2.bin"
#define WL1271_AP_FW_NAME "ti-connectivity/wl1271-fw-ap.bin" #define WL128X_FW_NAME "ti-connectivity/wl128x-fw.bin"
#define WL127X_AP_FW_NAME "ti-connectivity/wl1271-fw-ap.bin"
#define WL128X_AP_FW_NAME "ti-connectivity/wl128x-fw-ap.bin"
#define WL1271_NVS_NAME "ti-connectivity/wl1271-nvs.bin" /*
* wl127x and wl128x are using the same NVS file name. However, the
* ini parameters between them are different. The driver validates
* the correct NVS size in wl1271_boot_upload_nvs().
*/
#define WL12XX_NVS_NAME "ti-connectivity/wl1271-nvs.bin"
#define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff)) #define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff))
#define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff)) #define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff))
...@@ -200,13 +207,29 @@ struct wl1271_partition_set { ...@@ -200,13 +207,29 @@ struct wl1271_partition_set {
struct wl1271; struct wl1271;
#define WL12XX_NUM_FW_VER 5 enum {
FW_VER_CHIP,
FW_VER_IF_TYPE,
FW_VER_MAJOR,
FW_VER_SUBTYPE,
FW_VER_MINOR,
NUM_FW_VER
};
#define FW_VER_CHIP_WL127X 6
#define FW_VER_CHIP_WL128X 7
#define FW_VER_IF_TYPE_STA 1
#define FW_VER_IF_TYPE_AP 2
#define FW_VER_MINOR_1_SPARE_STA_MIN 58
#define FW_VER_MINOR_1_SPARE_AP_MIN 47
/* FIXME: I'm not sure about this structure name */
struct wl1271_chip { struct wl1271_chip {
u32 id; u32 id;
char fw_ver_str[ETHTOOL_BUSINFO_LEN]; char fw_ver_str[ETHTOOL_BUSINFO_LEN];
unsigned int fw_ver[WL12XX_NUM_FW_VER]; unsigned int fw_ver[NUM_FW_VER];
}; };
struct wl1271_stats { struct wl1271_stats {
...@@ -261,6 +284,8 @@ struct wl1271_fw_sta_status { ...@@ -261,6 +284,8 @@ struct wl1271_fw_sta_status {
u8 tx_total; u8 tx_total;
u8 reserved1; u8 reserved1;
__le16 reserved2; __le16 reserved2;
/* Total structure size is 68 bytes */
u32 padding;
} __packed; } __packed;
struct wl1271_fw_full_status { struct wl1271_fw_full_status {
...@@ -277,9 +302,10 @@ struct wl1271_rx_mem_pool_addr { ...@@ -277,9 +302,10 @@ struct wl1271_rx_mem_pool_addr {
u32 addr_extra; u32 addr_extra;
}; };
#define WL1271_MAX_CHANNELS 64
struct wl1271_scan { struct wl1271_scan {
struct cfg80211_scan_request *req; struct cfg80211_scan_request *req;
bool *scanned_ch; unsigned long scanned_ch[BITS_TO_LONGS(WL1271_MAX_CHANNELS)];
bool failed; bool failed;
u8 state; u8 state;
u8 ssid[IW_ESSID_MAX_SIZE+1]; u8 ssid[IW_ESSID_MAX_SIZE+1];
...@@ -297,6 +323,7 @@ struct wl1271_if_operations { ...@@ -297,6 +323,7 @@ struct wl1271_if_operations {
struct device* (*dev)(struct wl1271 *wl); struct device* (*dev)(struct wl1271 *wl);
void (*enable_irq)(struct wl1271 *wl); void (*enable_irq)(struct wl1271 *wl);
void (*disable_irq)(struct wl1271 *wl); void (*disable_irq)(struct wl1271 *wl);
void (*set_block_size) (struct wl1271 *wl, unsigned int blksz);
}; };
#define MAX_NUM_KEYS 14 #define MAX_NUM_KEYS 14
...@@ -327,7 +354,9 @@ enum wl12xx_flags { ...@@ -327,7 +354,9 @@ enum wl12xx_flags {
WL1271_FLAG_PSPOLL_FAILURE, WL1271_FLAG_PSPOLL_FAILURE,
WL1271_FLAG_STA_STATE_SENT, WL1271_FLAG_STA_STATE_SENT,
WL1271_FLAG_FW_TX_BUSY, WL1271_FLAG_FW_TX_BUSY,
WL1271_FLAG_AP_STARTED WL1271_FLAG_AP_STARTED,
WL1271_FLAG_IF_INITIALIZED,
WL1271_FLAG_DUMMY_PACKET_PENDING,
}; };
struct wl1271_link { struct wl1271_link {
...@@ -371,7 +400,7 @@ struct wl1271 { ...@@ -371,7 +400,7 @@ struct wl1271 {
u8 *fw; u8 *fw;
size_t fw_len; size_t fw_len;
u8 fw_bss_type; u8 fw_bss_type;
struct wl1271_nvs_file *nvs; void *nvs;
size_t nvs_len; size_t nvs_len;
s8 hw_pg_ver; s8 hw_pg_ver;
...@@ -389,6 +418,7 @@ struct wl1271 { ...@@ -389,6 +418,7 @@ struct wl1271 {
/* Accounting for allocated / available TX blocks on HW */ /* Accounting for allocated / available TX blocks on HW */
u32 tx_blocks_freed[NUM_TX_QUEUES]; u32 tx_blocks_freed[NUM_TX_QUEUES];
u32 tx_blocks_available; u32 tx_blocks_available;
u32 tx_allocated_blocks;
u32 tx_results_count; u32 tx_results_count;
/* Transmitted TX packets counter for chipset interface */ /* Transmitted TX packets counter for chipset interface */
...@@ -430,6 +460,9 @@ struct wl1271 { ...@@ -430,6 +460,9 @@ struct wl1271 {
/* Intermediate buffer, used for packet aggregation */ /* Intermediate buffer, used for packet aggregation */
u8 *aggr_buf; u8 *aggr_buf;
/* Reusable dummy packet template */
struct sk_buff *dummy_packet;
/* Network stack work */ /* Network stack work */
struct work_struct netstack_work; struct work_struct netstack_work;
...@@ -527,6 +560,8 @@ struct wl1271 { ...@@ -527,6 +560,8 @@ struct wl1271 {
bool ba_support; bool ba_support;
u8 ba_rx_bitmap; u8 ba_rx_bitmap;
int tcxo_clock;
/* /*
* AP-mode - links indexed by HLID. The global and broadcast links * AP-mode - links indexed by HLID. The global and broadcast links
* are always active. * are always active.
...@@ -544,6 +579,9 @@ struct wl1271 { ...@@ -544,6 +579,9 @@ struct wl1271 {
/* Quirks of specific hardware revisions */ /* Quirks of specific hardware revisions */
unsigned int quirks; unsigned int quirks;
/* Platform limitations */
unsigned int platform_quirks;
}; };
struct wl1271_station { struct wl1271_station {
...@@ -576,6 +614,15 @@ int wl1271_plt_stop(struct wl1271 *wl); ...@@ -576,6 +614,15 @@ int wl1271_plt_stop(struct wl1271 *wl);
/* Quirks */ /* Quirks */
/* Each RX/TX transaction requires an end-of-transaction transfer */ /* Each RX/TX transaction requires an end-of-transaction transfer */
#define WL12XX_QUIRK_END_OF_TRANSACTION BIT(0) #define WL12XX_QUIRK_END_OF_TRANSACTION BIT(0)
/*
* Older firmwares use 2 spare TX blocks
* (for STA < 6.1.3.50.58 or for AP < 6.2.0.0.47)
*/
#define WL12XX_QUIRK_USE_2_SPARE_BLOCKS BIT(1)
/* WL128X requires aggregated packets to be aligned to the SDIO block size */
#define WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT BIT(2)
#endif #endif
...@@ -24,12 +24,26 @@ ...@@ -24,12 +24,26 @@
#ifndef _LINUX_WL12XX_H #ifndef _LINUX_WL12XX_H
#define _LINUX_WL12XX_H #define _LINUX_WL12XX_H
/* The board reference clock values */ /* Reference clock values */
enum { enum {
WL12XX_REFCLOCK_19 = 0, /* 19.2 MHz */ WL12XX_REFCLOCK_19 = 0, /* 19.2 MHz */
WL12XX_REFCLOCK_26 = 1, /* 26 MHz */ WL12XX_REFCLOCK_26 = 1, /* 26 MHz */
WL12XX_REFCLOCK_38 = 2, /* 38.4 MHz */ WL12XX_REFCLOCK_38 = 2, /* 38.4 MHz */
WL12XX_REFCLOCK_54 = 3, /* 54 MHz */ WL12XX_REFCLOCK_52 = 3, /* 52 MHz */
WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */
WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */
};
/* TCXO clock values */
enum {
WL12XX_TCXOCLOCK_19_2 = 0, /* 19.2MHz */
WL12XX_TCXOCLOCK_26 = 1, /* 26 MHz */
WL12XX_TCXOCLOCK_38_4 = 2, /* 38.4MHz */
WL12XX_TCXOCLOCK_52 = 3, /* 52 MHz */
WL12XX_TCXOCLOCK_16_368 = 4, /* 16.368 MHz */
WL12XX_TCXOCLOCK_32_736 = 5, /* 32.736 MHz */
WL12XX_TCXOCLOCK_16_8 = 6, /* 16.8 MHz */
WL12XX_TCXOCLOCK_33_6 = 7, /* 33.6 MHz */
}; };
struct wl12xx_platform_data { struct wl12xx_platform_data {
...@@ -38,8 +52,13 @@ struct wl12xx_platform_data { ...@@ -38,8 +52,13 @@ struct wl12xx_platform_data {
int irq; int irq;
bool use_eeprom; bool use_eeprom;
int board_ref_clock; int board_ref_clock;
int board_tcxo_clock;
unsigned long platform_quirks;
}; };
/* Platform does not support level trigger interrupts */
#define WL12XX_PLATFORM_QUIRK_EDGE_IRQ BIT(0)
#ifdef CONFIG_WL12XX_PLATFORM_DATA #ifdef CONFIG_WL12XX_PLATFORM_DATA
int wl12xx_set_platform_data(const struct wl12xx_platform_data *data); int wl12xx_set_platform_data(const struct wl12xx_platform_data *data);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment