diff options
Diffstat (limited to 'package/kernel/mac80211/patches/905-wlcore-wl12xx-wl18xx-simplify-fw_status-handling.patch')
-rw-r--r-- | package/kernel/mac80211/patches/905-wlcore-wl12xx-wl18xx-simplify-fw_status-handling.patch | 731 |
1 files changed, 731 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/905-wlcore-wl12xx-wl18xx-simplify-fw_status-handling.patch b/package/kernel/mac80211/patches/905-wlcore-wl12xx-wl18xx-simplify-fw_status-handling.patch new file mode 100644 index 0000000000..c669797ef3 --- /dev/null +++ b/package/kernel/mac80211/patches/905-wlcore-wl12xx-wl18xx-simplify-fw_status-handling.patch @@ -0,0 +1,731 @@ +Instead of splitting the fw_status into 2 and using some +complex calculations, read the fw status and let each low-level +driver (wl12xx/wl18xx) convert it into a common struct. + +This is required for the upcoming fw api changes, which +break the current logic anyway. + +Signed-off-by: Eliad Peller <eliad@wizery.com> + +--- +drivers/net/wireless/ti/wl12xx/main.c | 35 ++++++++++- + drivers/net/wireless/ti/wl12xx/wl12xx.h | 50 ++++++++++++++++ + drivers/net/wireless/ti/wl18xx/main.c | 39 ++++++++++++- + drivers/net/wireless/ti/wl18xx/tx.c | 4 +- + drivers/net/wireless/ti/wl18xx/wl18xx.h | 53 +++++++++++++++++ + drivers/net/wireless/ti/wlcore/cmd.c | 11 +++- + drivers/net/wireless/ti/wlcore/hw_ops.h | 9 +++ + drivers/net/wireless/ti/wlcore/main.c | 96 +++++++++++++++---------------- + drivers/net/wireless/ti/wlcore/rx.c | 2 +- + drivers/net/wireless/ti/wlcore/rx.h | 2 +- + drivers/net/wireless/ti/wlcore/wlcore.h | 7 ++- + drivers/net/wireless/ti/wlcore/wlcore_i.h | 72 ++++++++++------------- + 12 files changed, 277 insertions(+), 103 deletions(-) + +--- a/drivers/net/wireless/ti/wl12xx/main.c ++++ b/drivers/net/wireless/ti/wl12xx/main.c +@@ -1378,7 +1378,7 @@ static u32 wl12xx_get_rx_packet_len(stru + + static int wl12xx_tx_delayed_compl(struct wl1271 *wl) + { +- if (wl->fw_status_1->tx_results_counter == ++ if (wl->fw_status->tx_results_counter == + (wl->tx_results_count & 0xff)) + return 0; + +@@ -1438,6 +1438,37 @@ out: + return ret; + } + ++static void wl12xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status, ++ struct wl_fw_status *fw_status) ++{ ++ struct wl12xx_fw_status *int_fw_status = raw_fw_status; ++ ++ fw_status->intr = le32_to_cpu(int_fw_status->intr); ++ fw_status->fw_rx_counter = int_fw_status->fw_rx_counter; ++ fw_status->drv_rx_counter = int_fw_status->drv_rx_counter; ++ fw_status->tx_results_counter = int_fw_status->tx_results_counter; ++ fw_status->rx_pkt_descs = int_fw_status->rx_pkt_descs; ++ ++ fw_status->fw_localtime = le32_to_cpu(int_fw_status->fw_localtime); ++ fw_status->link_ps_bitmap = le32_to_cpu(int_fw_status->link_ps_bitmap); ++ fw_status->link_fast_bitmap = ++ le32_to_cpu(int_fw_status->link_fast_bitmap); ++ fw_status->total_released_blks = ++ le32_to_cpu(int_fw_status->total_released_blks); ++ fw_status->tx_total = le32_to_cpu(int_fw_status->tx_total); ++ ++ fw_status->counters.tx_released_pkts = ++ int_fw_status->counters.tx_released_pkts; ++ fw_status->counters.tx_lnk_free_pkts = ++ int_fw_status->counters.tx_lnk_free_pkts; ++ fw_status->counters.tx_voice_released_blks = ++ int_fw_status->counters.tx_voice_released_blks; ++ fw_status->counters.tx_last_rate = ++ int_fw_status->counters.tx_last_rate; ++ ++ fw_status->log_start_addr = le32_to_cpu(int_fw_status->log_start_addr); ++} ++ + static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl, + struct wl12xx_vif *wlvif) + { +@@ -1677,6 +1708,7 @@ static struct wlcore_ops wl12xx_ops = { + .tx_delayed_compl = wl12xx_tx_delayed_compl, + .hw_init = wl12xx_hw_init, + .init_vif = NULL, ++ .convert_fw_status = wl12xx_convert_fw_status, + .sta_get_ap_rate_mask = wl12xx_sta_get_ap_rate_mask, + .get_pg_ver = wl12xx_get_pg_ver, + .get_mac = wl12xx_get_mac, +@@ -1725,6 +1757,7 @@ static int wl12xx_setup(struct wl1271 *w + wl->band_rate_to_idx = wl12xx_band_rate_to_idx; + wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX; + wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0; ++ wl->fw_status_len = sizeof(struct wl12xx_fw_status); + wl->fw_status_priv_len = 0; + wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics); + wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl12xx_ht_cap); +--- a/drivers/net/wireless/ti/wl12xx/wl12xx.h ++++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h +@@ -79,4 +79,54 @@ struct wl12xx_priv { + struct wl127x_rx_mem_pool_addr *rx_mem_addr; + }; + ++struct wl12xx_fw_packet_counters { ++ /* Cumulative counter of released packets per AC */ ++ u8 tx_released_pkts[NUM_TX_QUEUES]; ++ ++ /* Cumulative counter of freed packets per HLID */ ++ u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS]; ++ ++ /* Cumulative counter of released Voice memory blocks */ ++ u8 tx_voice_released_blks; ++ ++ /* Tx rate of the last transmitted packet */ ++ u8 tx_last_rate; ++ ++ u8 padding[2]; ++} __packed; ++ ++/* FW status registers */ ++struct wl12xx_fw_status { ++ __le32 intr; ++ u8 fw_rx_counter; ++ u8 drv_rx_counter; ++ u8 reserved; ++ u8 tx_results_counter; ++ __le32 rx_pkt_descs[WL12XX_NUM_RX_DESCRIPTORS]; ++ ++ __le32 fw_localtime; ++ ++ /* ++ * A bitmap (where each bit represents a single HLID) ++ * to indicate if the station is in PS mode. ++ */ ++ __le32 link_ps_bitmap; ++ ++ /* ++ * A bitmap (where each bit represents a single HLID) to indicate ++ * if the station is in Fast mode ++ */ ++ __le32 link_fast_bitmap; ++ ++ /* Cumulative counter of total released mem blocks since FW-reset */ ++ __le32 total_released_blks; ++ ++ /* Size (in Memory Blocks) of TX pool */ ++ __le32 tx_total; ++ ++ struct wl12xx_fw_packet_counters counters; ++ ++ __le32 log_start_addr; ++} __packed; ++ + #endif /* __WL12XX_PRIV_H__ */ +--- a/drivers/net/wireless/ti/wl18xx/main.c ++++ b/drivers/net/wireless/ti/wl18xx/main.c +@@ -1133,6 +1133,39 @@ static int wl18xx_hw_init(struct wl1271 + return ret; + } + ++static void wl18xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status, ++ struct wl_fw_status *fw_status) ++{ ++ struct wl18xx_fw_status *int_fw_status = raw_fw_status; ++ ++ fw_status->intr = le32_to_cpu(int_fw_status->intr); ++ fw_status->fw_rx_counter = int_fw_status->fw_rx_counter; ++ fw_status->drv_rx_counter = int_fw_status->drv_rx_counter; ++ fw_status->tx_results_counter = int_fw_status->tx_results_counter; ++ fw_status->rx_pkt_descs = int_fw_status->rx_pkt_descs; ++ ++ fw_status->fw_localtime = le32_to_cpu(int_fw_status->fw_localtime); ++ fw_status->link_ps_bitmap = le32_to_cpu(int_fw_status->link_ps_bitmap); ++ fw_status->link_fast_bitmap = ++ le32_to_cpu(int_fw_status->link_fast_bitmap); ++ fw_status->total_released_blks = ++ le32_to_cpu(int_fw_status->total_released_blks); ++ fw_status->tx_total = le32_to_cpu(int_fw_status->tx_total); ++ ++ fw_status->counters.tx_released_pkts = ++ int_fw_status->counters.tx_released_pkts; ++ fw_status->counters.tx_lnk_free_pkts = ++ int_fw_status->counters.tx_lnk_free_pkts; ++ fw_status->counters.tx_voice_released_blks = ++ int_fw_status->counters.tx_voice_released_blks; ++ fw_status->counters.tx_last_rate = ++ int_fw_status->counters.tx_last_rate; ++ ++ fw_status->log_start_addr = le32_to_cpu(int_fw_status->log_start_addr); ++ ++ fw_status->priv = &int_fw_status->priv; ++} ++ + static void wl18xx_set_tx_desc_csum(struct wl1271 *wl, + struct wl1271_tx_hw_descr *desc, + struct sk_buff *skb) +@@ -1572,7 +1605,7 @@ static bool wl18xx_lnk_high_prio(struct + { + u8 thold; + struct wl18xx_fw_status_priv *status_priv = +- (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv; ++ (struct wl18xx_fw_status_priv *)wl->fw_status->priv; + u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap); + + /* suspended links are never high priority */ +@@ -1594,7 +1627,7 @@ static bool wl18xx_lnk_low_prio(struct w + { + u8 thold; + struct wl18xx_fw_status_priv *status_priv = +- (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv; ++ (struct wl18xx_fw_status_priv *)wl->fw_status->priv; + u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap); + + if (test_bit(hlid, (unsigned long *)&suspend_bitmap)) +@@ -1632,6 +1665,7 @@ static struct wlcore_ops wl18xx_ops = { + .tx_immediate_compl = wl18xx_tx_immediate_completion, + .tx_delayed_compl = NULL, + .hw_init = wl18xx_hw_init, ++ .convert_fw_status = wl18xx_convert_fw_status, + .set_tx_desc_csum = wl18xx_set_tx_desc_csum, + .get_pg_ver = wl18xx_get_pg_ver, + .set_rx_csum = wl18xx_set_rx_csum, +@@ -1726,6 +1760,7 @@ static int wl18xx_setup(struct wl1271 *w + wl->band_rate_to_idx = wl18xx_band_rate_to_idx; + wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX; + wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0; ++ wl->fw_status_len = sizeof(struct wl18xx_fw_status); + wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv); + wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics); + wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv); +--- a/drivers/net/wireless/ti/wl18xx/tx.c ++++ b/drivers/net/wireless/ti/wl18xx/tx.c +@@ -32,7 +32,7 @@ static + void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif, + struct ieee80211_tx_rate *rate) + { +- u8 fw_rate = wl->fw_status_2->counters.tx_last_rate; ++ u8 fw_rate = wl->fw_status->counters.tx_last_rate; + + if (fw_rate > CONF_HW_RATE_INDEX_MAX) { + wl1271_error("last Tx rate invalid: %d", fw_rate); +@@ -139,7 +139,7 @@ static void wl18xx_tx_complete_packet(st + void wl18xx_tx_immediate_complete(struct wl1271 *wl) + { + struct wl18xx_fw_status_priv *status_priv = +- (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv; ++ (struct wl18xx_fw_status_priv *)wl->fw_status->priv; + struct wl18xx_priv *priv = wl->priv; + u8 i; + +--- a/drivers/net/wireless/ti/wl18xx/wl18xx.h ++++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h +@@ -109,6 +109,59 @@ struct wl18xx_fw_status_priv { + u8 padding[3]; + }; + ++struct wl18xx_fw_packet_counters { ++ /* Cumulative counter of released packets per AC */ ++ u8 tx_released_pkts[NUM_TX_QUEUES]; ++ ++ /* Cumulative counter of freed packets per HLID */ ++ u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS]; ++ ++ /* Cumulative counter of released Voice memory blocks */ ++ u8 tx_voice_released_blks; ++ ++ /* Tx rate of the last transmitted packet */ ++ u8 tx_last_rate; ++ ++ u8 padding[2]; ++} __packed; ++ ++/* FW status registers */ ++struct wl18xx_fw_status { ++ __le32 intr; ++ u8 fw_rx_counter; ++ u8 drv_rx_counter; ++ u8 reserved; ++ u8 tx_results_counter; ++ __le32 rx_pkt_descs[WL18XX_NUM_RX_DESCRIPTORS]; ++ ++ __le32 fw_localtime; ++ ++ /* ++ * A bitmap (where each bit represents a single HLID) ++ * to indicate if the station is in PS mode. ++ */ ++ __le32 link_ps_bitmap; ++ ++ /* ++ * A bitmap (where each bit represents a single HLID) to indicate ++ * if the station is in Fast mode ++ */ ++ __le32 link_fast_bitmap; ++ ++ /* Cumulative counter of total released mem blocks since FW-reset */ ++ __le32 total_released_blks; ++ ++ /* Size (in Memory Blocks) of TX pool */ ++ __le32 tx_total; ++ ++ struct wl18xx_fw_packet_counters counters; ++ ++ __le32 log_start_addr; ++ ++ /* Private status to be used by the lower drivers */ ++ struct wl18xx_fw_status_priv priv; ++} __packed; ++ + #define WL18XX_PHY_VERSION_MAX_LEN 20 + + struct wl18xx_static_data_priv { +--- a/drivers/net/wireless/ti/wlcore/cmd.c ++++ b/drivers/net/wireless/ti/wlcore/cmd.c +@@ -324,9 +324,14 @@ int wl12xx_allocate_link(struct wl1271 * + __set_bit(link, wlvif->links_map); + spin_unlock_irqrestore(&wl->wl_lock, flags); + +- /* take the last "freed packets" value from the current FW status */ +- wl->links[link].prev_freed_pkts = +- wl->fw_status_2->counters.tx_lnk_free_pkts[link]; ++ /* ++ * take the last "freed packets" value from the current FW status. ++ * on recovery, we might not have fw_status yet, and ++ * tx_lnk_free_pkts will be NULL. check for it. ++ */ ++ if (wl->fw_status->counters.tx_lnk_free_pkts) ++ wl->links[link].prev_freed_pkts = ++ wl->fw_status->counters.tx_lnk_free_pkts[link]; + wl->links[link].wlvif = wlvif; + + /* +--- a/drivers/net/wireless/ti/wlcore/hw_ops.h ++++ b/drivers/net/wireless/ti/wlcore/hw_ops.h +@@ -106,6 +106,15 @@ wlcore_hw_init_vif(struct wl1271 *wl, st + return 0; + } + ++static inline void ++wlcore_hw_convert_fw_status(struct wl1271 *wl, void *raw_fw_status, ++ struct wl_fw_status *fw_status) ++{ ++ BUG_ON(!wl->ops->convert_fw_status); ++ ++ wl->ops->convert_fw_status(wl, raw_fw_status, fw_status); ++} ++ + static inline u32 + wlcore_hw_sta_get_ap_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif) + { +--- a/drivers/net/wireless/ti/wlcore/main.c ++++ b/drivers/net/wireless/ti/wlcore/main.c +@@ -357,12 +357,12 @@ static void wl12xx_irq_ps_regulate_link( + + static void wl12xx_irq_update_links_status(struct wl1271 *wl, + struct wl12xx_vif *wlvif, +- struct wl_fw_status_2 *status) ++ struct wl_fw_status *status) + { + u32 cur_fw_ps_map; + u8 hlid; + +- cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap); ++ cur_fw_ps_map = status->link_ps_bitmap; + if (wl->ap_fw_ps_map != cur_fw_ps_map) { + wl1271_debug(DEBUG_PSM, + "link ps prev 0x%x cur 0x%x changed 0x%x", +@@ -377,41 +377,38 @@ static void wl12xx_irq_update_links_stat + wl->links[hlid].allocated_pkts); + } + +-static int wlcore_fw_status(struct wl1271 *wl, +- struct wl_fw_status_1 *status_1, +- struct wl_fw_status_2 *status_2) ++static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status) + { + struct wl12xx_vif *wlvif; + struct timespec ts; + u32 old_tx_blk_count = wl->tx_blocks_available; + int avail, freed_blocks; + int i; +- size_t status_len; + int ret; + struct wl1271_link *lnk; + +- status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + +- sizeof(*status_2) + wl->fw_status_priv_len; +- +- ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1, +- status_len, false); ++ ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, ++ wl->raw_fw_status, ++ wl->fw_status_len, false); + if (ret < 0) + return ret; + ++ wlcore_hw_convert_fw_status(wl, wl->raw_fw_status, wl->fw_status); ++ + wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " + "drv_rx_counter = %d, tx_results_counter = %d)", +- status_1->intr, +- status_1->fw_rx_counter, +- status_1->drv_rx_counter, +- status_1->tx_results_counter); ++ status->intr, ++ status->fw_rx_counter, ++ status->drv_rx_counter, ++ status->tx_results_counter); + + for (i = 0; i < NUM_TX_QUEUES; i++) { + /* prevent wrap-around in freed-packets counter */ + wl->tx_allocated_pkts[i] -= +- (status_2->counters.tx_released_pkts[i] - ++ (status->counters.tx_released_pkts[i] - + wl->tx_pkts_freed[i]) & 0xff; + +- wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i]; ++ wl->tx_pkts_freed[i] = status->counters.tx_released_pkts[i]; + } + + +@@ -420,29 +417,28 @@ static int wlcore_fw_status(struct wl127 + lnk = &wl->links[i]; + + /* prevent wrap-around in freed-packets counter */ +- diff = (status_2->counters.tx_lnk_free_pkts[i] - ++ diff = (status->counters.tx_lnk_free_pkts[i] - + lnk->prev_freed_pkts) & 0xff; + + if (diff == 0) + continue; + + lnk->allocated_pkts -= diff; +- lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i]; ++ lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[i]; + + /* accumulate the prev_freed_pkts counter */ + lnk->total_freed_pkts += diff; + } + + /* prevent wrap-around in total blocks counter */ +- if (likely(wl->tx_blocks_freed <= +- le32_to_cpu(status_2->total_released_blks))) +- freed_blocks = le32_to_cpu(status_2->total_released_blks) - ++ if (likely(wl->tx_blocks_freed <= status->total_released_blks)) ++ freed_blocks = status->total_released_blks - + wl->tx_blocks_freed; + else + freed_blocks = 0x100000000LL - wl->tx_blocks_freed + +- le32_to_cpu(status_2->total_released_blks); ++ status->total_released_blks; + +- wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks); ++ wl->tx_blocks_freed = status->total_released_blks; + + wl->tx_allocated_blocks -= freed_blocks; + +@@ -458,7 +454,7 @@ static int wlcore_fw_status(struct wl127 + cancel_delayed_work(&wl->tx_watchdog_work); + } + +- avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks; ++ avail = status->tx_total - wl->tx_allocated_blocks; + + /* + * The FW might change the total number of TX memblocks before +@@ -477,15 +473,15 @@ static int wlcore_fw_status(struct wl127 + + /* for AP update num of allocated TX blocks per link and ps status */ + wl12xx_for_each_wlvif_ap(wl, wlvif) { +- wl12xx_irq_update_links_status(wl, wlvif, status_2); ++ wl12xx_irq_update_links_status(wl, wlvif, status); + } + + /* update the host-chipset time offset */ + getnstimeofday(&ts); + wl->time_offset = (timespec_to_ns(&ts) >> 10) - +- (s64)le32_to_cpu(status_2->fw_localtime); ++ (s64)(status->fw_localtime); + +- wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap); ++ wl->fw_fast_lnk_map = status->link_fast_bitmap; + + return 0; + } +@@ -549,13 +545,13 @@ static int wlcore_irq_locked(struct wl12 + clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); + smp_mb__after_clear_bit(); + +- ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2); ++ ret = wlcore_fw_status(wl, wl->fw_status); + if (ret < 0) + goto out; + + wlcore_hw_tx_immediate_compl(wl); + +- intr = le32_to_cpu(wl->fw_status_1->intr); ++ intr = wl->fw_status->intr; + intr &= WLCORE_ALL_INTR_MASK; + if (!intr) { + done = true; +@@ -584,7 +580,7 @@ static int wlcore_irq_locked(struct wl12 + if (likely(intr & WL1271_ACX_INTR_DATA)) { + wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); + +- ret = wlcore_rx(wl, wl->fw_status_1); ++ ret = wlcore_rx(wl, wl->fw_status); + if (ret < 0) + goto out; + +@@ -843,11 +839,11 @@ static void wl12xx_read_fwlog_panic(stru + wl12xx_cmd_stop_fwlog(wl); + + /* Read the first memory block address */ +- ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2); ++ ret = wlcore_fw_status(wl, wl->fw_status); + if (ret < 0) + goto out; + +- addr = le32_to_cpu(wl->fw_status_2->log_start_addr); ++ addr = wl->fw_status->log_start_addr; + if (!addr) + goto out; + +@@ -990,23 +986,23 @@ static int wlcore_fw_wakeup(struct wl127 + + static int wl1271_setup(struct wl1271 *wl) + { +- wl->fw_status_1 = kzalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + +- sizeof(*wl->fw_status_2) + +- wl->fw_status_priv_len, GFP_KERNEL); +- if (!wl->fw_status_1) +- return -ENOMEM; ++ wl->raw_fw_status = kzalloc(wl->fw_status_len, GFP_KERNEL); ++ if (!wl->raw_fw_status) ++ goto err; + +- wl->fw_status_2 = (struct wl_fw_status_2 *) +- (((u8 *) wl->fw_status_1) + +- WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc)); ++ wl->fw_status = kzalloc(sizeof(*wl->fw_status), GFP_KERNEL); ++ if (!wl->fw_status) ++ goto err; + + wl->tx_res_if = kzalloc(sizeof(*wl->tx_res_if), GFP_KERNEL); +- if (!wl->tx_res_if) { +- kfree(wl->fw_status_1); +- return -ENOMEM; +- } ++ if (!wl->tx_res_if) ++ goto err; + + return 0; ++err: ++ kfree(wl->fw_status); ++ kfree(wl->raw_fw_status); ++ return -ENOMEM; + } + + static int wl12xx_set_power_on(struct wl1271 *wl) +@@ -1952,9 +1948,10 @@ static void wlcore_op_stop_locked(struct + + wl1271_debugfs_reset(wl); + +- kfree(wl->fw_status_1); +- wl->fw_status_1 = NULL; +- wl->fw_status_2 = NULL; ++ kfree(wl->raw_fw_status); ++ wl->raw_fw_status = NULL; ++ kfree(wl->fw_status); ++ wl->fw_status = NULL; + kfree(wl->tx_res_if); + wl->tx_res_if = NULL; + kfree(wl->target_mem_map); +@@ -6058,7 +6055,8 @@ int wlcore_free_hw(struct wl1271 *wl) + kfree(wl->nvs); + wl->nvs = NULL; + +- kfree(wl->fw_status_1); ++ kfree(wl->raw_fw_status); ++ kfree(wl->fw_status); + kfree(wl->tx_res_if); + destroy_workqueue(wl->freezable_wq); + +--- a/drivers/net/wireless/ti/wlcore/rx.c ++++ b/drivers/net/wireless/ti/wlcore/rx.c +@@ -203,7 +203,7 @@ static int wl1271_rx_handle_data(struct + return is_data; + } + +-int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status) ++int wlcore_rx(struct wl1271 *wl, struct wl_fw_status *status) + { + unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0}; + u32 buf_size; +--- a/drivers/net/wireless/ti/wlcore/rx.h ++++ b/drivers/net/wireless/ti/wlcore/rx.h +@@ -142,7 +142,7 @@ struct wl1271_rx_descriptor { + u8 reserved; + } __packed; + +-int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status); ++int wlcore_rx(struct wl1271 *wl, struct wl_fw_status *status); + u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); + int wl1271_rx_filter_enable(struct wl1271 *wl, + int index, bool enable, +--- a/drivers/net/wireless/ti/wlcore/wlcore.h ++++ b/drivers/net/wireless/ti/wlcore/wlcore.h +@@ -73,6 +73,8 @@ struct wlcore_ops { + void (*tx_immediate_compl)(struct wl1271 *wl); + int (*hw_init)(struct wl1271 *wl); + int (*init_vif)(struct wl1271 *wl, struct wl12xx_vif *wlvif); ++ void (*convert_fw_status)(struct wl1271 *wl, void *raw_fw_status, ++ struct wl_fw_status *fw_status); + u32 (*sta_get_ap_rate_mask)(struct wl1271 *wl, + struct wl12xx_vif *wlvif); + int (*get_pg_ver)(struct wl1271 *wl, s8 *ver); +@@ -348,8 +350,8 @@ struct wl1271 { + u32 buffer_cmd; + u32 buffer_busyword[WL1271_BUSY_WORD_CNT]; + +- struct wl_fw_status_1 *fw_status_1; +- struct wl_fw_status_2 *fw_status_2; ++ void *raw_fw_status; ++ struct wl_fw_status *fw_status; + struct wl1271_tx_hw_res_if *tx_res_if; + + /* Current chipset configuration */ +@@ -450,6 +452,7 @@ struct wl1271 { + struct ieee80211_sta_ht_cap ht_cap[WLCORE_NUM_BANDS]; + + /* size of the private FW status data */ ++ size_t fw_status_len; + size_t fw_status_priv_len; + + /* RX Data filter rule state - enabled/disabled */ +--- a/drivers/net/wireless/ti/wlcore/wlcore_i.h ++++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h +@@ -120,70 +120,58 @@ struct wl1271_chip { + + #define AP_MAX_STATIONS 8 + +-struct wl_fw_packet_counters { +- /* Cumulative counter of released packets per AC */ +- u8 tx_released_pkts[NUM_TX_QUEUES]; +- +- /* Cumulative counter of freed packets per HLID */ +- u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS]; +- +- /* Cumulative counter of released Voice memory blocks */ +- u8 tx_voice_released_blks; +- +- /* Tx rate of the last transmitted packet */ +- u8 tx_last_rate; +- +- u8 padding[2]; +-} __packed; +- +-/* FW status registers */ +-struct wl_fw_status_1 { +- __le32 intr; ++struct wl_fw_status { ++ u32 intr; + u8 fw_rx_counter; + u8 drv_rx_counter; +- u8 reserved; + u8 tx_results_counter; +- __le32 rx_pkt_descs[0]; +-} __packed; +- +-/* +- * Each HW arch has a different number of Rx descriptors. +- * The length of the status depends on it, since it holds an array +- * of descriptors. +- */ +-#define WLCORE_FW_STATUS_1_LEN(num_rx_desc) \ +- (sizeof(struct wl_fw_status_1) + \ +- (sizeof(((struct wl_fw_status_1 *)0)->rx_pkt_descs[0])) * \ +- num_rx_desc) ++ __le32 *rx_pkt_descs; + +-struct wl_fw_status_2 { +- __le32 fw_localtime; ++ u32 fw_localtime; + + /* + * A bitmap (where each bit represents a single HLID) + * to indicate if the station is in PS mode. + */ +- __le32 link_ps_bitmap; ++ u32 link_ps_bitmap; + + /* + * A bitmap (where each bit represents a single HLID) to indicate + * if the station is in Fast mode + */ +- __le32 link_fast_bitmap; ++ u32 link_fast_bitmap; + + /* Cumulative counter of total released mem blocks since FW-reset */ +- __le32 total_released_blks; ++ u32 total_released_blks; + + /* Size (in Memory Blocks) of TX pool */ +- __le32 tx_total; ++ u32 tx_total; + +- struct wl_fw_packet_counters counters; ++ struct { ++ /* ++ * Cumulative counter of released packets per AC ++ * (length of the array is NUM_TX_QUEUES) ++ */ ++ u8 *tx_released_pkts; ++ ++ /* ++ * Cumulative counter of freed packets per HLID ++ * (length of the array is WL12XX_MAX_LINKS) ++ */ ++ u8 *tx_lnk_free_pkts; ++ ++ /* Cumulative counter of released Voice memory blocks */ ++ u8 tx_voice_released_blks; ++ ++ /* Tx rate of the last transmitted packet */ ++ u8 tx_last_rate; ++ } counters; + +- __le32 log_start_addr; ++ u32 log_start_addr; + + /* Private status to be used by the lower drivers */ +- u8 priv[0]; +-} __packed; ++ void *priv; ++}; + + #define WL1271_MAX_CHANNELS 64 + struct wl1271_scan { |