From 62c7d085e1f2a1f2b4d89560551eff18d703b3b1 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 Mar 2011 16:42:47 +0200 Subject: wl12xx: add new board_tcxo_clock element to the platform data This new value is a new type of clock setting that is used by wl128x chipsets. Signed-off-by: Luciano Coelho diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h index bebb8ef..eb8aaca 100644 --- a/include/linux/wl12xx.h +++ b/include/linux/wl12xx.h @@ -24,7 +24,7 @@ #ifndef _LINUX_WL12XX_H #define _LINUX_WL12XX_H -/* The board reference clock values */ +/* Reference clock values */ enum { WL12XX_REFCLOCK_19 = 0, /* 19.2 MHz */ WL12XX_REFCLOCK_26 = 1, /* 26 MHz */ @@ -32,12 +32,25 @@ enum { WL12XX_REFCLOCK_54 = 3, /* 54 MHz */ }; +/* 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 { void (*set_power)(bool enable); /* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */ int irq; bool use_eeprom; int board_ref_clock; + int board_tcxo_clock; }; #ifdef CONFIG_WL12XX_PLATFORM_DATA -- cgit v0.10.2 From b9b0fdead0e8d964a534e5b09f40d8bd4bf7dfe8 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:06 +0200 Subject: wl12xx: 1281/1283 support - move IRQ polarity In order to prevent overran of IRQ polarity via FW the polarity setting move after FW download and before IRQ enable. Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 6934dff..69fe870 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -585,13 +585,6 @@ int wl1271_load_firmware(struct wl1271 *wl) /* 6. read the EEPROM parameters */ 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 * to upload_fw) */ @@ -618,6 +611,13 @@ int wl1271_boot(struct wl1271 *wl) if (ret < 0) 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 */ wl1271_boot_enable_interrupts(wl); -- cgit v0.10.2 From 5aa42346bba2e385674eb1dd4019dfce4c2ef771 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:07 +0200 Subject: wl12xx: 1281/1283 support - Add Definitions Definitions to support wl128x: - New FW file name - Chip ID - New PLL Configuration Algorithm macros that will be used at wl128x boot stage - Rename NVS macro name: wl127x and wl128x are using the same NVS file name. However, the ini parameters between them are different. The driver will validate the correct NVS size in wl1271_boot_upload_nvs(). [Cleaned up some of the definitions. -- Luca] Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/boot.h b/drivers/net/wireless/wl12xx/boot.h index 17229b8..1f5ee31 100644 --- a/drivers/net/wireless/wl12xx/boot.h +++ b/drivers/net/wireless/wl12xx/boot.h @@ -74,4 +74,52 @@ struct wl1271_static_data { #define FREF_CLK_POLARITY_BITS 0xfffff8ff #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_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 */ +#define HCI_IO_DS_8MA 0 +#define HCI_IO_DS_4MA 1 /* default */ +#define HCI_IO_DS_6MA 2 +#define HCI_IO_DS_2MA 3 +/* end PLL configuration algorithm for wl128x */ + #endif diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 856a8a2..a00f22c 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1004,7 +1004,9 @@ enum { CONF_REF_CLK_19_2_E, CONF_REF_CLK_26_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 { diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 8b3c8d1..b0c4935 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -838,7 +838,7 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) const struct firmware *fw; 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) { wl1271_error("could not get nvs file: %d", ret); diff --git a/drivers/net/wireless/wl12xx/reg.h b/drivers/net/wireless/wl12xx/reg.h index 9909607..440a4ee 100644 --- a/drivers/net/wireless/wl12xx/reg.h +++ b/drivers/net/wireless/wl12xx/reg.h @@ -207,6 +207,8 @@ #define CHIP_ID_1271_PG10 (0x4030101) #define CHIP_ID_1271_PG20 (0x4030111) +#define CHIP_ID_1283_PG10 (0x05030101) +#define CHIP_ID_1283_PG20 (0x05030111) #define ENABLE (REGISTERS_BASE + 0x5450) @@ -452,24 +454,11 @@ #define HI_CFG_UART_TX_OUT_GPIO_14 0x00000200 #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 \ (HI_CFG_UART_ENABLE | \ HI_CFG_RST232_ENABLE | \ HI_CFG_CLOCK_REQ_SELECT | \ 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_26_0 1 diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index b1c7d03..1b6a1ad 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -343,4 +343,5 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); MODULE_FIRMWARE(WL1271_FW_NAME); +MODULE_FIRMWARE(WL128X_FW_NAME); MODULE_FIRMWARE(WL1271_AP_FW_NAME); diff --git a/drivers/net/wireless/wl12xx/sdio_test.c b/drivers/net/wireless/wl12xx/sdio_test.c index 9fcbd3d..01adf1b 100644 --- a/drivers/net/wireless/wl12xx/sdio_test.c +++ b/drivers/net/wireless/wl12xx/sdio_test.c @@ -227,7 +227,7 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) const struct firmware *fw; 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) { wl1271_error("could not get nvs file: %d", ret); diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index ffc745b..80295f5 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -490,5 +490,6 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); MODULE_FIRMWARE(WL1271_FW_NAME); +MODULE_FIRMWARE(WL128X_FW_NAME); MODULE_FIRMWARE(WL1271_AP_FW_NAME); MODULE_ALIAS("spi:wl1271"); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 86be83e..a2e899d 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -131,9 +131,15 @@ extern u32 wl12xx_debug_level; #define WL1271_FW_NAME "ti-connectivity/wl1271-fw-2.bin" +#define WL128X_FW_NAME "ti-connectivity/wl128x-fw.bin" #define WL1271_AP_FW_NAME "ti-connectivity/wl1271-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_HI32(s) ((u32)(((s) >> 16) & 0xffffffff)) -- cgit v0.10.2 From 48a61477bdc04896bd96d259388a0c42a7019943 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:08 +0200 Subject: wl12xx: 1281/1283 support - Add acx commands New acx command that sets: Rx fifo enable reduced bus transactions in RX path. Tx bus transactions padding to SDIO block size that improve preference in Tx and essential for working with SDIO HS (48Mhz). The max SDIO block size is 256 when working with Tx bus transactions padding to SDIO block. Add new ops to SDIO & SPI that handles the win size change in case of transactions padding (relevant only for SDIO). [Fix endianess issues; simplify sdio-specific block_size handling; minor changes in comments; use "aligned_len" in one calculation instead of "pad" to avoid confusion -- Luca] Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index a3db755..50676b3 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1019,6 +1019,32 @@ out: 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 ret; diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index dd19b01..0a40cae 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -939,6 +939,16 @@ struct wl1271_acx_keep_alive_config { u8 padding; } __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 { WL1271_ACX_TRIG_TYPE_LEVEL = 0, WL1271_ACX_TRIG_TYPE_EDGE, @@ -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_sta_mem_cfg(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_smart_reflex(struct wl1271 *wl); int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable); diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 6072fe4..34c4108 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -31,6 +31,7 @@ #include "cmd.h" #include "reg.h" #include "tx.h" +#include "io.h" int wl1271_sta_init_templates_config(struct wl1271 *wl) { @@ -504,6 +505,27 @@ static int wl1271_set_ba_policies(struct wl1271 *wl) 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 (wl1271_set_block_size(wl)) + /* 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) { struct conf_tx_ac_category *conf_ac; @@ -519,6 +541,11 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) return ret; + /* Chip-specific init */ + ret = wl1271_chip_specific_init(wl); + if (ret < 0) + return ret; + /* Mode specific init */ if (is_ap) ret = wl1271_ap_hw_init(wl); diff --git a/drivers/net/wireless/wl12xx/init.h b/drivers/net/wireless/wl12xx/init.h index 3a8bd3f..4975270 100644 --- a/drivers/net/wireless/wl12xx/init.h +++ b/drivers/net/wireless/wl12xx/init.h @@ -31,6 +31,7 @@ int wl1271_sta_init_templates_config(struct wl1271 *wl); int wl1271_init_phy_config(struct wl1271 *wl); int wl1271_init_pta(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); #endif diff --git a/drivers/net/wireless/wl12xx/io.c b/drivers/net/wireless/wl12xx/io.c index d557f73..aa40c98 100644 --- a/drivers/net/wireless/wl12xx/io.c +++ b/drivers/net/wireless/wl12xx/io.c @@ -43,6 +43,16 @@ #define OCP_STATUS_REQ_FAILED 0x20000 #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); + return true; + } + + return false; +} + void wl1271_disable_interrupts(struct wl1271 *wl) { wl->if_ops->disable_irq(wl); diff --git a/drivers/net/wireless/wl12xx/io.h b/drivers/net/wireless/wl12xx/io.h index c1aac82..84454f6 100644 --- a/drivers/net/wireless/wl12xx/io.h +++ b/drivers/net/wireless/wl12xx/io.h @@ -169,5 +169,6 @@ int wl1271_init_ieee80211(struct wl1271 *wl); struct ieee80211_hw *wl1271_alloc_hw(void); int wl1271_free_hw(struct wl1271 *wl); irqreturn_t wl1271_irq(int irq, void *data); +bool wl1271_set_block_size(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index b0c4935..f24906f 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -450,6 +450,11 @@ static int wl1271_plt_init(struct wl1271 *wl) if (ret < 0) return ret; + /* Chip-specific initializations */ + ret = wl1271_chip_specific_init(wl); + if (ret < 0) + return ret; + ret = wl1271_sta_init_templates_config(wl); if (ret < 0) return ret; @@ -1335,6 +1340,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map)); wl->ap_fw_ps_map = 0; wl->ap_ps_map = 0; + wl->block_size = 0; for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; @@ -3458,6 +3464,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->ap_ps_map = 0; wl->ap_fw_ps_map = 0; wl->quirks = 0; + wl->block_size = 0; memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 5e9ef7d..e296f0a 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -132,6 +132,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, { struct wl1271_tx_hw_descr *desc; u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra; + u32 len; u32 total_blocks; int id, ret = -EBUSY; @@ -145,14 +146,20 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, /* approximate the number of blocks required for this packet in the firmware */ - total_blocks = total_len + TX_HW_BLOCK_SIZE - 1; - total_blocks = total_blocks / TX_HW_BLOCK_SIZE + TX_HW_BLOCK_SPARE; + if (wl->block_size) + len = ALIGN(total_len, wl->block_size); + else + len = total_len; + + total_blocks = (len + TX_HW_BLOCK_SIZE - 1) / TX_HW_BLOCK_SIZE + + TX_HW_BLOCK_SPARE; + if (total_blocks <= wl->tx_blocks_available) { desc = (struct wl1271_tx_hw_descr *)skb_push( skb, total_len - skb->len); - desc->extra_mem_blocks = TX_HW_BLOCK_SPARE; - desc->total_mem_blocks = total_blocks; + desc->wl127x_mem.extra_blocks = TX_HW_BLOCK_SPARE; + desc->wl127x_mem.total_mem_blocks = total_blocks; desc->id = id; wl->tx_blocks_available -= total_blocks; @@ -178,7 +185,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, { struct timespec ts; struct wl1271_tx_hw_descr *desc; - int pad, ac, rate_idx; + int aligned_len, ac, rate_idx; s64 hosttime; u16 tx_attr; @@ -237,20 +244,32 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY; desc->reserved = 0; - /* align the length (and store in terms of words) */ - pad = ALIGN(skb->len, WL1271_TX_ALIGN_TO); - desc->length = cpu_to_le16(pad >> 2); + if (wl->block_size) { + aligned_len = ALIGN(skb->len, wl->block_size); + + desc->wl128x_mem.extra_bytes = aligned_len - skb->len; + desc->length = cpu_to_le16(aligned_len >> 2); + } else { + int pad; + + /* align the length (and store in terms of words) */ + aligned_len = ALIGN(skb->len, WL1271_TX_ALIGN_TO); + 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; - /* calculate number of padding bytes */ - pad = pad - skb->len; - tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; + wl1271_debug(DEBUG_TX, "tx_fill_hdr: padding: %d", pad); + } desc->tx_attr = cpu_to_le16(tx_attr); - wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d hlid: %d " - "tx_attr: 0x%x len: %d life: %d mem: %d", pad, desc->hlid, - le16_to_cpu(desc->tx_attr), le16_to_cpu(desc->length), - le16_to_cpu(desc->life_time), desc->total_mem_blocks); + wl1271_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d tx_attr: 0x%x " + "len: %d life: %d mem: %d", + desc->hlid, le16_to_cpu(desc->tx_attr), + le16_to_cpu(desc->length), le16_to_cpu(desc->life_time), + desc->wl127x_mem.total_mem_blocks); } /* caller must hold wl->mutex */ @@ -305,11 +324,18 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, wl1271_tx_fill_hdr(wl, skb, extra, info, hlid); /* - * The length of each packet is stored in terms of words. Thus, we must - * pad the skb data to make sure its length is aligned. - * The number of padding bytes is computed and set in wl1271_tx_fill_hdr + * The length of each packet is stored in terms of + * words. Thus, we must pad the skb data to make sure its + * 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); + if (wl->block_size) + total_len = ALIGN(skb->len, wl->block_size); + else + total_len = ALIGN(skb->len, WL1271_TX_ALIGN_TO); + memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len); memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len); diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index 02f07fa..e313177 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -55,20 +55,48 @@ #define WL1271_TX_ALIGN_TO 4 #define WL1271_TKIP_IV_SPACE 4 +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; + struct wl1271_tx_hw_descr { /* Length of packet in words, including descriptor+header+data */ __le16 length; - /* Number of extra memory blocks to allocate for this packet in - addition to the number of blocks derived from the packet length */ - u8 extra_mem_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; + union { + struct wl127x_tx_mem wl127x_mem; + struct wl128x_tx_mem wl128x_mem; + } __packed; /* Device time (in us) when the packet arrived to the driver */ __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; /* Bitwise fields - see TX_ATTR... definitions above. */ __le16 tx_attr; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index a2e899d..959b338 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -303,6 +303,7 @@ struct wl1271_if_operations { struct device* (*dev)(struct wl1271 *wl); void (*enable_irq)(struct wl1271 *wl); void (*disable_irq)(struct wl1271 *wl); + void (*set_block_size) (struct wl1271 *wl); }; #define MAX_NUM_KEYS 14 @@ -533,6 +534,8 @@ struct wl1271 { bool ba_support; u8 ba_rx_bitmap; + u32 block_size; + /* * AP-mode - links indexed by HLID. The global and broadcast links * are always active. -- cgit v0.10.2 From a81159edf8d64011933df177ec42f82d7896a0c7 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 14 Mar 2011 14:05:13 +0200 Subject: wl12xx: 1281/1283 support - add block size handling for sdio and spi Add the the set_block_size op in the SDIO and in the SPI modules. Since it is only used with SDIO, just explicitly set the op to NULL in spi.c Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 1b6a1ad..7491b3d 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -51,6 +51,18 @@ static const struct sdio_device_id wl1271_devices[] = { }; MODULE_DEVICE_TABLE(sdio, wl1271_devices); +/* The max SDIO block size is 256 when working with tx padding to SDIO block */ +#define TX_PAD_SDIO_BLK_SIZE 256 + +static void wl1271_sdio_set_block_size(struct wl1271 *wl) +{ + wl->block_size = TX_PAD_SDIO_BLK_SIZE; + + sdio_claim_host(wl->if_priv); + sdio_set_block_size(wl->if_priv, TX_PAD_SDIO_BLK_SIZE); + sdio_release_host(wl->if_priv); +} + static inline struct sdio_func *wl_to_func(struct wl1271 *wl) { return wl->if_priv; @@ -166,6 +178,9 @@ static int wl1271_sdio_power_on(struct wl1271 *wl) sdio_claim_host(func); sdio_enable_func(func); + /* Set the default block size in case it was modified */ + sdio_set_block_size(func, 0); + out: return ret; } @@ -203,7 +218,8 @@ static struct wl1271_if_operations sdio_ops = { .power = wl1271_sdio_set_power, .dev = wl1271_sdio_wl_to_dev, .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, diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index 80295f5..bfb1171 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -355,7 +355,8 @@ static struct wl1271_if_operations spi_ops = { .power = wl1271_spi_set_power, .dev = wl1271_spi_wl_to_dev, .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) -- cgit v0.10.2 From 49d750ca14cd49e76ab039b33b5a621e0a92b9fd Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:09 +0200 Subject: wl12xx: 1281/1283 support - New radio structs and functions New general and radio parameters structures and functions. Implemented as separate functions due to auto-detection between wl127x and wl128x. Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index f0aa7ab..37eb9f3 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -110,7 +110,47 @@ out: int wl1271_cmd_general_parms(struct wl1271 *wl) { 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; int ret; @@ -186,6 +226,50 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl) 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) { struct wl1271_ext_radio_parms_cmd *ext_radio_parms; diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index 54c12e7..5cac95d 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -32,7 +32,9 @@ struct acx_header; int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, size_t res_len); 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 wl128x_cmd_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_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); @@ -415,6 +417,21 @@ struct wl1271_general_parms_cmd { u8 padding[3]; } __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_cmd_header header; @@ -431,6 +448,23 @@ struct wl1271_radio_parms_cmd { u8 padding3[2]; } __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_cmd_header header; diff --git a/drivers/net/wireless/wl12xx/ini.h b/drivers/net/wireless/wl12xx/ini.h index c330a25..30efcd6 100644 --- a/drivers/net/wireless/wl12xx/ini.h +++ b/drivers/net/wireless/wl12xx/ini.h @@ -41,6 +41,28 @@ struct wl1271_ini_general_params { u8 srf3[WL1271_INI_MAX_SMART_REFLEX_PARAM]; } __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 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]; } __packed; -#define WL1271_INI_RATE_GROUP_COUNT 6 #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 { __le16 tx_bip_ref_pd_voltage; u8 tx_bip_ref_power; @@ -68,6 +97,28 @@ struct wl1271_ini_fem_params_2 { u8 normal_to_degraded_high_thr; } __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_SUB_BAND_COUNT_5 7 @@ -77,6 +128,12 @@ struct wl1271_ini_band_params_5 { u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE]; } __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 { __le16 tx_bip_ref_pd_voltage[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 { u8 normal_to_degraded_high_thr; } __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 */ #define WL1271_INI_NVS_SECTION_SIZE 468 @@ -120,4 +194,24 @@ struct wl1271_nvs_file { } dyn_radio_params_5[WL1271_INI_FEM_MODULE_COUNT]; } __packed; +struct wl128x_nvs_file { + /* NVS section */ + 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 diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 34c4108..2dbc083 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -322,9 +322,11 @@ static int wl1271_sta_hw_init(struct wl1271 *wl) { int ret; - ret = wl1271_cmd_ext_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; + } /* PS config */ ret = wl1271_acx_config_ps(wl); @@ -533,11 +535,17 @@ int wl1271_hw_init(struct wl1271 *wl) int ret, i; 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; - 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; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index f24906f..e1fd005 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -438,15 +438,25 @@ static int wl1271_plt_init(struct wl1271 *wl) struct conf_tx_tid *conf_tid; 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) 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; - ret = wl1271_cmd_ext_radio_parms(wl); + if (wl->chip.id != CHIP_ID_1283_PG20) { + ret = wl1271_cmd_ext_radio_parms(wl); + if (ret < 0) + return ret; + } if (ret < 0) return ret; -- cgit v0.10.2 From bc765bf3b9a095b3e41c8cda80643901884c3dd4 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:10 +0200 Subject: wl12xx: 1281/1283 support - Loading FW & NVS Take care of FW & NVS with the auto-detection between wl127x and wl128x. [Moved some common code outside if statements and added notes about NVS structure assumptions; Fixed a bug when checking the nvs size: if the size was incorrect, the local nvs variable was set to NULL, it should be wl->nvs instead. -- Luca] [Merged with potential buffer overflow fix -- Luca] Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 69fe870..38f3e8b 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -243,33 +243,57 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) if (wl->nvs == NULL) return -ENODEV; - /* - * 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 && - wl->nvs->general_params.dual_mode_select) - wl->enable_11a = true; - } + if (wl->chip.id == CHIP_ID_1283_PG20) { + struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs; + + if (wl->nvs_len == sizeof(struct wl128x_nvs_file)) { + if (nvs->general_params.dual_mode_select) + wl->enable_11a = true; + } else { + wl1271_error("nvs size is not as expected: %zu != %zu", + wl->nvs_len, + sizeof(struct wl128x_nvs_file)); + kfree(wl->nvs); + wl->nvs = NULL; + wl->nvs_len = 0; + return -EILSEQ; + } - if (wl->nvs_len != sizeof(struct wl1271_nvs_file) && - (wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE || - 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; - /* only the first part of the NVS needs to be uploaded */ - nvs_len = sizeof(wl->nvs->nvs); - nvs_ptr = (u8 *)wl->nvs->nvs; + } else { + struct wl1271_nvs_file *nvs = + (struct wl1271_nvs_file *)wl->nvs; + /* + * 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; + } + + if (wl->nvs_len != sizeof(struct wl1271_nvs_file) && + (wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE || + 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 */ nvs_ptr[11] = wl->mac_addr[0]; @@ -319,10 +343,13 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) /* * We've reached the first zero length, the first NVS table * 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 + - ALIGN(nvs_ptr - (u8 *)wl->nvs->nvs + 7, 4); - nvs_len -= nvs_ptr - (u8 *)wl->nvs->nvs; + nvs_ptr = (u8 *)wl->nvs + + ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4); + nvs_len -= nvs_ptr - (u8 *)wl->nvs; /* Now we must set the partition correctly */ wl1271_set_partition(wl, &part_table[PART_WORK]); diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 37eb9f3..2468044 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -187,8 +187,9 @@ out: 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_ini_general_params *gp = &wl->nvs->general_params; + struct wl1271_ini_general_params *gp = &nvs->general_params; int ret; if (!wl->nvs) @@ -201,18 +202,18 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl) radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM; /* 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)); 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)); /* 5GHz parameters */ memcpy(&radio_parms->static_params_5, - &wl->nvs->stat_radio_params_5, + &nvs->stat_radio_params_5, sizeof(struct wl1271_ini_band_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)); wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ", diff --git a/drivers/net/wireless/wl12xx/ini.h b/drivers/net/wireless/wl12xx/ini.h index 30efcd6..1420c84 100644 --- a/drivers/net/wireless/wl12xx/ini.h +++ b/drivers/net/wireless/wl12xx/ini.h @@ -174,7 +174,7 @@ struct wl128x_ini_fem_params_5 { #define WL1271_INI_LEGACY_NVS_FILE_SIZE 800 struct wl1271_nvs_file { - /* NVS section */ + /* NVS section - must be first! */ u8 nvs[WL1271_INI_NVS_SECTION_SIZE]; /* INI section */ @@ -195,7 +195,7 @@ struct wl1271_nvs_file { } __packed; struct wl128x_nvs_file { - /* NVS section */ + /* NVS section - must be first! */ u8 nvs[WL1271_INI_NVS_SECTION_SIZE]; /* INI section */ diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index e1fd005..fe0cf47 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -804,7 +804,10 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) break; case BSS_TYPE_IBSS: 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; default: wl1271_error("no compatible firmware for bss_type %d", @@ -860,7 +863,7 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) 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) { wl1271_error("could not allocate memory for the nvs file"); @@ -3289,7 +3292,11 @@ int wl1271_register_hw(struct wl1271 *wl) ret = wl1271_fetch_nvs(wl); 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[1] = nvs_ptr[10]; diff --git a/drivers/net/wireless/wl12xx/sdio_test.c b/drivers/net/wireless/wl12xx/sdio_test.c index 01adf1b..e6e2ad6 100644 --- a/drivers/net/wireless/wl12xx/sdio_test.c +++ b/drivers/net/wireless/wl12xx/sdio_test.c @@ -189,7 +189,12 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) const struct firmware *fw; 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) { wl1271_error("could not get firmware: %d", ret); @@ -234,7 +239,7 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) 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) { wl1271_error("could not allocate memory for the nvs file"); diff --git a/drivers/net/wireless/wl12xx/testmode.c b/drivers/net/wireless/wl12xx/testmode.c index 6ec06a4..da351d7 100644 --- a/drivers/net/wireless/wl12xx/testmode.c +++ b/drivers/net/wireless/wl12xx/testmode.c @@ -27,6 +27,7 @@ #include "wl12xx.h" #include "acx.h" +#include "reg.h" #define WL1271_TM_MAX_DATA_LENGTH 1024 @@ -204,7 +205,10 @@ static int wl1271_tm_cmd_nvs_push(struct wl1271 *wl, struct nlattr *tb[]) 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; wl->nvs = kzalloc(len, GFP_KERNEL); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 959b338..e59f539 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -378,7 +378,7 @@ struct wl1271 { u8 *fw; size_t fw_len; u8 fw_bss_type; - struct wl1271_nvs_file *nvs; + void *nvs; size_t nvs_len; s8 hw_pg_ver; -- cgit v0.10.2 From 5ea417ae7749076ddaacb5b36487cae6ac920413 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:11 +0200 Subject: wl12xx: 1281/1283 support - New boot sequence Boot sequence support FREF clock and TCXO clock. 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. Auto-detection checks where TCXO is 32.736MHz or 16.368MHz, in that case the FREF will be used as the WLAN/BT main clock. [Use clock enumeration as defined in linux/wl12xx.h; remove unnecessary else block in wl128x_switch_fref; remove unnecessary change in main.c; remove some unnecessary debug prints and comments; fix potential use of uninitialized value (pll_config) -- Luca] Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 38f3e8b..9d742c1 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -22,6 +22,7 @@ */ #include +#include #include "acx.h" #include "reg.h" @@ -520,24 +521,159 @@ static void wl1271_boot_hw_version(struct wl1271 *wl) wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; } -/* uploads NVS and firmware */ -int wl1271_load_firmware(struct wl1271 *wl) +/* + * 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_switch_fref(struct wl1271 *wl, bool *is_ref_clk) { - int ret = 0; - u32 tmp, clk, pause; + u16 sys_clk_cfg_val; + + /* if working on XTAL-only mode go directly to TCXO TO FREF SWITCH */ + if ((wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL) || + (wl->ref_clock == CONF_REF_CLK_26_M_XTAL)) + return true; + + /* Read clock source FREF or TCXO */ + sys_clk_cfg_val = wl1271_top_reg_read(wl, SYS_CLK_CFG_REG); + + if (sys_clk_cfg_val & PRCM_CM_EN_MUX_WLAN_FREF) { + /* if bit 3 is set - working with FREF clock */ + wl1271_debug(DEBUG_BOOT, "working with FREF clock, skip" + " to FREF"); + + *is_ref_clk = true; + } else { + /* if bit 3 is clear - working with TCXO clock */ + wl1271_debug(DEBUG_BOOT, "working with TCXO clock"); + + /* TCXO to FREF switch, check TXCO clock config */ + if ((wl->tcxo_clock != WL12XX_TCXOCLOCK_16_368) && + (wl->tcxo_clock != WL12XX_TCXOCLOCK_32_736)) { + /* + * not 16.368Mhz and not 32.736Mhz - skip to + * configure ELP stage + */ + wl1271_debug(DEBUG_BOOT, "NEW PLL ALGO:" + " TcxoRefClk=%d - not 16.368Mhz and not" + " 32.736Mhz - skip to configure ELP" + " stage", wl->tcxo_clock); + + *is_ref_clk = false; + } else { + wl1271_debug(DEBUG_BOOT, "NEW PLL ALGO:" + "TcxoRefClk=%d - 16.368Mhz or 32.736Mhz" + " - TCXO to FREF switch", + wl->tcxo_clock); + + return true; + } + } + + return false; +} + +static int wl128x_boot_clk(struct wl1271 *wl, bool *is_ref_clk) +{ + if (wl128x_switch_fref(wl, is_ref_clk)) { + wl1271_debug(DEBUG_BOOT, "XTAL-only mode go directly to" + " TCXO TO FREF SWITCH"); + /* TCXO to FREF switch - for PG2.0 */ + wl1271_top_reg_write(wl, WL_SPARE_REG, + WL_SPARE_MASK_8526); + + wl1271_top_reg_write(wl, SYS_CLK_CFG_REG, + WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF); + + *is_ref_clk = true; + mdelay(15); + } + + /* Set bit 2 in spare register to avoid illegal access */ + wl1271_top_reg_write(wl, WL_SPARE_REG, WL_SPARE_VAL); + + /* working with TCXO clock */ + if ((*is_ref_clk == false) && + ((wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8) || + (wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6))) { + wl1271_debug(DEBUG_BOOT, "16_8_M or 33_6_M TCXO detected"); + + /* Manually Configure MCS PLL settings PG2.0 Only */ + 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); + } else { + int pll_config; + u16 mcs_pll_config_val; + + /* + * Configure MCS PLL settings to FREF Freq + * Set the values that determine the time elapse since the PLL's + * get their enable signal until the lock indication is set + */ + wl1271_top_reg_write(wl, PLL_LOCK_COUNTERS_REG, + PLL_LOCK_COUNTERS_COEX | PLL_LOCK_COUNTERS_MCS); + + mcs_pll_config_val = wl1271_top_reg_read(wl, + MCS_PLL_CONFIG_REG); + /* + * Set the MCS PLL input frequency value according to the + * reference clock value detected/read + */ + if (*is_ref_clk == false) { + if ((wl->tcxo_clock == WL12XX_TCXOCLOCK_19_2) || + (wl->tcxo_clock == WL12XX_TCXOCLOCK_38_4)) + pll_config = 1; + else if ((wl->tcxo_clock == WL12XX_TCXOCLOCK_26) + || + (wl->tcxo_clock == WL12XX_TCXOCLOCK_52)) + pll_config = 2; + else + return -EINVAL; + } else { + if ((wl->ref_clock == CONF_REF_CLK_19_2_E) || + (wl->ref_clock == CONF_REF_CLK_38_4_E)) + pll_config = 1; + else if ((wl->ref_clock == CONF_REF_CLK_26_E) || + (wl->ref_clock == CONF_REF_CLK_52_E)) + pll_config = 2; + else + return -EINVAL; + } + + mcs_pll_config_val |= (pll_config << (MCS_SEL_IN_FREQ_SHIFT)) & + (MCS_SEL_IN_FREQ_MASK); + wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, + mcs_pll_config_val); + } + + return 0; +} + +static int wl127x_boot_clk(struct wl1271 *wl) +{ + u32 pause; + u32 clk; 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 */ 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 */ clk = 0x5; else return -EINVAL; - if (wl->ref_clock != 0) { + if (wl->ref_clock != CONF_REF_CLK_19_2_E) { u16 val; /* Set clock type (open drain) */ val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE); @@ -567,6 +703,26 @@ int wl1271_load_firmware(struct wl1271 *wl) pause |= WU_COUNTER_PAUSE_VAL; 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; + bool is_ref_clk = false; + + if (wl->chip.id == CHIP_ID_1283_PG20) { + ret = wl128x_boot_clk(wl, &is_ref_clk); + if (ret < 0) + goto out; + } else { + ret = wl127x_boot_clk(wl); + if (ret < 0) + goto out; + } + /* Continue the ELP wake up sequence */ wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); udelay(500); @@ -582,7 +738,15 @@ int wl1271_load_firmware(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); - clk |= (wl->ref_clock << 1) << 4; + if (wl->chip.id == CHIP_ID_1283_PG20) { + if (is_ref_clk == false) + clk |= ((wl->tcxo_clock & 0x3) << 1) << 4; + else + clk |= ((wl->ref_clock & 0x3) << 1) << 4; + } else { + clk |= (wl->ref_clock << 1) << 4; + } + wl1271_write32(wl, DRPW_SCRATCH_START, clk); wl1271_set_partition(wl, &part_table[PART_WORK]); @@ -615,6 +779,9 @@ int wl1271_load_firmware(struct wl1271 *wl) /* WL1271: The reference driver skips steps 7 to 10 (jumps directly * to upload_fw) */ + if (wl->chip.id == CHIP_ID_1283_PG20) + wl1271_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA); + ret = wl1271_boot_upload_firmware(wl); if (ret < 0) goto out; diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 7491b3d..f6dd3de 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -255,6 +255,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, wl->irq = wlan_data->irq; wl->ref_clock = wlan_data->board_ref_clock; + wl->tcxo_clock = wlan_data->board_tcxo_clock; ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, diff --git a/drivers/net/wireless/wl12xx/sdio_test.c b/drivers/net/wireless/wl12xx/sdio_test.c index e6e2ad6..c02c870 100644 --- a/drivers/net/wireless/wl12xx/sdio_test.c +++ b/drivers/net/wireless/wl12xx/sdio_test.c @@ -421,6 +421,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, wl->irq = wlan_data->irq; wl->ref_clock = wlan_data->board_ref_clock; + wl->tcxo_clock = wlan_data->board_tcxo_clock; sdio_set_drvdata(func, wl_test); diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index bfb1171..f552536 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -401,6 +401,7 @@ static int __devinit wl1271_probe(struct spi_device *spi) } wl->ref_clock = pdata->board_ref_clock; + wl->tcxo_clock = pdata->board_tcxo_clock; wl->irq = spi->irq; if (wl->irq < 0) { diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index e59f539..4b55677 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -535,6 +535,7 @@ struct wl1271 { u8 ba_rx_bitmap; u32 block_size; + int tcxo_clock; /* * AP-mode - links indexed by HLID. The global and broadcast links -- cgit v0.10.2 From 13b107dd9808343d05627f0fba7fbc764c86738e Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:12 +0200 Subject: wl12xx: 1281/1283 support - use dynamic memory for the RX/TX pools Separate the memory configuration to chip-specific structures and implement dynamic memory for wl128x. This feature allows us to move TX memory blocks to the RX pool when the RX path is overloaded. Thanks for Arik Nemtsov for helping simplify the wl1271_fw_status() code. [Rewrote the commit subject and message for clarity; improved some comments and changed "spare" to "padding" for consistency; added a FIXME for the AP memory configuration -- Luca] Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 50676b3..e005aa4 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -965,10 +965,13 @@ int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) } /* memory config */ - mem_conf->num_stations = wl->conf.mem.num_stations; - mem_conf->rx_mem_block_num = wl->conf.mem.rx_block_num; - mem_conf->tx_min_mem_block_num = wl->conf.mem.tx_min_block_num; - mem_conf->num_ssid_profiles = wl->conf.mem.ssid_profiles; + /* FIXME: for now we always use mem_wl127x for AP, because it + * doesn't support dynamic memory and we don't have the + * optimal values for wl128x without dynamic memory yet */ + 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); ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, @@ -986,6 +989,7 @@ out: int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) { struct wl1271_acx_sta_config_memory *mem_conf; + struct conf_memory_settings *mem; int ret; wl1271_debug(DEBUG_ACX, "wl1271 mem cfg"); @@ -996,16 +1000,21 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) goto out; } + if (wl->chip.id == CHIP_ID_1283_PG20) + mem = &wl->conf.mem_wl128x; + else + mem = &wl->conf.mem_wl127x; + /* memory config */ - mem_conf->num_stations = wl->conf.mem.num_stations; - mem_conf->rx_mem_block_num = wl->conf.mem.rx_block_num; - mem_conf->tx_min_mem_block_num = wl->conf.mem.tx_min_block_num; - mem_conf->num_ssid_profiles = wl->conf.mem.ssid_profiles; + mem_conf->num_stations = mem->num_stations; + mem_conf->rx_mem_block_num = mem->rx_block_num; + mem_conf->tx_min_mem_block_num = mem->tx_min_block_num; + mem_conf->num_ssid_profiles = mem->ssid_profiles; mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); - mem_conf->dyn_mem_enable = wl->conf.mem.dynamic_memory; - mem_conf->tx_free_req = wl->conf.mem.min_req_tx_blocks; - mem_conf->rx_free_req = wl->conf.mem.min_req_rx_blocks; - mem_conf->tx_min = wl->conf.mem.tx_min; + mem_conf->dyn_mem_enable = mem->dynamic_memory; + mem_conf->tx_free_req = mem->min_req_tx_blocks; + mem_conf->rx_free_req = mem->min_req_rx_blocks; + mem_conf->tx_min = mem->tx_min; ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, sizeof(*mem_conf)); @@ -1072,6 +1081,8 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl) wl1271_debug(DEBUG_TX, "available tx blocks: %d", wl->tx_blocks_available); + wl->tx_new_total = wl->tx_blocks_available; + return 0; } diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index a00f22c..743bd0b 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1204,7 +1204,8 @@ struct conf_drv_settings { struct conf_scan_settings scan; struct conf_rf_settings rf; struct conf_ht_setting ht; - struct conf_memory_settings mem; + struct conf_memory_settings mem_wl127x; + struct conf_memory_settings mem_wl128x; }; #endif diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index fe0cf47..3c381ce 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -298,7 +298,7 @@ static struct conf_drv_settings default_conf = { .tx_ba_win_size = 64, .inactivity_timeout = 10000, }, - .mem = { + .mem_wl127x = { .num_stations = 1, .ssid_profiles = 1, .rx_block_num = 70, @@ -307,7 +307,17 @@ static struct conf_drv_settings default_conf = { .min_req_tx_blocks = 100, .min_req_rx_blocks = 22, .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, + }, }; static void __wl1271_op_remove_interface(struct wl1271 *wl); @@ -608,16 +618,27 @@ static void wl1271_fw_status(struct wl1271 *wl, { struct wl1271_fw_common_status *status = &full_status->common; struct timespec ts; + u32 old_tx_blk_count = wl->tx_blocks_available; u32 total = 0; 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, sizeof(struct wl1271_fw_ap_status), false); - else + } else { wl1271_raw_read(wl, FW_STATUS_ADDR, status, sizeof(struct wl1271_fw_sta_status), false); + /* Update tx total blocks change */ + wl->tx_total_diff += + ((struct wl1271_fw_sta_status *)status)->tx_total - + wl->tx_new_total; + + /* Update total tx blocks */ + wl->tx_new_total = + ((struct wl1271_fw_sta_status *)status)->tx_total; + } + wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " "drv_rx_counter = %d, tx_results_counter = %d)", status->intr, @@ -627,17 +648,28 @@ static void wl1271_fw_status(struct wl1271 *wl, /* update number of available TX blocks */ for (i = 0; i < NUM_TX_QUEUES; i++) { - u32 cnt = le32_to_cpu(status->tx_released_blks[i]) - + total += le32_to_cpu(status->tx_released_blks[i]) - wl->tx_blocks_freed[i]; wl->tx_blocks_freed[i] = le32_to_cpu(status->tx_released_blks[i]); - wl->tx_blocks_available += cnt; - total += cnt; + + } + + /* + * By adding the freed blocks to tx_total_diff we are actually + * moving them to the RX pool. + */ + wl->tx_total_diff += total; + + /* if we have positive difference, add the blocks to the TX pool */ + if (wl->tx_total_diff >= 0) { + wl->tx_blocks_available += wl->tx_total_diff; + wl->tx_total_diff = 0; } /* if more blocks are available now, tx work can be scheduled */ - if (total) + if (wl->tx_blocks_available > old_tx_blk_count) clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); /* for AP update num of allocated TX blocks per link and ps status */ diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 4b55677..ad04b83 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -267,6 +267,8 @@ struct wl1271_fw_sta_status { u8 tx_total; u8 reserved1; __le16 reserved2; + /* Total structure size is 68 bytes */ + u32 padding; } __packed; struct wl1271_fw_full_status { @@ -397,6 +399,9 @@ struct wl1271 { u32 tx_blocks_freed[NUM_TX_QUEUES]; u32 tx_blocks_available; u32 tx_results_count; + /* Indicates how many memory blocks should be moved to the RX pool */ + int tx_total_diff; + u32 tx_new_total; /* Transmitted TX packets counter for chipset interface */ u32 tx_packets_count; -- cgit v0.10.2 From ae77eccf04f8c36769bdba334e1bbcc7bb9d3644 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:13 +0200 Subject: wl12xx: 1281/1283 support - Improve Tx & Rx path Reduced bus transactions in the Tx & Rx path. [Removed unnecessary check wl->chip.id != CHIP_ID_1283_PG20 when checking the quirk -- Luca] Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 919b59f..132b0ca 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -163,18 +163,25 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) break; } - /* - * Choose the block we want to read - * 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) + - le32_to_cpu(wl_mem_map->packet_memory_pool_start); - 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); + if (wl->chip.id != CHIP_ID_1283_PG20) { + /* + * Choose the block we want to read + * 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) + + le32_to_cpu(wl_mem_map->packet_memory_pool_start); + + 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 */ wl1271_read(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf, diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index e296f0a..afc8505 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -158,8 +158,14 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, desc = (struct wl1271_tx_hw_descr *)skb_push( skb, total_len - skb->len); - desc->wl127x_mem.extra_blocks = TX_HW_BLOCK_SPARE; - desc->wl127x_mem.total_mem_blocks = total_blocks; + /* HW descriptor fields change between wl127x and wl128x */ + if (wl->chip.id == CHIP_ID_1283_PG20) { + desc->wl128x_mem.total_mem_blocks = total_blocks; + } else { + desc->wl127x_mem.extra_blocks = TX_HW_BLOCK_SPARE; + desc->wl127x_mem.total_mem_blocks = total_blocks; + } + desc->id = id; wl->tx_blocks_available -= total_blocks; @@ -249,6 +255,13 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, desc->wl128x_mem.extra_bytes = aligned_len - skb->len; desc->length = cpu_to_le16(aligned_len >> 2); + + 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; @@ -260,16 +273,15 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, pad = aligned_len - skb->len; tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; - wl1271_debug(DEBUG_TX, "tx_fill_hdr: padding: %d", pad); + 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, + 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); - - wl1271_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d tx_attr: 0x%x " - "len: %d life: %d mem: %d", - desc->hlid, le16_to_cpu(desc->tx_attr), - le16_to_cpu(desc->length), le16_to_cpu(desc->life_time), - desc->wl127x_mem.total_mem_blocks); } /* caller must hold wl->mutex */ -- cgit v0.10.2 From ae47c45fd02fdf88d57adc370e78e7a01e2bfcbc Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:14 +0200 Subject: wl12xx: 1281/1283 support - Add dummy packet support Support sending dummy packet to wl128x FW as results of dummy packet event. That is part of dynamic TX mem blocks mechanism. Only send dummy packet when not in AP mode. [Even though the DUMMY_PACKET_EVENT_ID and the STA_REMOVE_COMPLETE_EVENT_ID events are defined to the same value, we need to treat them separately in the code. Keep the check and enable STA_REMOVE_COMPLETE_EVENT_ID for AP mode and DUMMY_PACKET_EVENT_ID for STA. Moved one warning to a cleaner place. Use WL1271_TID_MGMT for dummy packets -- Luca] Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 9d742c1..34bf2fe 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -482,6 +482,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) if (wl->bss_type == BSS_TYPE_AP_BSS) wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID; + else + wl->event_mask |= DUMMY_PACKET_EVENT_ID; ret = wl1271_event_unmask(wl); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index 1b170c5..413d901 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -228,6 +228,12 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *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) ieee80211_connection_loss(wl->vif); diff --git a/drivers/net/wireless/wl12xx/event.h b/drivers/net/wireless/wl12xx/event.h index 0e80886..b6cf06e 100644 --- a/drivers/net/wireless/wl12xx/event.h +++ b/drivers/net/wireless/wl12xx/event.h @@ -59,7 +59,10 @@ enum { BSS_LOSE_EVENT_ID = BIT(18), REGAINED_BSS_EVENT_ID = BIT(19), 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_PREDICTION_EVENT_ID = BIT(23), SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24), diff --git a/drivers/net/wireless/wl12xx/io.h b/drivers/net/wireless/wl12xx/io.h index 84454f6..e6199eb 100644 --- a/drivers/net/wireless/wl12xx/io.h +++ b/drivers/net/wireless/wl12xx/io.h @@ -170,5 +170,6 @@ struct ieee80211_hw *wl1271_alloc_hw(void); int wl1271_free_hw(struct wl1271 *wl); irqreturn_t wl1271_irq(int irq, void *data); bool wl1271_set_block_size(struct wl1271 *wl); +int wl1271_tx_dummy_packet(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 3c381ce..54ac675 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1174,6 +1174,48 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) spin_unlock_irqrestore(&wl->wl_lock, flags); } +#define TX_DUMMY_PACKET_SIZE 1400 +int wl1271_tx_dummy_packet(struct wl1271 *wl) +{ + struct sk_buff *skb = NULL; + struct ieee80211_hdr_3addr *hdr; + int ret = 0; + + skb = dev_alloc_skb( + sizeof(struct wl1271_tx_hw_descr) + sizeof(*hdr) + + TX_DUMMY_PACKET_SIZE); + if (!skb) { + wl1271_warning("failed to allocate buffer for dummy packet"); + ret = -ENOMEM; + goto out; + } + + 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_FCTL_TODS | + IEEE80211_STYPE_NULLFUNC); + + memcpy(hdr->addr1, wl->bssid, ETH_ALEN); + memcpy(hdr->addr2, wl->mac_addr, ETH_ALEN); + memcpy(hdr->addr3, wl->bssid, ETH_ALEN); + + skb_put(skb, TX_DUMMY_PACKET_SIZE); + + memset(skb->data, 0, TX_DUMMY_PACKET_SIZE); + + skb->pkt_type = TX_PKT_TYPE_DUMMY_REQ; + /* CONF_TX_AC_VO */ + skb->queue_mapping = 0; + + wl1271_op_tx(wl->hw, skb); + +out: + return ret; +} + static struct notifier_block wl1271_dev_notifier = { .notifier_call = wl1271_dev_notify, }; diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index afc8505..75222a6 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -215,13 +215,29 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, else desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU); - /* configure the tx attributes */ - 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)); desc->tid = ac; + if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { + /* + * 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; + + /* Dummy packets require the TID to be management */ + desc->tid = WL1271_TID_MGMT; + } else { + /* configure the tx attributes */ + tx_attr = + wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER; + } + if (wl->bss_type != BSS_TYPE_AP_BSS) { desc->aid = hlid; @@ -587,6 +603,12 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, skb = wl->tx_frames[id]; info = IEEE80211_SKB_CB(skb); + if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { + dev_kfree_skb(skb); + wl1271_free_tx_id(wl, id); + return; + } + /* update the TX status info */ if (result->status == TX_SUCCESS) { if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) @@ -716,10 +738,15 @@ void wl1271_tx_reset(struct wl1271 *wl) while ((skb = skb_dequeue(&wl->tx_queue[i]))) { wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); - info = IEEE80211_SKB_CB(skb); - info->status.rates[0].idx = -1; - info->status.rates[0].count = 0; - ieee80211_tx_status(wl->hw, skb); + + if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { + dev_kfree_skb(skb); + } else { + info = IEEE80211_SKB_CB(skb); + info->status.rates[0].idx = -1; + info->status.rates[0].count = 0; + ieee80211_tx_status(wl->hw, skb); + } } } } @@ -740,21 +767,29 @@ void wl1271_tx_reset(struct wl1271 *wl) wl1271_free_tx_id(wl, i); wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); - /* Remove private headers before passing the skb to mac80211 */ - info = IEEE80211_SKB_CB(skb); - skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); - if (info->control.hw_key && - 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); - } + if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { + dev_kfree_skb(skb); + } else { + /* + * Remove private headers before passing the skb to + * mac80211 + */ + info = IEEE80211_SKB_CB(skb); + skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); + if (info->control.hw_key && + 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].count = 0; + info->status.rates[0].idx = -1; + info->status.rates[0].count = 0; - ieee80211_tx_status(wl->hw, skb); + ieee80211_tx_status(wl->hw, skb); + } } } diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index e313177..6f45e91 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -41,6 +41,9 @@ BIT(8) | BIT(9)) #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_DUMMY_REQ BIT(13) + +#define TX_PKT_TYPE_DUMMY_REQ 5 #define TX_HW_ATTR_OFST_SAVE_RETRIES 0 #define TX_HW_ATTR_OFST_HEADER_PAD 1 @@ -55,6 +58,9 @@ #define WL1271_TX_ALIGN_TO 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 -- cgit v0.10.2 From 1aed55fd784d000fb6741cefb68712d64817bd68 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 6 Mar 2011 16:32:18 +0200 Subject: wl12xx: 1281/1283 support - Use different FW file for AP mode wl127x/wl128x chips Choose a different FW for AP-mode wl127x and wl128x chips, base on chip ID at boot time. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 54ac675..9a7ca65 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -832,7 +832,10 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) switch (wl->bss_type) { 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; case BSS_TYPE_IBSS: case BSS_TYPE_STA_BSS: diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index f6dd3de..5a2951e 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -361,4 +361,5 @@ MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); MODULE_FIRMWARE(WL1271_FW_NAME); MODULE_FIRMWARE(WL128X_FW_NAME); -MODULE_FIRMWARE(WL1271_AP_FW_NAME); +MODULE_FIRMWARE(WL127X_AP_FW_NAME); +MODULE_FIRMWARE(WL128X_AP_FW_NAME); diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index f552536..7b82b5f 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -493,5 +493,6 @@ MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); MODULE_FIRMWARE(WL1271_FW_NAME); MODULE_FIRMWARE(WL128X_FW_NAME); -MODULE_FIRMWARE(WL1271_AP_FW_NAME); +MODULE_FIRMWARE(WL127X_AP_FW_NAME); +MODULE_FIRMWARE(WL128X_AP_FW_NAME); MODULE_ALIAS("spi:wl1271"); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index ad04b83..890c1a5 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -132,7 +132,8 @@ extern u32 wl12xx_debug_level; #define WL1271_FW_NAME "ti-connectivity/wl1271-fw-2.bin" #define WL128X_FW_NAME "ti-connectivity/wl128x-fw.bin" -#define WL1271_AP_FW_NAME "ti-connectivity/wl1271-fw-ap.bin" +#define WL127X_AP_FW_NAME "ti-connectivity/wl1271-fw-ap.bin" +#define WL128X_AP_FW_NAME "ti-connectivity/wl128x-fw-ap.bin" /* * wl127x and wl128x are using the same NVS file name. However, the -- cgit v0.10.2 From 0830ceedbfde20c9110c59597fdffbf51886565a Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:20 +0200 Subject: wl12xx: 1281/1283 support - enable chip support Add support to wl128x chip via chip id Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig index 692ebff..35ce7b0 100644 --- a/drivers/net/wireless/wl12xx/Kconfig +++ b/drivers/net/wireless/wl12xx/Kconfig @@ -3,7 +3,7 @@ menuconfig WL12XX_MENU depends on MAC80211 && EXPERIMENTAL ---help--- 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. config WL12XX diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 9a7ca65..0b9d41f 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1007,6 +1007,15 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) if (ret < 0) goto out; 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; + break; + case CHIP_ID_1283_PG10: default: wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); ret = -ENODEV; diff --git a/drivers/net/wireless/wl12xx/sdio_test.c b/drivers/net/wireless/wl12xx/sdio_test.c index c02c870..968249a 100644 --- a/drivers/net/wireless/wl12xx/sdio_test.c +++ b/drivers/net/wireless/wl12xx/sdio_test.c @@ -293,6 +293,11 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) wl1271_notice("chip id 0x%x (1271 PG20)", wl->chip.id); break; + case CHIP_ID_1283_PG20: + wl1271_notice("chip id 0x%x (1283 PG20)", + wl->chip.id); + break; + case CHIP_ID_1283_PG10: default: wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); return -ENODEV; -- cgit v0.10.2 From e7ddf549f3f2da156f5c12921e6699024e80a3f4 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 Mar 2011 15:24:57 +0200 Subject: wl12xx: use 1 spare TX block instead of two All the new firmware versions (>=6.1.3.50.58 for STA and >=6.2.0.0.47 for AP) use 1 spare TX block. We still want to support older firmwares that require 2 spare blocks, so added a quirk to handle the difference. Also implemented a generic way of setting quirks that depend on the firmware revision. Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 0b9d41f..db7ab85 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1040,6 +1040,24 @@ out: 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 retries = WL1271_BOOT_RETRIES; @@ -1075,6 +1093,9 @@ int wl1271_plt_start(struct wl1271 *wl) wl->state = WL1271_STATE_PLT; wl1271_notice("firmware booted in PLT mode (%s)", 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; irq_disable: @@ -1353,6 +1374,9 @@ power_off: strncpy(wiphy->fw_version, wl->chip.fw_ver_str, 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 * 11a channels if not supported diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 75222a6..109878c 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -135,6 +135,12 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, u32 len; u32 total_blocks; 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) return -EAGAIN; @@ -152,7 +158,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, len = total_len; total_blocks = (len + TX_HW_BLOCK_SIZE - 1) / TX_HW_BLOCK_SIZE + - TX_HW_BLOCK_SPARE; + spare_blocks; if (total_blocks <= wl->tx_blocks_available) { desc = (struct wl1271_tx_hw_descr *)skb_push( @@ -162,7 +168,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, if (wl->chip.id == CHIP_ID_1283_PG20) { desc->wl128x_mem.total_mem_blocks = total_blocks; } else { - desc->wl127x_mem.extra_blocks = TX_HW_BLOCK_SPARE; + desc->wl127x_mem.extra_blocks = spare_blocks; desc->wl127x_mem.total_mem_blocks = total_blocks; } diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index 6f45e91..a3877ba 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -25,7 +25,6 @@ #ifndef __TX_H__ #define __TX_H__ -#define TX_HW_BLOCK_SPARE 2 #define TX_HW_BLOCK_SIZE 252 #define TX_HW_MGMT_PKT_LIFETIME_TU 2000 diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 890c1a5..b04481a 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -207,13 +207,29 @@ struct wl1271_partition_set { 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 { u32 id; 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 { @@ -594,4 +610,10 @@ int wl1271_plt_stop(struct wl1271 *wl); /* Each RX/TX transaction requires an end-of-transaction transfer */ #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) + #endif -- cgit v0.10.2 From 0af0467f09207cbbeb387d2e09ea539534c6655c Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 10 Mar 2011 10:01:43 +0200 Subject: wl12xx: Fix potential incorrect band in rx-status The rx-status passed to mac80211 along with each received frame contains the band on which the frame was received. Under certain circumstances, this band information may be incorrect, causing in worst case a WARNING from mac80211, and causes the received frame to be dropped. This scenario mainly occurs when performing connected-mode scans, when the received scan results are from the other band than the one currently associated to. [Since desc_band doesn't exist anymore, use status->band in the later call to ieee80211_channel_to_frequency() to fix compilation -- Luca] Signed-off-by: Juuso Oikarinen Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 132b0ca..2a58149 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -48,18 +48,14 @@ static void wl1271_rx_status(struct wl1271 *wl, struct ieee80211_rx_status *status, u8 beacon) { - enum ieee80211_band desc_band; - 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) - desc_band = IEEE80211_BAND_2GHZ; + status->band = IEEE80211_BAND_2GHZ; 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 /* 11n support */ @@ -76,7 +72,8 @@ static void wl1271_rx_status(struct wl1271 *wl, */ 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) { status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; -- cgit v0.10.2 From 871d0c3ba32c2d2e1e7d9ac0d231a440d2653fc5 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 13 Mar 2011 11:24:40 +0200 Subject: wl12xx: Add support for 11n Rx STBC one spatial stream The wl12xx chip supports one Rx STBC spatial stream. Announce this in the HT capabilities info field. Signed-off-by: Shahar Levi Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index db7ab85..2804847 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3132,7 +3132,8 @@ static const u8 wl1271_rate_to_idx_2ghz[] = { #ifdef CONFIG_WL12XX_HT #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, \ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, \ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \ -- cgit v0.10.2 From 958b20e068be2c6267c2b5764babf15b0d4f5c69 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Mon, 14 Mar 2011 18:53:10 +0200 Subject: wl12xx: update bet_max_consecutive Allow early termination of 50 consecutive beacons. This value is the recommended one by the 12xx's system/RF team, and tests show that power consumption is improved as expected. Reported-by: Ruthy Zaphir Tested-by: Danil Shalumov Signed-off-by: Ohad Ben-Cohen Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 2804847..dd49b22 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -254,7 +254,7 @@ static struct conf_drv_settings default_conf = { .ps_poll_threshold = 10, .ps_poll_recovery_period = 700, .bet_enable = CONF_BET_MODE_ENABLE, - .bet_max_consecutive = 10, + .bet_max_consecutive = 50, .psm_entry_retries = 5, .psm_exit_retries = 255, .psm_entry_nullfunc_retries = 3, -- cgit v0.10.2 From 1d732e8cf3dcc09d7c862b6c12f876533529073d Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Fri, 18 Mar 2011 14:49:57 +0200 Subject: wl12xx: Clamp byte mode transfers for 128x chips On wl128x based devices, when TX packets are aggregated, each packet size must be aligned to the SDIO block size, and sent using block mode transfers. The block size is set to 256 bytes, which is less than the maximum possible byte transfer. Thus, if two small packets (< 256 bytes) are aggregated, the aggregation buffer size would be 512, and will be sent using byte mode transfers. This can have undesired side effects. Fix this by setting the MMC_QUIRK_BLKSZ_FOR_BYTE_MODE mmc card quirk. For 127x chips this has no effect, as the block size is set to 512 bytes. Signed-off-by: Arik Nemtsov Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 5a2951e..2ade222 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -246,6 +246,9 @@ static int __devinit wl1271_probe(struct sdio_func *func, /* Grab access to FN0 for ELP reg. */ 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(); if (IS_ERR(wlan_data)) { ret = PTR_ERR(wlan_data); diff --git a/drivers/net/wireless/wl12xx/sdio_test.c b/drivers/net/wireless/wl12xx/sdio_test.c index 968249a..f289153 100644 --- a/drivers/net/wireless/wl12xx/sdio_test.c +++ b/drivers/net/wireless/wl12xx/sdio_test.c @@ -417,6 +417,9 @@ static int __devinit wl1271_probe(struct sdio_func *func, /* Grab access to FN0 for ELP reg. */ 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(); if (IS_ERR(wlan_data)) { ret = PTR_ERR(wlan_data); -- cgit v0.10.2 From f9f774c17e19da6f98bd7b57527f55d0ec920fce Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Mon, 21 Mar 2011 10:43:36 +0200 Subject: wl12xx: Add mutex protection for interface list The interface list maintained in main.c is not mutex protected. This could cause issues, as the list is accessed from notifier chains. Signed-off-by: Juuso Oikarinen Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index dd49b22..72e84a2 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -339,6 +339,7 @@ static struct platform_device wl1271_device = { }, }; +static DEFINE_MUTEX(wl_list_mutex); static LIST_HEAD(wl_list); static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, @@ -369,10 +370,12 @@ static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, return NOTIFY_DONE; wl_temp = hw->priv; + mutex_lock(&wl_list_mutex); list_for_each_entry(wl, &wl_list, list) { if (wl == wl_temp) break; } + mutex_unlock(&wl_list_mutex); if (wl != wl_temp) return NOTIFY_DONE; @@ -1390,8 +1393,10 @@ power_off: out: mutex_unlock(&wl->mutex); + mutex_lock(&wl_list_mutex); if (!ret) list_add(&wl->list, &wl_list); + mutex_unlock(&wl_list_mutex); return ret; } @@ -1404,7 +1409,9 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl1271_info("down"); + mutex_lock(&wl_list_mutex); list_del(&wl->list); + mutex_unlock(&wl_list_mutex); WARN_ON(wl->state != WL1271_STATE_ON); -- cgit v0.10.2 From db674d249c1fa20fd6731048f41646b3a2e8bdf5 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 16 Mar 2011 23:03:54 +0200 Subject: wl12xx: set the actual tid instead of the ac When passing a tx frame, the driver incorrectly set desc->tid with the ac instead of the actual tid. It has some serious implications when using 802.11n + QoS, as the fw starts a BlockAck with the wrong tid (which finally cause beacon loss and disconnection / some fw crash) Fix it by using the actual tid stored in skb->priority. Reported-by: Shahar Levi Signed-off-by: Eliad Peller Reviewed-by: Juuso Oikarinen Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 109878c..f3031cd 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -221,9 +221,9 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, else desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU); - /* queue (we use same identifiers for tid's and ac's */ + /* queue */ ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); - desc->tid = ac; + desc->tid = skb->priority; if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { /* -- cgit v0.10.2 From 18b92ffaf33c862e852992e82e17b9fffca8d5a4 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 21 Mar 2011 16:35:21 +0200 Subject: wl12xx: set the skbuff priority for dummy packets The firmware requires dummy packets to be sent using TID 7 (WL1271_TID_MGMT). Instead of hardcoding it in the tx_fill_hdr() function, set it when creating the packet itself. This requires Eliad's fix to set the actual TID in the TX descriptor. Cc: Ido Yariv Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 72e84a2..59e0f79 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1243,6 +1243,8 @@ int wl1271_tx_dummy_packet(struct wl1271 *wl) memset(skb->data, 0, TX_DUMMY_PACKET_SIZE); skb->pkt_type = TX_PKT_TYPE_DUMMY_REQ; + /* Dummy packets require the TID to be management */ + skb->priority = WL1271_TID_MGMT; /* CONF_TX_AC_VO */ skb->queue_mapping = 0; diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index f3031cd..db9e47e 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -235,9 +235,6 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, TX_HW_ATTR_SESSION_COUNTER; tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ; - - /* Dummy packets require the TID to be management */ - desc->tid = WL1271_TID_MGMT; } else { /* configure the tx attributes */ tx_attr = -- cgit v0.10.2 From d9482e2b5132fd40f8de528af6bb715accbab11d Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 21 Mar 2011 17:58:32 +0200 Subject: wl12xx: fix SG BT load value to reflect its new meaning The Soft Gemini BT load ratio value has changed its meaning with FW version 6.1.0.0.310. It now means the passive scan compensation percentage during A2DP EDR. Instead of 50, we need to use 200. Fix the SG configuration accordingly. Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 59e0f79..916ebd1 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -54,7 +54,7 @@ static struct conf_drv_settings default_conf = { [CONF_SG_BT_PER_THRESHOLD] = 7500, [CONF_SG_HV3_MAX_OVERRIDE] = 0, [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_SCAN_PROBE_REQ] = 170, [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, -- cgit v0.10.2 From 4623ec7d97afaf7a8489036e2c2e71e8349716b4 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 21 Mar 2011 19:26:41 +0200 Subject: wl12xx: fix a couple of sparse warnings about undeclared functions Fix the following sparse warnings: drivers/net/wireless/wl12xx/main.c:1129:5: warning: symbol '__wl1271_plt_stop' was not declared. Should it be static? drivers/net/wireless/wl12xx/main.c:2988:5: warning: symbol 'wl1271_op_ampdu_action' was not declared. Should it be static? Both functions should be static. Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 916ebd1..85cb4da 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1126,7 +1126,7 @@ out: return ret; } -int __wl1271_plt_stop(struct wl1271 *wl) +static int __wl1271_plt_stop(struct wl1271 *wl) { int ret = 0; @@ -2985,10 +2985,11 @@ out: return ret; } -int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) +static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) { struct wl1271 *wl = hw->priv; int ret; -- cgit v0.10.2 From 4a31c11c7d8c482598754a577a8fb71abb61ffa0 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 21 Mar 2011 23:16:14 +0200 Subject: wl12xx: use a bitmask instead of list of booleans in scanned_ch We were using an array of booleans to mark the channels we had already scanned. This was causing a sparse error, because bool is not a type with defined size. To fix this, use bitmasks instead, which is much cleaner anyway. Thanks Johannes Berg for the idea. Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 85cb4da..9663326 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1423,8 +1423,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) if (wl->scan.state != WL1271_SCAN_STATE_IDLE) { wl->scan.state = WL1271_SCAN_STATE_IDLE; - kfree(wl->scan.scanned_ch); - wl->scan.scanned_ch = NULL; + memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan.req = NULL; ieee80211_scan_completed(wl->hw, true); } @@ -3502,6 +3501,10 @@ int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - 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 * modify them on a per-device basis. diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 420653a..5d0544c 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -48,8 +48,7 @@ void wl1271_scan_complete_work(struct work_struct *work) goto out; wl->scan.state = WL1271_SCAN_STATE_IDLE; - kfree(wl->scan.scanned_ch); - wl->scan.scanned_ch = NULL; + memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan.req = NULL; ieee80211_scan_completed(wl->hw, false); @@ -87,7 +86,7 @@ static int wl1271_get_scan_channels(struct wl1271 *wl, 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_PASSIVE_SCAN)) == passive) && (req->channels[i]->band == band)) { @@ -124,7 +123,7 @@ static int wl1271_get_scan_channels(struct wl1271 *wl, memset(&channels[j].bssid_msb, 0xff, 2); /* Mark the channels we already used */ - wl->scan.scanned_ch[i] = true; + set_bit(i, wl->scan.scanned_ch); j++; } @@ -291,6 +290,12 @@ void wl1271_scan_stm(struct wl1271 *wl) int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, 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) return -EBUSY; @@ -304,10 +309,8 @@ int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, } 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 */ wl->scan.failed = true; ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index b04481a..ba98e18 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -302,9 +302,10 @@ struct wl1271_rx_mem_pool_addr { u32 addr_extra; }; +#define WL1271_MAX_CHANNELS 64 struct wl1271_scan { struct cfg80211_scan_request *req; - bool *scanned_ch; + unsigned long scanned_ch[BITS_TO_LONGS(WL1271_MAX_CHANNELS)]; bool failed; u8 state; u8 ssid[IW_ESSID_MAX_SIZE+1]; -- cgit v0.10.2 From 17e672d6e4b5a8a3f330a70dfd58d25a2cb497b5 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 22 Mar 2011 10:07:47 +0200 Subject: wl12xx: configure channel/band while FW is off Initialize the channel and band from mac80211 conf even when the FW is not yet loaded. This mitigates a bug in AP-mode where the channel was never changed from its initial setting after FW boot and was therefore never configured to FW. Reported-by: Alexander Boukaty Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 9663326..5545c3b 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1706,7 +1706,12 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) mutex_lock(&wl->mutex); 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; } -- cgit v0.10.2 From c1b193eb6557279d037ab18c00ab628c6c78847f Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 23 Mar 2011 22:22:15 +0200 Subject: wl12xx: rearrange some ELP wake_up/sleep calls ELP (Extremely/Enhanced Low Power, or something like that ;)) refers to the powerstate of the 12xx chip, in which very low power is consumed, and no commands (from the host) can be issued until the chip is woken up. Wakeup/sleep commands must be protected by a wl->mutex, so it's generally a good idea to call wakeup/sleep along with the mutex lock/unlock (where needed). However, in some places the wl12xx driver calls wakeup/sleep in some "inner" functions. This result in some "nested" wakeup/sleep calls which might end up letting the chip go to sleep prematurely (e.g. during event handling). Fix it by rearranging the elp calls to come along with mutex_lock/unlock. Signed-off-by: Eliad Peller Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index 413d901..ae69330 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -33,6 +33,7 @@ void wl1271_pspoll_work(struct work_struct *work) { struct delayed_work *dwork; struct wl1271 *wl; + int ret; dwork = container_of(work, struct delayed_work, work); wl = container_of(dwork, struct wl1271, pspoll_work); @@ -55,8 +56,13 @@ void wl1271_pspoll_work(struct work_struct *work) * delivery failure occurred, and no-one changed state since, so * 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_elp_sleep(wl); out: mutex_unlock(&wl->mutex); }; @@ -129,11 +135,6 @@ static int wl1271_event_ps_report(struct wl1271 *wl, /* enable beacon early termination */ ret = wl1271_acx_bet_enable(wl, true); - if (ret < 0) - break; - - /* go to extremely low power mode */ - wl1271_ps_elp_sleep(wl); break; default: break; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 5545c3b..3f6517d 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2792,32 +2792,31 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY; conf_tid->apsd_conf[0] = 0; conf_tid->apsd_conf[1] = 0; - } else { - ret = wl1271_ps_elp_wakeup(wl); - if (ret < 0) - goto out; + goto out; + } - /* - * the txop is confed in units of 32us by the mac80211, - * we need us - */ - 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_ps_elp_wakeup(wl); + if (ret < 0) + goto out; - 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); - if (ret < 0) - goto out_sleep; + /* + * the txop is confed in units of 32us by the mac80211, + * we need us + */ + 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, + wl1271_tx_get_queue(queue), + ps_scheme, CONF_ACK_POLICY_LEGACY, + 0, 0); out_sleep: - wl1271_ps_elp_sleep(wl); - } + wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index 971f13e..b8deada 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -149,9 +149,6 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, case STATION_ACTIVE_MODE: default: wl1271_debug(DEBUG_PSM, "leaving psm"); - ret = wl1271_ps_elp_wakeup(wl); - if (ret < 0) - return ret; /* disable beacon early termination */ ret = wl1271_acx_bet_enable(wl, false); diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index db9e47e..2019ed9 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -511,22 +511,14 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) void wl1271_tx_work_locked(struct wl1271 *wl) { struct sk_buff *skb; - bool woken_up = false; u32 buf_offset = 0; bool sent_packets = false; int ret; if (unlikely(wl->state == WL1271_STATE_OFF)) - goto out; + return; 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); if (ret == -EAGAIN) { /* @@ -573,18 +565,22 @@ out_ack: wl1271_handle_tx_low_watermark(wl); } - -out: - if (woken_up) - wl1271_ps_elp_sleep(wl); } void wl1271_tx_work(struct work_struct *work) { struct wl1271 *wl = container_of(work, struct wl1271, tx_work); + int ret; mutex_lock(&wl->mutex); + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + wl1271_tx_work_locked(wl); + + wl1271_ps_elp_wakeup(wl); +out: mutex_unlock(&wl->mutex); } -- cgit v0.10.2 From 13026decf7b74d0908df034dc6dc86c2caaec939 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Tue, 29 Mar 2011 16:43:50 +0300 Subject: wl12xx: Handle duplicate calling of remove interface Because of the hardware recovery mechanism, its possible the __wl1271_op_remove_interface is called twice. Currently, this leads to a kernel crash even before a kernel WARNing can be issued. Fix this. Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 3f6517d..57d0af6 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1304,6 +1304,16 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, 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) { case NL80211_IFTYPE_STATION: wl->bss_type = BSS_TYPE_STA_BSS; @@ -1372,6 +1382,7 @@ power_off: wl->vif = vif; wl->state = WL1271_STATE_ON; + set_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags); wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str); /* update hw/fw version info in wiphy struct */ @@ -1409,14 +1420,16 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) 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"); mutex_lock(&wl_list_mutex); 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) */ if (wl->bss_type == BSS_TYPE_STA_BSS) ieee80211_enable_dyn_ps(wl->vif); @@ -1428,6 +1441,10 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) 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; mutex_unlock(&wl->mutex); @@ -1464,7 +1481,6 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl->time_offset = 0; wl->session_counter = 0; wl->rate_set = CONF_TX_RATE_MASK_BASIC; - wl->flags = 0; wl->vif = NULL; wl->filters = 0; wl1271_free_ap_keys(wl); @@ -1473,6 +1489,13 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl->ap_ps_map = 0; wl->block_size = 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++) wl->tx_blocks_freed[i] = 0; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index ba98e18..c129459 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -354,7 +354,8 @@ enum wl12xx_flags { WL1271_FLAG_PSPOLL_FAILURE, WL1271_FLAG_STA_STATE_SENT, WL1271_FLAG_FW_TX_BUSY, - WL1271_FLAG_AP_STARTED + WL1271_FLAG_AP_STARTED, + WL1271_FLAG_IF_INITIALIZED, }; struct wl1271_link { -- cgit v0.10.2 From c5745187a4812f2991a58e469a866582a7326d91 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Wed, 30 Mar 2011 16:35:59 +0200 Subject: wl12xx: fix roaming The wl12xx device normally drops all frames coming from BSSID it is not joined with. This behavior is configured today by the wl12xx driver in response to a handful of ieee80211_bss_change and ieee80211_conf_changed notification flags, such as BSS_CHANGED_ASSOC, BSS_CHANGED_BSSID, IEEE80211_CONF_CHANGE_IDLE, etc.. This breaks when we roam to a new BSSID, where authentication frames are sent before any BSS_CHANGED/CONF_CHANGED flags are received. When this happens the hardware silently drops the authentication responses, and the roaming fails. Ideally this aggressive filtering behavior of the device should be disabled upon a notification from mac80211. Such notification will take place after multi-channel support will be added: mac80211 will likely send a remain-on-channel notification to drivers when entering sensitive states (like authentication), otherwise the firmware might jump to different channels (to serve a different role). Until those notifications materialize, disable the hw BSSID filter when authentication requests are sent, so roaming would work. Signed-off-by: Ohad Ben-Cohen Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/io.h b/drivers/net/wireless/wl12xx/io.h index e6199eb..36e1855 100644 --- a/drivers/net/wireless/wl12xx/io.h +++ b/drivers/net/wireless/wl12xx/io.h @@ -171,5 +171,6 @@ int wl1271_free_hw(struct wl1271 *wl); 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 diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 57d0af6..0efa7a0 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1528,7 +1528,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, 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); @@ -1650,7 +1650,7 @@ static int wl1271_unjoin(struct wl1271 *wl) clear_bit(WL1271_FLAG_JOINED, &wl->flags); 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); out: diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 2019ed9..7686bc1 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -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, struct sk_buff *skb) { @@ -350,6 +372,8 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, if (wl->bss_type == BSS_TYPE_AP_BSS) { wl1271_tx_ap_update_inconnection_sta(wl, skb); wl1271_tx_regulate_link(wl, hlid); + } else { + wl1271_tx_update_filters(wl, skb); } wl1271_tx_fill_hdr(wl, skb, extra, info, hlid); -- cgit v0.10.2 From 8bf69aae4cb9b196ba5ac386f83a1ca3865af11f Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Wed, 30 Mar 2011 19:18:31 +0200 Subject: wl12xx: fix "JOIN while associated" commentary Issuing multiple JOIN commands to the wl12xx's firmware, while we're associated, might have undesired implications, so the driver prints a message when that happens, and warn developers who check out the source. Update the commentary in order to consider the one valid scenario where this can happen: roaming. Cautiously keep the message for now, until we either gain confidence there are no unintentional JOIN-while-associated events, or until we move to the new multi-role fw who solves this multiple-join issue for good. Signed-off-by: Ohad Ben-Cohen Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 0efa7a0..01205ea 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1591,10 +1591,10 @@ static int wl1271_join(struct wl1271 *wl, bool set_assoc) * One of the side effects of the JOIN command is that is clears * WPA/WPA2 keys from the chipset. Performing a JOIN while associated * to a WPA/WPA2 access point will therefore kill the data-path. - * Currently there is no supported scenario for JOIN during - * association - if it becomes a supported scenario, the WPA/WPA2 keys - * must be handled somehow. - * + * Currently the only valid scenario for JOIN during association + * is on roaming, in which case we will also be given new keys. + * 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)) wl1271_info("JOIN while associated."); -- cgit v0.10.2 From d29633b40e6afc6b4276a4e381bc532cc84be104 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Thu, 31 Mar 2011 10:06:57 +0200 Subject: wl12xx: Clean up and fix the 128x boot sequence Clean up the boot sequence code & fix the following issues: 1. Always read the registers' values and set the relevant bits instead of zeroing all other bits 2. Handle cases where wl1271_top_reg_read returns an error 3. Verify that the HW can detect the selected clock source 4. Remove 128x PG10 initialization code 5. Configure the MCS PLL to work in HP mode Signed-off-by: Ido Yariv Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 34bf2fe..b5ec2c2 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -523,137 +523,137 @@ static void wl1271_boot_hw_version(struct wl1271 *wl) wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; } -/* - * 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_switch_fref(struct wl1271 *wl, bool *is_ref_clk) +static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl) { - u16 sys_clk_cfg_val; + u16 spare_reg; - /* if working on XTAL-only mode go directly to TCXO TO FREF SWITCH */ - if ((wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL) || - (wl->ref_clock == CONF_REF_CLK_26_M_XTAL)) - return true; + /* 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); - /* Read clock source FREF or TCXO */ - sys_clk_cfg_val = wl1271_top_reg_read(wl, SYS_CLK_CFG_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); - if (sys_clk_cfg_val & PRCM_CM_EN_MUX_WLAN_FREF) { - /* if bit 3 is set - working with FREF clock */ - wl1271_debug(DEBUG_BOOT, "working with FREF clock, skip" - " to FREF"); + /* Delay execution for 15msec, to let the HW settle */ + mdelay(15); - *is_ref_clk = true; - } else { - /* if bit 3 is clear - working with TCXO clock */ - wl1271_debug(DEBUG_BOOT, "working with TCXO clock"); - - /* TCXO to FREF switch, check TXCO clock config */ - if ((wl->tcxo_clock != WL12XX_TCXOCLOCK_16_368) && - (wl->tcxo_clock != WL12XX_TCXOCLOCK_32_736)) { - /* - * not 16.368Mhz and not 32.736Mhz - skip to - * configure ELP stage - */ - wl1271_debug(DEBUG_BOOT, "NEW PLL ALGO:" - " TcxoRefClk=%d - not 16.368Mhz and not" - " 32.736Mhz - skip to configure ELP" - " stage", wl->tcxo_clock); - - *is_ref_clk = false; - } else { - wl1271_debug(DEBUG_BOOT, "NEW PLL ALGO:" - "TcxoRefClk=%d - 16.368Mhz or 32.736Mhz" - " - TCXO to FREF switch", - wl->tcxo_clock); + return 0; +} - return true; - } - } +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 false; + return true; } -static int wl128x_boot_clk(struct wl1271 *wl, bool *is_ref_clk) +static bool wl128x_is_fref_valid(struct wl1271 *wl) { - if (wl128x_switch_fref(wl, is_ref_clk)) { - wl1271_debug(DEBUG_BOOT, "XTAL-only mode go directly to" - " TCXO TO FREF SWITCH"); - /* TCXO to FREF switch - for PG2.0 */ - wl1271_top_reg_write(wl, WL_SPARE_REG, - WL_SPARE_MASK_8526); - - wl1271_top_reg_write(wl, SYS_CLK_CFG_REG, - WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF); - - *is_ref_clk = true; - mdelay(15); - } + u16 fref_detection; - /* Set bit 2 in spare register to avoid illegal access */ - wl1271_top_reg_write(wl, WL_SPARE_REG, WL_SPARE_VAL); + fref_detection = wl1271_top_reg_read(wl, FREF_CLK_DETECT_REG); + if (fref_detection & FREF_CLK_DETECT_FAIL) + return false; - /* working with TCXO clock */ - if ((*is_ref_clk == false) && - ((wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8) || - (wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6))) { - wl1271_debug(DEBUG_BOOT, "16_8_M or 33_6_M TCXO detected"); + return true; +} - /* Manually Configure MCS PLL settings PG2.0 Only */ - 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); - } else { - int pll_config; - u16 mcs_pll_config_val; +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); - /* - * Configure MCS PLL settings to FREF Freq - * Set the values that determine the time elapse since the PLL's - * get their enable signal until the lock indication is set - */ - wl1271_top_reg_write(wl, PLL_LOCK_COUNTERS_REG, - PLL_LOCK_COUNTERS_COEX | PLL_LOCK_COUNTERS_MCS); + return 0; +} - mcs_pll_config_val = wl1271_top_reg_read(wl, - MCS_PLL_CONFIG_REG); - /* - * Set the MCS PLL input frequency value according to the - * reference clock value detected/read - */ - if (*is_ref_clk == false) { - if ((wl->tcxo_clock == WL12XX_TCXOCLOCK_19_2) || - (wl->tcxo_clock == WL12XX_TCXOCLOCK_38_4)) - pll_config = 1; - else if ((wl->tcxo_clock == WL12XX_TCXOCLOCK_26) - || - (wl->tcxo_clock == WL12XX_TCXOCLOCK_52)) - pll_config = 2; - else - return -EINVAL; - } else { - if ((wl->ref_clock == CONF_REF_CLK_19_2_E) || - (wl->ref_clock == CONF_REF_CLK_38_4_E)) - pll_config = 1; - else if ((wl->ref_clock == CONF_REF_CLK_26_E) || - (wl->ref_clock == CONF_REF_CLK_52_E)) - pll_config = 2; - else - return -EINVAL; - } +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); - mcs_pll_config_val |= (pll_config << (MCS_SEL_IN_FREQ_SHIFT)) & - (MCS_SEL_IN_FREQ_MASK); - wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, - mcs_pll_config_val); + 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; } - return 0; + /* 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) @@ -713,10 +713,10 @@ int wl1271_load_firmware(struct wl1271 *wl) { int ret = 0; u32 tmp, clk; - bool is_ref_clk = false; + int selected_clock = -1; if (wl->chip.id == CHIP_ID_1283_PG20) { - ret = wl128x_boot_clk(wl, &is_ref_clk); + ret = wl128x_boot_clk(wl, &selected_clock); if (ret < 0) goto out; } else { @@ -741,10 +741,7 @@ int wl1271_load_firmware(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); if (wl->chip.id == CHIP_ID_1283_PG20) { - if (is_ref_clk == false) - clk |= ((wl->tcxo_clock & 0x3) << 1) << 4; - else - clk |= ((wl->ref_clock & 0x3) << 1) << 4; + clk |= ((selected_clock & 0x3) << 1) << 4; } else { clk |= (wl->ref_clock << 1) << 4; } diff --git a/drivers/net/wireless/wl12xx/boot.h b/drivers/net/wireless/wl12xx/boot.h index 1f5ee31..d9de64a 100644 --- a/drivers/net/wireless/wl12xx/boot.h +++ b/drivers/net/wireless/wl12xx/boot.h @@ -107,6 +107,7 @@ struct wl1271_static_data { #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 diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h index eb8aaca..c1a743e 100644 --- a/include/linux/wl12xx.h +++ b/include/linux/wl12xx.h @@ -26,10 +26,12 @@ /* Reference clock values */ enum { - WL12XX_REFCLOCK_19 = 0, /* 19.2 MHz */ - WL12XX_REFCLOCK_26 = 1, /* 26 MHz */ - WL12XX_REFCLOCK_38 = 2, /* 38.4 MHz */ - WL12XX_REFCLOCK_54 = 3, /* 54 MHz */ + WL12XX_REFCLOCK_19 = 0, /* 19.2 MHz */ + WL12XX_REFCLOCK_26 = 1, /* 26 MHz */ + WL12XX_REFCLOCK_38 = 2, /* 38.4 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 */ -- cgit v0.10.2 From 0da13da767cd568c1fe2a7b5b936e86e521b5ae7 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Thu, 31 Mar 2011 10:06:58 +0200 Subject: wl12xx: Clean up the block size alignment code Simplify and clean up the block size alignment code: 1. Set the block size according to the padding field type, as it cannot exceed the maximum value this field can hold. 2. Move the alignment code into a function instead of duplicating it in multiple places. 3. In the current implementation, the block_size member can be misleading because a zero value actually means that there's no need to align. Declare a block size alignment quirk instead. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 2dbc083..cf46607 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -514,7 +514,7 @@ int wl1271_chip_specific_init(struct wl1271 *wl) if (wl->chip.id == CHIP_ID_1283_PG20) { u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE; - if (wl1271_set_block_size(wl)) + if (wl->quirks & WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT) /* Enable SDIO padding */ host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK; diff --git a/drivers/net/wireless/wl12xx/io.c b/drivers/net/wireless/wl12xx/io.c index aa40c98..da5c1ad 100644 --- a/drivers/net/wireless/wl12xx/io.c +++ b/drivers/net/wireless/wl12xx/io.c @@ -29,6 +29,7 @@ #include "wl12xx.h" #include "wl12xx_80211.h" #include "io.h" +#include "tx.h" #define OCP_CMD_LOOP 32 @@ -46,7 +47,7 @@ bool wl1271_set_block_size(struct wl1271 *wl) { if (wl->if_ops->set_block_size) { - wl->if_ops->set_block_size(wl); + wl->if_ops->set_block_size(wl, WL12XX_BUS_BLOCK_SIZE); return true; } diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 01205ea..1cd3963 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1017,6 +1017,8 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) 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: @@ -1487,7 +1489,6 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map)); wl->ap_fw_ps_map = 0; wl->ap_ps_map = 0; - wl->block_size = 0; /* * this is performed after the cancel_work calls and the associated @@ -3632,7 +3633,6 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->ap_ps_map = 0; wl->ap_fw_ps_map = 0; wl->quirks = 0; - wl->block_size = 0; memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 2ade222..8246e9d 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -51,15 +51,10 @@ static const struct sdio_device_id wl1271_devices[] = { }; MODULE_DEVICE_TABLE(sdio, wl1271_devices); -/* The max SDIO block size is 256 when working with tx padding to SDIO block */ -#define TX_PAD_SDIO_BLK_SIZE 256 - -static void wl1271_sdio_set_block_size(struct wl1271 *wl) +static void wl1271_sdio_set_block_size(struct wl1271 *wl, unsigned int blksz) { - wl->block_size = TX_PAD_SDIO_BLK_SIZE; - sdio_claim_host(wl->if_priv); - sdio_set_block_size(wl->if_priv, TX_PAD_SDIO_BLK_SIZE); + sdio_set_block_size(wl->if_priv, blksz); sdio_release_host(wl->if_priv); } @@ -178,9 +173,6 @@ static int wl1271_sdio_power_on(struct wl1271 *wl) sdio_claim_host(func); sdio_enable_func(func); - /* Set the default block size in case it was modified */ - sdio_set_block_size(func, 0); - out: return ret; } diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 7686bc1..ba69ba7 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -149,6 +149,15 @@ 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, u32 buf_offset, u8 hlid) { @@ -174,10 +183,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, /* approximate the number of blocks required for this packet in the firmware */ - if (wl->block_size) - len = ALIGN(total_len, wl->block_size); - else - len = total_len; + len = wl12xx_calc_packet_alignment(wl, total_len); total_blocks = (len + TX_HW_BLOCK_SIZE - 1) / TX_HW_BLOCK_SIZE + spare_blocks; @@ -291,9 +297,9 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY; desc->reserved = 0; - if (wl->block_size) { - aligned_len = ALIGN(skb->len, wl->block_size); + aligned_len = wl12xx_calc_packet_alignment(wl, skb->len); + if (wl->chip.id == CHIP_ID_1283_PG20) { desc->wl128x_mem.extra_bytes = aligned_len - skb->len; desc->length = cpu_to_le16(aligned_len >> 2); @@ -306,8 +312,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, } else { int pad; - /* align the length (and store in terms of words) */ - aligned_len = ALIGN(skb->len, WL1271_TX_ALIGN_TO); + /* Store the aligned length in terms of words */ desc->length = cpu_to_le16(aligned_len >> 2); /* calculate number of padding bytes */ @@ -386,10 +391,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, * In special cases, we want to align to a specific block size * (eg. for wl128x with SDIO we align to 256). */ - if (wl->block_size) - total_len = ALIGN(skb->len, wl->block_size); - else - 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); memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len); diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index a3877ba..d6b05d9 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -89,6 +89,15 @@ struct wl128x_tx_mem { 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 { /* Length of packet in words, including descriptor+header+data */ __le16 length; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index c129459..c7c42b6 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -323,7 +323,7 @@ struct wl1271_if_operations { struct device* (*dev)(struct wl1271 *wl); void (*enable_irq)(struct wl1271 *wl); void (*disable_irq)(struct wl1271 *wl); - void (*set_block_size) (struct wl1271 *wl); + void (*set_block_size) (struct wl1271 *wl, unsigned int blksz); }; #define MAX_NUM_KEYS 14 @@ -558,7 +558,6 @@ struct wl1271 { bool ba_support; u8 ba_rx_bitmap; - u32 block_size; int tcxo_clock; /* @@ -610,12 +609,15 @@ int wl1271_plt_stop(struct wl1271 *wl); /* Quirks */ /* 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) +#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 -- cgit v0.10.2 From 990f5de7384f9e5922e4c7c7572cbf4f29a9441e Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Thu, 31 Mar 2011 10:06:59 +0200 Subject: wl12xx: Clean up the dummy packet mechanism The current implementation allocates a skb each time one is requested by the firmware. Since dummy packets are handled differently than regular packets, the skb needs to be marked. Currently, this is done by setting the pkt_type member to 5. This might not be safe, as we cannot be sure that there won't be any other packets with this pkt_type value. Since the packet does not change from one request to another, we can simply allocate a dummy packet template and always send it. All changes to the skb done during packet preparation must be reverted, so the same skb can be reused. The dummy packets are not transmitted, therefore there's no need to set the BSSID or our own MAC address. In addition, the header portion of the packet was zeroed by mistake, so fix that as well. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 1cd3963..7dce24c 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1212,20 +1212,46 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) spin_unlock_irqrestore(&wl->wl_lock, flags); } -#define TX_DUMMY_PACKET_SIZE 1400 int wl1271_tx_dummy_packet(struct wl1271 *wl) { - struct sk_buff *skb = NULL; + 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)) + +struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) +{ + struct sk_buff *skb; struct ieee80211_hdr_3addr *hdr; - int ret = 0; + 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( - sizeof(struct wl1271_tx_hw_descr) + sizeof(*hdr) + - TX_DUMMY_PACKET_SIZE); + skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE); if (!skb) { - wl1271_warning("failed to allocate buffer for dummy packet"); - ret = -ENOMEM; - goto out; + wl1271_warning("Failed to allocate a dummy packet skb"); + return NULL; } skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr)); @@ -1233,29 +1259,22 @@ int wl1271_tx_dummy_packet(struct wl1271 *wl) 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_FCTL_TODS | - IEEE80211_STYPE_NULLFUNC); - - memcpy(hdr->addr1, wl->bssid, ETH_ALEN); - memcpy(hdr->addr2, wl->mac_addr, ETH_ALEN); - memcpy(hdr->addr3, wl->bssid, ETH_ALEN); + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_TODS); - skb_put(skb, TX_DUMMY_PACKET_SIZE); + memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size); - memset(skb->data, 0, TX_DUMMY_PACKET_SIZE); - - skb->pkt_type = TX_PKT_TYPE_DUMMY_REQ; /* Dummy packets require the TID to be management */ skb->priority = WL1271_TID_MGMT; - /* CONF_TX_AC_VO */ - skb->queue_mapping = 0; - wl1271_op_tx(wl->hw, skb); + /* Initialize all fields that might be used */ + skb->queue_mapping = 0; + memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info)); -out: - return ret; + return skb; } + static struct notifier_block wl1271_dev_notifier = { .notifier_call = wl1271_dev_notify, }; @@ -3653,11 +3672,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void) goto err_hw; } + wl->dummy_packet = wl12xx_alloc_dummy_packet(wl); + if (!wl->dummy_packet) { + ret = -ENOMEM; + goto err_aggr; + } + /* Register platform device */ ret = platform_device_register(wl->plat_dev); if (ret) { wl1271_error("couldn't register platform device"); - goto err_aggr; + goto err_dummy_packet; } dev_set_drvdata(&wl->plat_dev->dev, wl); @@ -3683,6 +3708,9 @@ err_bt_coex_state: err_platform: platform_device_unregister(wl->plat_dev); +err_dummy_packet: + dev_kfree_skb(wl->dummy_packet); + err_aggr: free_pages((unsigned long)wl->aggr_buf, order); @@ -3702,6 +3730,7 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw); int wl1271_free_hw(struct wl1271 *wl) { platform_device_unregister(wl->plat_dev); + dev_kfree_skb(wl->dummy_packet); free_pages((unsigned long)wl->aggr_buf, get_order(WL1271_AGGR_BUFFER_SIZE)); kfree(wl->plat_dev); diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index ba69ba7..67245ad 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -219,6 +219,11 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, 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, u32 extra, struct ieee80211_tx_info *control, u8 hlid) @@ -253,7 +258,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); desc->tid = skb->priority; - if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { + 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 @@ -396,6 +401,10 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, memcpy(wl->aggr_buf + buf_offset, skb->data, 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; } @@ -508,10 +517,23 @@ out: 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) - return wl1271_ap_skb_dequeue(wl); + skb = wl1271_ap_skb_dequeue(wl); + else + skb = wl1271_sta_skb_dequeue(wl); - return 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 skb; } static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) @@ -519,7 +541,9 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) unsigned long flags; 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); skb_queue_head(&wl->links[hlid].tx_queue[q], skb); @@ -628,8 +652,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, skb = wl->tx_frames[id]; info = IEEE80211_SKB_CB(skb); - if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { - dev_kfree_skb(skb); + if (wl12xx_is_dummy_packet(wl, skb)) { wl1271_free_tx_id(wl, id); return; } @@ -764,9 +787,7 @@ void wl1271_tx_reset(struct wl1271 *wl) wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); - if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { - dev_kfree_skb(skb); - } else { + if (!wl12xx_is_dummy_packet(wl, skb)) { info = IEEE80211_SKB_CB(skb); info->status.rates[0].idx = -1; info->status.rates[0].count = 0; @@ -792,9 +813,7 @@ void wl1271_tx_reset(struct wl1271 *wl) wl1271_free_tx_id(wl, i); wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); - if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { - dev_kfree_skb(skb); - } else { + if (!wl12xx_is_dummy_packet(wl, skb)) { /* * Remove private headers before passing the skb to * mac80211 diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index d6b05d9..fc7835c 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -42,8 +42,6 @@ #define TX_HW_ATTR_TX_CMPLT_REQ BIT(12) #define TX_HW_ATTR_TX_DUMMY_REQ BIT(13) -#define TX_PKT_TYPE_DUMMY_REQ 5 - #define TX_HW_ATTR_OFST_SAVE_RETRIES 0 #define TX_HW_ATTR_OFST_HEADER_PAD 1 #define TX_HW_ATTR_OFST_SESSION_COUNTER 2 diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index c7c42b6..1b430d2 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -356,6 +356,7 @@ enum wl12xx_flags { WL1271_FLAG_FW_TX_BUSY, WL1271_FLAG_AP_STARTED, WL1271_FLAG_IF_INITIALIZED, + WL1271_FLAG_DUMMY_PACKET_PENDING, }; struct wl1271_link { @@ -461,6 +462,9 @@ struct wl1271 { /* Intermediate buffer, used for packet aggregation */ u8 *aggr_buf; + /* Reusable dummy packet template */ + struct sk_buff *dummy_packet; + /* Network stack work */ struct work_struct netstack_work; -- cgit v0.10.2 From d2f4d47d84f8c665ab9babb2cc84d2e7872a96e1 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Thu, 31 Mar 2011 10:07:00 +0200 Subject: wl12xx: Simplify TX blocks accounting The total number of TX memory blocks may change when the dynamic memory option is enabled. The current implementation only tracks the available memory blocks, which over-complicates TX blocks accounting. By tracking the number of allocated blocks, calculation of the number of available blocks becomes simpler and cleaner. It simply equals the total number of TX memory blocks minus the allocated ones. Also, remove some unnecessary castings and use union member accesses instead. Signed-off-by: Ido Yariv Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index e005aa4..b277947 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1081,8 +1081,6 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl) wl1271_debug(DEBUG_TX, "available tx blocks: %d", wl->tx_blocks_available); - wl->tx_new_total = wl->tx_blocks_available; - return 0; } diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 7dce24c..1feb955 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -622,7 +622,7 @@ static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_common_status *status = &full_status->common; struct timespec ts; u32 old_tx_blk_count = wl->tx_blocks_available; - u32 total = 0; + u32 freed_blocks = 0; int i; if (wl->bss_type == BSS_TYPE_AP_BSS) { @@ -631,15 +631,6 @@ static void wl1271_fw_status(struct wl1271 *wl, } else { wl1271_raw_read(wl, FW_STATUS_ADDR, status, sizeof(struct wl1271_fw_sta_status), false); - - /* Update tx total blocks change */ - wl->tx_total_diff += - ((struct wl1271_fw_sta_status *)status)->tx_total - - wl->tx_new_total; - - /* Update total tx blocks */ - wl->tx_new_total = - ((struct wl1271_fw_sta_status *)status)->tx_total; } wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " @@ -651,34 +642,38 @@ static void wl1271_fw_status(struct wl1271 *wl, /* update number of available TX blocks */ for (i = 0; i < NUM_TX_QUEUES; i++) { - total += le32_to_cpu(status->tx_released_blks[i]) - - wl->tx_blocks_freed[i]; + freed_blocks += le32_to_cpu(status->tx_released_blks[i]) - + wl->tx_blocks_freed[i]; wl->tx_blocks_freed[i] = le32_to_cpu(status->tx_released_blks[i]); - } - /* - * By adding the freed blocks to tx_total_diff we are actually - * moving them to the RX pool. - */ - wl->tx_total_diff += total; + wl->tx_allocated_blocks -= freed_blocks; + + 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); + wl->tx_blocks_available += freed_blocks; + } else { + int avail = full_status->sta.tx_total - wl->tx_allocated_blocks; - /* if we have positive difference, add the blocks to the TX pool */ - if (wl->tx_total_diff >= 0) { - wl->tx_blocks_available += wl->tx_total_diff; - wl->tx_total_diff = 0; + /* + * 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); - /* for AP update num of allocated TX blocks per link and ps status */ - if (wl->bss_type == BSS_TYPE_AP_BSS) - wl1271_irq_update_links_status(wl, &full_status->ap); - /* update the host-chipset time offset */ getnstimeofday(&ts); wl->time_offset = (timespec_to_ns(&ts) >> 10) - @@ -1495,6 +1490,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl->psm_entry_retry = 0; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->tx_blocks_available = 0; + wl->tx_allocated_blocks = 0; wl->tx_results_count = 0; wl->tx_packets_count = 0; wl->tx_security_last_seq = 0; diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 67245ad..7a3339f 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -203,6 +203,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, desc->id = id; wl->tx_blocks_available -= total_blocks; + wl->tx_allocated_blocks += total_blocks; if (wl->bss_type == BSS_TYPE_AP_BSS) wl->links[hlid].allocated_blks += total_blocks; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 1b430d2..9ccbcfd 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -418,10 +418,8 @@ struct wl1271 { /* Accounting for allocated / available TX blocks on HW */ u32 tx_blocks_freed[NUM_TX_QUEUES]; u32 tx_blocks_available; + u32 tx_allocated_blocks; u32 tx_results_count; - /* Indicates how many memory blocks should be moved to the RX pool */ - int tx_total_diff; - u32 tx_new_total; /* Transmitted TX packets counter for chipset interface */ u32 tx_packets_count; -- cgit v0.10.2 From 341b7cde6ccc60672fcd7fc84dd24a1b7c0b8d94 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Thu, 31 Mar 2011 10:07:01 +0200 Subject: wl12xx: Handle platforms without level trigger interrupts Some platforms are incapable of triggering on level interrupts. Add a platform quirks member in the platform data structure, as well as an edge interrupt quirk which can be set on such platforms. When the interrupt is requested with IRQF_TRIGGER_RISING, IRQF_ONESHOT cannot be used, as we might miss interrupts that occur after the FW status is cleared and before the threaded interrupt handler exits. Moreover, when IRQF_ONESHOT is not set, iterating more than once in the threaded interrupt handler introduces a few race conditions between this handler and the hardirq handler. Currently this is worked around by limiting the loop to one iteration only. This workaround has an impact on performance. To remove to this restriction, the race conditions will need to be addressed. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 1feb955..7126506 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "wl12xx.h" #include "wl12xx_80211.h" @@ -719,6 +720,13 @@ irqreturn_t wl1271_irq(int irq, void *cookie) set_bit(WL1271_FLAG_TX_PENDING, &wl->flags); 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); wl1271_debug(DEBUG_IRQ, "IRQ work"); @@ -3648,6 +3656,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->ap_ps_map = 0; wl->ap_fw_ps_map = 0; wl->quirks = 0; + wl->platform_quirks = 0; memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 8246e9d..bcd4ad7 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -220,6 +220,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, struct ieee80211_hw *hw; const struct wl12xx_platform_data *wlan_data; struct wl1271 *wl; + unsigned long irqflags; int ret; /* We are only able to handle the wlan function */ @@ -251,9 +252,15 @@ static int __devinit wl1271_probe(struct sdio_func *func, wl->irq = wlan_data->irq; 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, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + irqflags, DRIVER_NAME, wl); if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index 7b82b5f..51662bb 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -364,6 +364,7 @@ static int __devinit wl1271_probe(struct spi_device *spi) struct wl12xx_platform_data *pdata; struct ieee80211_hw *hw; struct wl1271 *wl; + unsigned long irqflags; int ret; pdata = spi->dev.platform_data; @@ -402,6 +403,12 @@ static int __devinit wl1271_probe(struct spi_device *spi) 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; if (wl->irq < 0) { @@ -411,7 +418,7 @@ static int __devinit wl1271_probe(struct spi_device *spi) } ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + irqflags, DRIVER_NAME, wl); if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 9ccbcfd..fb2b79f 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -579,6 +579,9 @@ struct wl1271 { /* Quirks of specific hardware revisions */ unsigned int quirks; + + /* Platform limitations */ + unsigned int platform_quirks; }; struct wl1271_station { diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h index c1a743e..4b69739 100644 --- a/include/linux/wl12xx.h +++ b/include/linux/wl12xx.h @@ -53,8 +53,12 @@ struct wl12xx_platform_data { bool use_eeprom; 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 int wl12xx_set_platform_data(const struct wl12xx_platform_data *data); -- cgit v0.10.2 From 6277ed65704d19377b0874618e5f23d64c9e71a6 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 1 Apr 2011 17:49:54 +0300 Subject: wl12xx: use kstrtoul functions Use the new kstrtoul functions instead of the deprecated strict_strtoul(). Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 8e75b09..70ab198 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -267,7 +267,7 @@ static ssize_t gpio_power_write(struct file *file, } buf[len] = '\0'; - ret = strict_strtoul(buf, 0, &value); + ret = kstrtoul(buf, 0, &value); if (ret < 0) { wl1271_warning("illegal value in gpio_power"); return -EINVAL; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 7126506..a5a5d01 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3397,8 +3397,7 @@ static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev, unsigned long res; int ret; - ret = strict_strtoul(buf, 10, &res); - + ret = kstrtoul(buf, 10, &res); if (ret < 0) { wl1271_warning("incorrect value written to bt_coex_mode"); return count; -- cgit v0.10.2 From afb7d3cd805df7a206439a7e7b5d1167d2bb06f6 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 1 Apr 2011 20:48:02 +0300 Subject: wl12xx: move hardcoded hci_io_ds value into the conf struct Instead of hardcoding the hci_io_ds configuration that we write to the SDIO_IO_DS top registed, read it from the default configuration so that it's easier to change for different platforms. Reported-by: Ido Yariv Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index b5ec2c2..2b0cf85 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -779,7 +779,7 @@ int wl1271_load_firmware(struct wl1271 *wl) * to upload_fw) */ if (wl->chip.id == CHIP_ID_1283_PG20) - wl1271_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA); + wl1271_top_reg_write(wl, SDIO_IO_DS, wl->conf.hci_io_ds); ret = wl1271_boot_upload_firmware(wl); if (ret < 0) diff --git a/drivers/net/wireless/wl12xx/boot.h b/drivers/net/wireless/wl12xx/boot.h index d9de64a..e8f8255 100644 --- a/drivers/net/wireless/wl12xx/boot.h +++ b/drivers/net/wireless/wl12xx/boot.h @@ -117,10 +117,13 @@ struct wl1271_static_data { #define SDIO_IO_DS 0xd14 /* SDIO/wSPI DS configuration values */ -#define HCI_IO_DS_8MA 0 -#define HCI_IO_DS_4MA 1 /* default */ -#define HCI_IO_DS_6MA 2 -#define HCI_IO_DS_2MA 3 +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 diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 743bd0b..52269d4 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1206,6 +1206,7 @@ struct conf_drv_settings { struct conf_ht_setting ht; struct conf_memory_settings mem_wl127x; struct conf_memory_settings mem_wl128x; + u8 hci_io_ds; }; #endif diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index a5a5d01..732fd21 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -319,6 +319,7 @@ static struct conf_drv_settings default_conf = { .min_req_rx_blocks = 22, .tx_min = 27, }, + .hci_io_ds = HCI_IO_DS_6MA, }; static void __wl1271_op_remove_interface(struct wl1271 *wl); -- cgit v0.10.2 From 4ec23d6e136c890806f0e00bcf24e2e3a242b30a Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 1 Apr 2011 20:55:01 +0300 Subject: wl12xx: remove unused conf_radio_params structure This structure has not been used anymore since commit e6b190ff3c2f4e4859502c41fa17b5c595e82000. Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 52269d4..6c9e3a6 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1020,15 +1020,6 @@ enum single_dual_band_enum { #define CONF_NUMBER_OF_CHANNELS_2_4 14 #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 { /* enable dco itrim */ u8 enable; -- cgit v0.10.2 From cf27d8677515441602f5e4e40f90448e964504b8 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 1 Apr 2011 21:08:23 +0300 Subject: wl12xx: fix sparse warning about undeclared wl12xx_alloc_dummy_packet Fix sparse warning: CHECK drivers/net/wireless/wl12xx/main.c drivers/net/wireless/wl12xx/main.c:1246:17: warning: symbol 'wl12xx_alloc_dummy_packet' was not declared. Should it be static? Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 732fd21..866453b 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1243,7 +1243,7 @@ int wl1271_tx_dummy_packet(struct wl1271 *wl) */ #define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512)) -struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) +static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) { struct sk_buff *skb; struct ieee80211_hdr_3addr *hdr; -- cgit v0.10.2