190921014SLuciano Coelho /* 290921014SLuciano Coelho * This file is part of wl1251 390921014SLuciano Coelho * 490921014SLuciano Coelho * Copyright (C) 2009 Nokia Corporation 590921014SLuciano Coelho * 690921014SLuciano Coelho * This program is free software; you can redistribute it and/or 790921014SLuciano Coelho * modify it under the terms of the GNU General Public License 890921014SLuciano Coelho * version 2 as published by the Free Software Foundation. 990921014SLuciano Coelho * 1090921014SLuciano Coelho * This program is distributed in the hope that it will be useful, but 1190921014SLuciano Coelho * WITHOUT ANY WARRANTY; without even the implied warranty of 1290921014SLuciano Coelho * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1390921014SLuciano Coelho * General Public License for more details. 1490921014SLuciano Coelho * 1590921014SLuciano Coelho * You should have received a copy of the GNU General Public License 1690921014SLuciano Coelho * along with this program; if not, write to the Free Software 1790921014SLuciano Coelho * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 1890921014SLuciano Coelho * 02110-1301 USA 1990921014SLuciano Coelho * 2090921014SLuciano Coelho */ 2190921014SLuciano Coelho 2290921014SLuciano Coelho #include <linux/kernel.h> 2390921014SLuciano Coelho #include <linux/module.h> 2490921014SLuciano Coelho #include <linux/slab.h> 2590921014SLuciano Coelho 2690921014SLuciano Coelho #include "init.h" 2790921014SLuciano Coelho #include "wl12xx_80211.h" 2890921014SLuciano Coelho #include "acx.h" 2990921014SLuciano Coelho #include "cmd.h" 3090921014SLuciano Coelho #include "reg.h" 3190921014SLuciano Coelho 3290921014SLuciano Coelho int wl1251_hw_init_hwenc_config(struct wl1251 *wl) 3390921014SLuciano Coelho { 3490921014SLuciano Coelho int ret; 3590921014SLuciano Coelho 364d09b537SDavid Gnedt ret = wl1251_acx_feature_cfg(wl, 0); 3790921014SLuciano Coelho if (ret < 0) { 3890921014SLuciano Coelho wl1251_warning("couldn't set feature config"); 3990921014SLuciano Coelho return ret; 4090921014SLuciano Coelho } 4190921014SLuciano Coelho 4290921014SLuciano Coelho ret = wl1251_acx_default_key(wl, wl->default_key); 4390921014SLuciano Coelho if (ret < 0) { 4490921014SLuciano Coelho wl1251_warning("couldn't set default key"); 4590921014SLuciano Coelho return ret; 4690921014SLuciano Coelho } 4790921014SLuciano Coelho 4890921014SLuciano Coelho return 0; 4990921014SLuciano Coelho } 5090921014SLuciano Coelho 5190921014SLuciano Coelho int wl1251_hw_init_templates_config(struct wl1251 *wl) 5290921014SLuciano Coelho { 5390921014SLuciano Coelho int ret; 5490921014SLuciano Coelho u8 partial_vbm[PARTIAL_VBM_MAX]; 5590921014SLuciano Coelho 5690921014SLuciano Coelho /* send empty templates for fw memory reservation */ 5790921014SLuciano Coelho ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, NULL, 5890921014SLuciano Coelho sizeof(struct wl12xx_probe_req_template)); 5990921014SLuciano Coelho if (ret < 0) 6090921014SLuciano Coelho return ret; 6190921014SLuciano Coelho 6290921014SLuciano Coelho ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, NULL, 6390921014SLuciano Coelho sizeof(struct wl12xx_null_data_template)); 6490921014SLuciano Coelho if (ret < 0) 6590921014SLuciano Coelho return ret; 6690921014SLuciano Coelho 6790921014SLuciano Coelho ret = wl1251_cmd_template_set(wl, CMD_PS_POLL, NULL, 6890921014SLuciano Coelho sizeof(struct wl12xx_ps_poll_template)); 6990921014SLuciano Coelho if (ret < 0) 7090921014SLuciano Coelho return ret; 7190921014SLuciano Coelho 7290921014SLuciano Coelho ret = wl1251_cmd_template_set(wl, CMD_QOS_NULL_DATA, NULL, 7390921014SLuciano Coelho sizeof 7490921014SLuciano Coelho (struct wl12xx_qos_null_data_template)); 7590921014SLuciano Coelho if (ret < 0) 7690921014SLuciano Coelho return ret; 7790921014SLuciano Coelho 7890921014SLuciano Coelho ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, NULL, 7990921014SLuciano Coelho sizeof 8090921014SLuciano Coelho (struct wl12xx_probe_resp_template)); 8190921014SLuciano Coelho if (ret < 0) 8290921014SLuciano Coelho return ret; 8390921014SLuciano Coelho 8490921014SLuciano Coelho ret = wl1251_cmd_template_set(wl, CMD_BEACON, NULL, 8590921014SLuciano Coelho sizeof 8690921014SLuciano Coelho (struct wl12xx_beacon_template)); 8790921014SLuciano Coelho if (ret < 0) 8890921014SLuciano Coelho return ret; 8990921014SLuciano Coelho 9090921014SLuciano Coelho /* tim templates, first reserve space then allocate an empty one */ 9190921014SLuciano Coelho memset(partial_vbm, 0, PARTIAL_VBM_MAX); 9290921014SLuciano Coelho ret = wl1251_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, PARTIAL_VBM_MAX, 0); 9390921014SLuciano Coelho if (ret < 0) 9490921014SLuciano Coelho return ret; 9590921014SLuciano Coelho 9690921014SLuciano Coelho ret = wl1251_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, 1, 0); 9790921014SLuciano Coelho if (ret < 0) 9890921014SLuciano Coelho return ret; 9990921014SLuciano Coelho 10090921014SLuciano Coelho return 0; 10190921014SLuciano Coelho } 10290921014SLuciano Coelho 10390921014SLuciano Coelho int wl1251_hw_init_rx_config(struct wl1251 *wl, u32 config, u32 filter) 10490921014SLuciano Coelho { 10590921014SLuciano Coelho int ret; 10690921014SLuciano Coelho 10790921014SLuciano Coelho ret = wl1251_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF); 10890921014SLuciano Coelho if (ret < 0) 10990921014SLuciano Coelho return ret; 11090921014SLuciano Coelho 11190921014SLuciano Coelho ret = wl1251_acx_rx_config(wl, config, filter); 11290921014SLuciano Coelho if (ret < 0) 11390921014SLuciano Coelho return ret; 11490921014SLuciano Coelho 11590921014SLuciano Coelho return 0; 11690921014SLuciano Coelho } 11790921014SLuciano Coelho 11890921014SLuciano Coelho int wl1251_hw_init_phy_config(struct wl1251 *wl) 11990921014SLuciano Coelho { 12090921014SLuciano Coelho int ret; 12190921014SLuciano Coelho 12290921014SLuciano Coelho ret = wl1251_acx_pd_threshold(wl); 12390921014SLuciano Coelho if (ret < 0) 12490921014SLuciano Coelho return ret; 12590921014SLuciano Coelho 12690921014SLuciano Coelho ret = wl1251_acx_slot(wl, DEFAULT_SLOT_TIME); 12790921014SLuciano Coelho if (ret < 0) 12890921014SLuciano Coelho return ret; 12990921014SLuciano Coelho 13090921014SLuciano Coelho ret = wl1251_acx_group_address_tbl(wl); 13190921014SLuciano Coelho if (ret < 0) 13290921014SLuciano Coelho return ret; 13390921014SLuciano Coelho 13490921014SLuciano Coelho ret = wl1251_acx_service_period_timeout(wl); 13590921014SLuciano Coelho if (ret < 0) 13690921014SLuciano Coelho return ret; 13790921014SLuciano Coelho 13890921014SLuciano Coelho ret = wl1251_acx_rts_threshold(wl, RTS_THRESHOLD_DEF); 13990921014SLuciano Coelho if (ret < 0) 14090921014SLuciano Coelho return ret; 14190921014SLuciano Coelho 14290921014SLuciano Coelho return 0; 14390921014SLuciano Coelho } 14490921014SLuciano Coelho 14590921014SLuciano Coelho int wl1251_hw_init_beacon_filter(struct wl1251 *wl) 14690921014SLuciano Coelho { 14790921014SLuciano Coelho int ret; 14890921014SLuciano Coelho 14990921014SLuciano Coelho /* disable beacon filtering at this stage */ 15090921014SLuciano Coelho ret = wl1251_acx_beacon_filter_opt(wl, false); 15190921014SLuciano Coelho if (ret < 0) 15290921014SLuciano Coelho return ret; 15390921014SLuciano Coelho 15490921014SLuciano Coelho ret = wl1251_acx_beacon_filter_table(wl); 15590921014SLuciano Coelho if (ret < 0) 15690921014SLuciano Coelho return ret; 15790921014SLuciano Coelho 15890921014SLuciano Coelho return 0; 15990921014SLuciano Coelho } 16090921014SLuciano Coelho 16190921014SLuciano Coelho int wl1251_hw_init_pta(struct wl1251 *wl) 16290921014SLuciano Coelho { 16390921014SLuciano Coelho int ret; 16490921014SLuciano Coelho 16590921014SLuciano Coelho ret = wl1251_acx_sg_enable(wl); 16690921014SLuciano Coelho if (ret < 0) 16790921014SLuciano Coelho return ret; 16890921014SLuciano Coelho 16990921014SLuciano Coelho ret = wl1251_acx_sg_cfg(wl); 17090921014SLuciano Coelho if (ret < 0) 17190921014SLuciano Coelho return ret; 17290921014SLuciano Coelho 17390921014SLuciano Coelho return 0; 17490921014SLuciano Coelho } 17590921014SLuciano Coelho 17690921014SLuciano Coelho int wl1251_hw_init_energy_detection(struct wl1251 *wl) 17790921014SLuciano Coelho { 17890921014SLuciano Coelho int ret; 17990921014SLuciano Coelho 18090921014SLuciano Coelho ret = wl1251_acx_cca_threshold(wl); 18190921014SLuciano Coelho if (ret < 0) 18290921014SLuciano Coelho return ret; 18390921014SLuciano Coelho 18490921014SLuciano Coelho return 0; 18590921014SLuciano Coelho } 18690921014SLuciano Coelho 18790921014SLuciano Coelho int wl1251_hw_init_beacon_broadcast(struct wl1251 *wl) 18890921014SLuciano Coelho { 18990921014SLuciano Coelho int ret; 19090921014SLuciano Coelho 19190921014SLuciano Coelho ret = wl1251_acx_bcn_dtim_options(wl); 19290921014SLuciano Coelho if (ret < 0) 19390921014SLuciano Coelho return ret; 19490921014SLuciano Coelho 19590921014SLuciano Coelho return 0; 19690921014SLuciano Coelho } 19790921014SLuciano Coelho 19890921014SLuciano Coelho int wl1251_hw_init_power_auth(struct wl1251 *wl) 19990921014SLuciano Coelho { 20090921014SLuciano Coelho return wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM); 20190921014SLuciano Coelho } 20290921014SLuciano Coelho 20390921014SLuciano Coelho int wl1251_hw_init_mem_config(struct wl1251 *wl) 20490921014SLuciano Coelho { 20590921014SLuciano Coelho int ret; 20690921014SLuciano Coelho 20790921014SLuciano Coelho ret = wl1251_acx_mem_cfg(wl); 20890921014SLuciano Coelho if (ret < 0) 20990921014SLuciano Coelho return ret; 21090921014SLuciano Coelho 21190921014SLuciano Coelho wl->target_mem_map = kzalloc(sizeof(struct wl1251_acx_mem_map), 21290921014SLuciano Coelho GFP_KERNEL); 21390921014SLuciano Coelho if (!wl->target_mem_map) { 21490921014SLuciano Coelho wl1251_error("couldn't allocate target memory map"); 21590921014SLuciano Coelho return -ENOMEM; 21690921014SLuciano Coelho } 21790921014SLuciano Coelho 21890921014SLuciano Coelho /* we now ask for the firmware built memory map */ 21990921014SLuciano Coelho ret = wl1251_acx_mem_map(wl, wl->target_mem_map, 22090921014SLuciano Coelho sizeof(struct wl1251_acx_mem_map)); 22190921014SLuciano Coelho if (ret < 0) { 22290921014SLuciano Coelho wl1251_error("couldn't retrieve firmware memory map"); 22390921014SLuciano Coelho kfree(wl->target_mem_map); 22490921014SLuciano Coelho wl->target_mem_map = NULL; 22590921014SLuciano Coelho return ret; 22690921014SLuciano Coelho } 22790921014SLuciano Coelho 22890921014SLuciano Coelho return 0; 22990921014SLuciano Coelho } 23090921014SLuciano Coelho 23190921014SLuciano Coelho static int wl1251_hw_init_txq_fill(u8 qid, 23290921014SLuciano Coelho struct acx_tx_queue_qos_config *config, 23390921014SLuciano Coelho u32 num_blocks) 23490921014SLuciano Coelho { 23590921014SLuciano Coelho config->qid = qid; 23690921014SLuciano Coelho 23790921014SLuciano Coelho switch (qid) { 23890921014SLuciano Coelho case QOS_AC_BE: 23990921014SLuciano Coelho config->high_threshold = 24090921014SLuciano Coelho (QOS_TX_HIGH_BE_DEF * num_blocks) / 100; 24190921014SLuciano Coelho config->low_threshold = 24290921014SLuciano Coelho (QOS_TX_LOW_BE_DEF * num_blocks) / 100; 24390921014SLuciano Coelho break; 24490921014SLuciano Coelho case QOS_AC_BK: 24590921014SLuciano Coelho config->high_threshold = 24690921014SLuciano Coelho (QOS_TX_HIGH_BK_DEF * num_blocks) / 100; 24790921014SLuciano Coelho config->low_threshold = 24890921014SLuciano Coelho (QOS_TX_LOW_BK_DEF * num_blocks) / 100; 24990921014SLuciano Coelho break; 25090921014SLuciano Coelho case QOS_AC_VI: 25190921014SLuciano Coelho config->high_threshold = 25290921014SLuciano Coelho (QOS_TX_HIGH_VI_DEF * num_blocks) / 100; 25390921014SLuciano Coelho config->low_threshold = 25490921014SLuciano Coelho (QOS_TX_LOW_VI_DEF * num_blocks) / 100; 25590921014SLuciano Coelho break; 25690921014SLuciano Coelho case QOS_AC_VO: 25790921014SLuciano Coelho config->high_threshold = 25890921014SLuciano Coelho (QOS_TX_HIGH_VO_DEF * num_blocks) / 100; 25990921014SLuciano Coelho config->low_threshold = 26090921014SLuciano Coelho (QOS_TX_LOW_VO_DEF * num_blocks) / 100; 26190921014SLuciano Coelho break; 26290921014SLuciano Coelho default: 26390921014SLuciano Coelho wl1251_error("Invalid TX queue id: %d", qid); 26490921014SLuciano Coelho return -EINVAL; 26590921014SLuciano Coelho } 26690921014SLuciano Coelho 26790921014SLuciano Coelho return 0; 26890921014SLuciano Coelho } 26990921014SLuciano Coelho 27090921014SLuciano Coelho static int wl1251_hw_init_tx_queue_config(struct wl1251 *wl) 27190921014SLuciano Coelho { 27290921014SLuciano Coelho struct acx_tx_queue_qos_config *config; 27390921014SLuciano Coelho struct wl1251_acx_mem_map *wl_mem_map = wl->target_mem_map; 27490921014SLuciano Coelho int ret, i; 27590921014SLuciano Coelho 27690921014SLuciano Coelho wl1251_debug(DEBUG_ACX, "acx tx queue config"); 27790921014SLuciano Coelho 27890921014SLuciano Coelho config = kzalloc(sizeof(*config), GFP_KERNEL); 27990921014SLuciano Coelho if (!config) { 28090921014SLuciano Coelho ret = -ENOMEM; 28190921014SLuciano Coelho goto out; 28290921014SLuciano Coelho } 28390921014SLuciano Coelho 28490921014SLuciano Coelho for (i = 0; i < MAX_NUM_OF_AC; i++) { 28590921014SLuciano Coelho ret = wl1251_hw_init_txq_fill(i, config, 28690921014SLuciano Coelho wl_mem_map->num_tx_mem_blocks); 28790921014SLuciano Coelho if (ret < 0) 28890921014SLuciano Coelho goto out; 28990921014SLuciano Coelho 29090921014SLuciano Coelho ret = wl1251_cmd_configure(wl, ACX_TX_QUEUE_CFG, 29190921014SLuciano Coelho config, sizeof(*config)); 29290921014SLuciano Coelho if (ret < 0) 29390921014SLuciano Coelho goto out; 29490921014SLuciano Coelho } 29590921014SLuciano Coelho 29690921014SLuciano Coelho wl1251_acx_ac_cfg(wl, AC_BE, CWMIN_BE, CWMAX_BE, AIFS_DIFS, TXOP_BE); 29790921014SLuciano Coelho wl1251_acx_ac_cfg(wl, AC_BK, CWMIN_BK, CWMAX_BK, AIFS_DIFS, TXOP_BK); 29890921014SLuciano Coelho wl1251_acx_ac_cfg(wl, AC_VI, CWMIN_VI, CWMAX_VI, AIFS_DIFS, TXOP_VI); 29990921014SLuciano Coelho wl1251_acx_ac_cfg(wl, AC_VO, CWMIN_VO, CWMAX_VO, AIFS_DIFS, TXOP_VO); 30090921014SLuciano Coelho 30190921014SLuciano Coelho out: 30290921014SLuciano Coelho kfree(config); 30390921014SLuciano Coelho return ret; 30490921014SLuciano Coelho } 30590921014SLuciano Coelho 30690921014SLuciano Coelho static int wl1251_hw_init_data_path_config(struct wl1251 *wl) 30790921014SLuciano Coelho { 30890921014SLuciano Coelho int ret; 30990921014SLuciano Coelho 31090921014SLuciano Coelho /* asking for the data path parameters */ 31190921014SLuciano Coelho wl->data_path = kzalloc(sizeof(struct acx_data_path_params_resp), 31290921014SLuciano Coelho GFP_KERNEL); 31390921014SLuciano Coelho if (!wl->data_path) { 31490921014SLuciano Coelho wl1251_error("Couldnt allocate data path parameters"); 31590921014SLuciano Coelho return -ENOMEM; 31690921014SLuciano Coelho } 31790921014SLuciano Coelho 31890921014SLuciano Coelho ret = wl1251_acx_data_path_params(wl, wl->data_path); 31990921014SLuciano Coelho if (ret < 0) { 32090921014SLuciano Coelho kfree(wl->data_path); 32190921014SLuciano Coelho wl->data_path = NULL; 32290921014SLuciano Coelho return ret; 32390921014SLuciano Coelho } 32490921014SLuciano Coelho 32590921014SLuciano Coelho return 0; 32690921014SLuciano Coelho } 32790921014SLuciano Coelho 32890921014SLuciano Coelho 32990921014SLuciano Coelho int wl1251_hw_init(struct wl1251 *wl) 33090921014SLuciano Coelho { 33190921014SLuciano Coelho struct wl1251_acx_mem_map *wl_mem_map; 33290921014SLuciano Coelho int ret; 33390921014SLuciano Coelho 33490921014SLuciano Coelho ret = wl1251_hw_init_hwenc_config(wl); 33590921014SLuciano Coelho if (ret < 0) 33690921014SLuciano Coelho return ret; 33790921014SLuciano Coelho 33890921014SLuciano Coelho /* Template settings */ 33990921014SLuciano Coelho ret = wl1251_hw_init_templates_config(wl); 34090921014SLuciano Coelho if (ret < 0) 34190921014SLuciano Coelho return ret; 34290921014SLuciano Coelho 34390921014SLuciano Coelho /* Default memory configuration */ 34490921014SLuciano Coelho ret = wl1251_hw_init_mem_config(wl); 34590921014SLuciano Coelho if (ret < 0) 34690921014SLuciano Coelho return ret; 34790921014SLuciano Coelho 34890921014SLuciano Coelho /* Default data path configuration */ 34990921014SLuciano Coelho ret = wl1251_hw_init_data_path_config(wl); 35090921014SLuciano Coelho if (ret < 0) 35190921014SLuciano Coelho goto out_free_memmap; 35290921014SLuciano Coelho 35390921014SLuciano Coelho /* RX config */ 35490921014SLuciano Coelho ret = wl1251_hw_init_rx_config(wl, 35590921014SLuciano Coelho RX_CFG_PROMISCUOUS | RX_CFG_TSF, 35690921014SLuciano Coelho RX_FILTER_OPTION_DEF); 35790921014SLuciano Coelho /* RX_CONFIG_OPTION_ANY_DST_ANY_BSS, 35890921014SLuciano Coelho RX_FILTER_OPTION_FILTER_ALL); */ 35990921014SLuciano Coelho if (ret < 0) 36090921014SLuciano Coelho goto out_free_data_path; 36190921014SLuciano Coelho 36290921014SLuciano Coelho /* TX queues config */ 36390921014SLuciano Coelho ret = wl1251_hw_init_tx_queue_config(wl); 36490921014SLuciano Coelho if (ret < 0) 36590921014SLuciano Coelho goto out_free_data_path; 36690921014SLuciano Coelho 36790921014SLuciano Coelho /* PHY layer config */ 36890921014SLuciano Coelho ret = wl1251_hw_init_phy_config(wl); 36990921014SLuciano Coelho if (ret < 0) 37090921014SLuciano Coelho goto out_free_data_path; 37190921014SLuciano Coelho 37290921014SLuciano Coelho /* Initialize connection monitoring thresholds */ 37390921014SLuciano Coelho ret = wl1251_acx_conn_monit_params(wl); 37490921014SLuciano Coelho if (ret < 0) 37590921014SLuciano Coelho goto out_free_data_path; 37690921014SLuciano Coelho 37790921014SLuciano Coelho /* Beacon filtering */ 37890921014SLuciano Coelho ret = wl1251_hw_init_beacon_filter(wl); 37990921014SLuciano Coelho if (ret < 0) 38090921014SLuciano Coelho goto out_free_data_path; 38190921014SLuciano Coelho 38290921014SLuciano Coelho /* Bluetooth WLAN coexistence */ 38390921014SLuciano Coelho ret = wl1251_hw_init_pta(wl); 38490921014SLuciano Coelho if (ret < 0) 38590921014SLuciano Coelho goto out_free_data_path; 38690921014SLuciano Coelho 38790921014SLuciano Coelho /* Energy detection */ 38890921014SLuciano Coelho ret = wl1251_hw_init_energy_detection(wl); 38990921014SLuciano Coelho if (ret < 0) 39090921014SLuciano Coelho goto out_free_data_path; 39190921014SLuciano Coelho 39290921014SLuciano Coelho /* Beacons and boradcast settings */ 39390921014SLuciano Coelho ret = wl1251_hw_init_beacon_broadcast(wl); 39490921014SLuciano Coelho if (ret < 0) 39590921014SLuciano Coelho goto out_free_data_path; 39690921014SLuciano Coelho 3979281691fSDavid Gnedt /* Enable rx data path */ 3989281691fSDavid Gnedt ret = wl1251_cmd_data_path_rx(wl, wl->channel, 1); 3999281691fSDavid Gnedt if (ret < 0) 4009281691fSDavid Gnedt goto out_free_data_path; 4019281691fSDavid Gnedt 4029281691fSDavid Gnedt /* Enable tx data path */ 4039281691fSDavid Gnedt ret = wl1251_cmd_data_path_tx(wl, wl->channel, 1); 40490921014SLuciano Coelho if (ret < 0) 40590921014SLuciano Coelho goto out_free_data_path; 40690921014SLuciano Coelho 40790921014SLuciano Coelho /* Default power state */ 40890921014SLuciano Coelho ret = wl1251_hw_init_power_auth(wl); 40990921014SLuciano Coelho if (ret < 0) 41090921014SLuciano Coelho goto out_free_data_path; 41190921014SLuciano Coelho 41290921014SLuciano Coelho wl_mem_map = wl->target_mem_map; 41390921014SLuciano Coelho wl1251_info("%d tx blocks at 0x%x, %d rx blocks at 0x%x", 41490921014SLuciano Coelho wl_mem_map->num_tx_mem_blocks, 41590921014SLuciano Coelho wl->data_path->tx_control_addr, 41690921014SLuciano Coelho wl_mem_map->num_rx_mem_blocks, 41790921014SLuciano Coelho wl->data_path->rx_control_addr); 41890921014SLuciano Coelho 41990921014SLuciano Coelho return 0; 42090921014SLuciano Coelho 42190921014SLuciano Coelho out_free_data_path: 42290921014SLuciano Coelho kfree(wl->data_path); 42390921014SLuciano Coelho 42490921014SLuciano Coelho out_free_memmap: 42590921014SLuciano Coelho kfree(wl->target_mem_map); 42690921014SLuciano Coelho 42790921014SLuciano Coelho return ret; 42890921014SLuciano Coelho } 429