162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file is part of wl1251 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009 Nokia Corporation 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "init.h" 1362306a36Sopenharmony_ci#include "wl12xx_80211.h" 1462306a36Sopenharmony_ci#include "acx.h" 1562306a36Sopenharmony_ci#include "cmd.h" 1662306a36Sopenharmony_ci#include "reg.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciint wl1251_hw_init_hwenc_config(struct wl1251 *wl) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci int ret; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci ret = wl1251_acx_feature_cfg(wl, 0); 2362306a36Sopenharmony_ci if (ret < 0) { 2462306a36Sopenharmony_ci wl1251_warning("couldn't set feature config"); 2562306a36Sopenharmony_ci return ret; 2662306a36Sopenharmony_ci } 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci ret = wl1251_acx_default_key(wl, wl->default_key); 2962306a36Sopenharmony_ci if (ret < 0) { 3062306a36Sopenharmony_ci wl1251_warning("couldn't set default key"); 3162306a36Sopenharmony_ci return ret; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci return 0; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ciint wl1251_hw_init_templates_config(struct wl1251 *wl) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci int ret; 4062306a36Sopenharmony_ci u8 partial_vbm[PARTIAL_VBM_MAX]; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* send empty templates for fw memory reservation */ 4362306a36Sopenharmony_ci ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, NULL, 4462306a36Sopenharmony_ci sizeof(struct wl12xx_probe_req_template)); 4562306a36Sopenharmony_ci if (ret < 0) 4662306a36Sopenharmony_ci return ret; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, NULL, 4962306a36Sopenharmony_ci sizeof(struct wl12xx_null_data_template)); 5062306a36Sopenharmony_ci if (ret < 0) 5162306a36Sopenharmony_ci return ret; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ret = wl1251_cmd_template_set(wl, CMD_PS_POLL, NULL, 5462306a36Sopenharmony_ci sizeof(struct wl12xx_ps_poll_template)); 5562306a36Sopenharmony_ci if (ret < 0) 5662306a36Sopenharmony_ci return ret; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ret = wl1251_cmd_template_set(wl, CMD_QOS_NULL_DATA, NULL, 5962306a36Sopenharmony_ci sizeof 6062306a36Sopenharmony_ci (struct wl12xx_qos_null_data_template)); 6162306a36Sopenharmony_ci if (ret < 0) 6262306a36Sopenharmony_ci return ret; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, NULL, 6562306a36Sopenharmony_ci sizeof 6662306a36Sopenharmony_ci (struct wl12xx_probe_resp_template)); 6762306a36Sopenharmony_ci if (ret < 0) 6862306a36Sopenharmony_ci return ret; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci ret = wl1251_cmd_template_set(wl, CMD_BEACON, NULL, 7162306a36Sopenharmony_ci sizeof 7262306a36Sopenharmony_ci (struct wl12xx_beacon_template)); 7362306a36Sopenharmony_ci if (ret < 0) 7462306a36Sopenharmony_ci return ret; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* tim templates, first reserve space then allocate an empty one */ 7762306a36Sopenharmony_ci memset(partial_vbm, 0, PARTIAL_VBM_MAX); 7862306a36Sopenharmony_ci ret = wl1251_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, PARTIAL_VBM_MAX, 0); 7962306a36Sopenharmony_ci if (ret < 0) 8062306a36Sopenharmony_ci return ret; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci ret = wl1251_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, 1, 0); 8362306a36Sopenharmony_ci if (ret < 0) 8462306a36Sopenharmony_ci return ret; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciint wl1251_hw_init_rx_config(struct wl1251 *wl, u32 config, u32 filter) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci int ret; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci ret = wl1251_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF); 9462306a36Sopenharmony_ci if (ret < 0) 9562306a36Sopenharmony_ci return ret; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci ret = wl1251_acx_rx_config(wl, config, filter); 9862306a36Sopenharmony_ci if (ret < 0) 9962306a36Sopenharmony_ci return ret; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ciint wl1251_hw_init_phy_config(struct wl1251 *wl) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci int ret; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci ret = wl1251_acx_pd_threshold(wl); 10962306a36Sopenharmony_ci if (ret < 0) 11062306a36Sopenharmony_ci return ret; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ret = wl1251_acx_slot(wl, DEFAULT_SLOT_TIME); 11362306a36Sopenharmony_ci if (ret < 0) 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci ret = wl1251_acx_group_address_tbl(wl, true, NULL, 0); 11762306a36Sopenharmony_ci if (ret < 0) 11862306a36Sopenharmony_ci return ret; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci ret = wl1251_acx_service_period_timeout(wl); 12162306a36Sopenharmony_ci if (ret < 0) 12262306a36Sopenharmony_ci return ret; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci ret = wl1251_acx_rts_threshold(wl, RTS_THRESHOLD_DEF); 12562306a36Sopenharmony_ci if (ret < 0) 12662306a36Sopenharmony_ci return ret; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ciint wl1251_hw_init_beacon_filter(struct wl1251 *wl) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci int ret; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* disable beacon filtering at this stage */ 13662306a36Sopenharmony_ci ret = wl1251_acx_beacon_filter_opt(wl, false); 13762306a36Sopenharmony_ci if (ret < 0) 13862306a36Sopenharmony_ci return ret; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci ret = wl1251_acx_beacon_filter_table(wl); 14162306a36Sopenharmony_ci if (ret < 0) 14262306a36Sopenharmony_ci return ret; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciint wl1251_hw_init_pta(struct wl1251 *wl) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci int ret; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ret = wl1251_acx_sg_enable(wl); 15262306a36Sopenharmony_ci if (ret < 0) 15362306a36Sopenharmony_ci return ret; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ret = wl1251_acx_sg_cfg(wl); 15662306a36Sopenharmony_ci if (ret < 0) 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ciint wl1251_hw_init_energy_detection(struct wl1251 *wl) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci int ret; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci ret = wl1251_acx_cca_threshold(wl); 16762306a36Sopenharmony_ci if (ret < 0) 16862306a36Sopenharmony_ci return ret; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciint wl1251_hw_init_beacon_broadcast(struct wl1251 *wl) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci int ret; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ret = wl1251_acx_bcn_dtim_options(wl); 17862306a36Sopenharmony_ci if (ret < 0) 17962306a36Sopenharmony_ci return ret; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ciint wl1251_hw_init_power_auth(struct wl1251 *wl) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci return wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciint wl1251_hw_init_mem_config(struct wl1251 *wl) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci int ret; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ret = wl1251_acx_mem_cfg(wl); 19462306a36Sopenharmony_ci if (ret < 0) 19562306a36Sopenharmony_ci return ret; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci wl->target_mem_map = kzalloc(sizeof(struct wl1251_acx_mem_map), 19862306a36Sopenharmony_ci GFP_KERNEL); 19962306a36Sopenharmony_ci if (!wl->target_mem_map) { 20062306a36Sopenharmony_ci wl1251_error("couldn't allocate target memory map"); 20162306a36Sopenharmony_ci return -ENOMEM; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* we now ask for the firmware built memory map */ 20562306a36Sopenharmony_ci ret = wl1251_acx_mem_map(wl, wl->target_mem_map, 20662306a36Sopenharmony_ci sizeof(struct wl1251_acx_mem_map)); 20762306a36Sopenharmony_ci if (ret < 0) { 20862306a36Sopenharmony_ci wl1251_error("couldn't retrieve firmware memory map"); 20962306a36Sopenharmony_ci kfree(wl->target_mem_map); 21062306a36Sopenharmony_ci wl->target_mem_map = NULL; 21162306a36Sopenharmony_ci return ret; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int wl1251_hw_init_txq_fill(u8 qid, 21862306a36Sopenharmony_ci struct acx_tx_queue_qos_config *config, 21962306a36Sopenharmony_ci u32 num_blocks) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci config->qid = qid; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci switch (qid) { 22462306a36Sopenharmony_ci case QOS_AC_BE: 22562306a36Sopenharmony_ci config->high_threshold = 22662306a36Sopenharmony_ci (QOS_TX_HIGH_BE_DEF * num_blocks) / 100; 22762306a36Sopenharmony_ci config->low_threshold = 22862306a36Sopenharmony_ci (QOS_TX_LOW_BE_DEF * num_blocks) / 100; 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci case QOS_AC_BK: 23162306a36Sopenharmony_ci config->high_threshold = 23262306a36Sopenharmony_ci (QOS_TX_HIGH_BK_DEF * num_blocks) / 100; 23362306a36Sopenharmony_ci config->low_threshold = 23462306a36Sopenharmony_ci (QOS_TX_LOW_BK_DEF * num_blocks) / 100; 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci case QOS_AC_VI: 23762306a36Sopenharmony_ci config->high_threshold = 23862306a36Sopenharmony_ci (QOS_TX_HIGH_VI_DEF * num_blocks) / 100; 23962306a36Sopenharmony_ci config->low_threshold = 24062306a36Sopenharmony_ci (QOS_TX_LOW_VI_DEF * num_blocks) / 100; 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci case QOS_AC_VO: 24362306a36Sopenharmony_ci config->high_threshold = 24462306a36Sopenharmony_ci (QOS_TX_HIGH_VO_DEF * num_blocks) / 100; 24562306a36Sopenharmony_ci config->low_threshold = 24662306a36Sopenharmony_ci (QOS_TX_LOW_VO_DEF * num_blocks) / 100; 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci default: 24962306a36Sopenharmony_ci wl1251_error("Invalid TX queue id: %d", qid); 25062306a36Sopenharmony_ci return -EINVAL; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int wl1251_hw_init_tx_queue_config(struct wl1251 *wl) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct acx_tx_queue_qos_config *config; 25962306a36Sopenharmony_ci struct wl1251_acx_mem_map *wl_mem_map = wl->target_mem_map; 26062306a36Sopenharmony_ci int ret, i; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci wl1251_debug(DEBUG_ACX, "acx tx queue config"); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci config = kzalloc(sizeof(*config), GFP_KERNEL); 26562306a36Sopenharmony_ci if (!config) { 26662306a36Sopenharmony_ci ret = -ENOMEM; 26762306a36Sopenharmony_ci goto out; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci for (i = 0; i < MAX_NUM_OF_AC; i++) { 27162306a36Sopenharmony_ci ret = wl1251_hw_init_txq_fill(i, config, 27262306a36Sopenharmony_ci wl_mem_map->num_tx_mem_blocks); 27362306a36Sopenharmony_ci if (ret < 0) 27462306a36Sopenharmony_ci goto out; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ret = wl1251_cmd_configure(wl, ACX_TX_QUEUE_CFG, 27762306a36Sopenharmony_ci config, sizeof(*config)); 27862306a36Sopenharmony_ci if (ret < 0) 27962306a36Sopenharmony_ci goto out; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci wl1251_acx_ac_cfg(wl, AC_BE, CWMIN_BE, CWMAX_BE, AIFS_DIFS, TXOP_BE); 28362306a36Sopenharmony_ci wl1251_acx_ac_cfg(wl, AC_BK, CWMIN_BK, CWMAX_BK, AIFS_DIFS, TXOP_BK); 28462306a36Sopenharmony_ci wl1251_acx_ac_cfg(wl, AC_VI, CWMIN_VI, CWMAX_VI, AIFS_DIFS, TXOP_VI); 28562306a36Sopenharmony_ci wl1251_acx_ac_cfg(wl, AC_VO, CWMIN_VO, CWMAX_VO, AIFS_DIFS, TXOP_VO); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ciout: 28862306a36Sopenharmony_ci kfree(config); 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int wl1251_hw_init_data_path_config(struct wl1251 *wl) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci int ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* asking for the data path parameters */ 29762306a36Sopenharmony_ci wl->data_path = kzalloc(sizeof(struct acx_data_path_params_resp), 29862306a36Sopenharmony_ci GFP_KERNEL); 29962306a36Sopenharmony_ci if (!wl->data_path) 30062306a36Sopenharmony_ci return -ENOMEM; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci ret = wl1251_acx_data_path_params(wl, wl->data_path); 30362306a36Sopenharmony_ci if (ret < 0) { 30462306a36Sopenharmony_ci kfree(wl->data_path); 30562306a36Sopenharmony_ci wl->data_path = NULL; 30662306a36Sopenharmony_ci return ret; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ciint wl1251_hw_init(struct wl1251 *wl) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct wl1251_acx_mem_map *wl_mem_map; 31662306a36Sopenharmony_ci int ret; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ret = wl1251_hw_init_hwenc_config(wl); 31962306a36Sopenharmony_ci if (ret < 0) 32062306a36Sopenharmony_ci return ret; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* Template settings */ 32362306a36Sopenharmony_ci ret = wl1251_hw_init_templates_config(wl); 32462306a36Sopenharmony_ci if (ret < 0) 32562306a36Sopenharmony_ci return ret; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Default memory configuration */ 32862306a36Sopenharmony_ci ret = wl1251_hw_init_mem_config(wl); 32962306a36Sopenharmony_ci if (ret < 0) 33062306a36Sopenharmony_ci return ret; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* Default data path configuration */ 33362306a36Sopenharmony_ci ret = wl1251_hw_init_data_path_config(wl); 33462306a36Sopenharmony_ci if (ret < 0) 33562306a36Sopenharmony_ci goto out_free_memmap; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* RX config */ 33862306a36Sopenharmony_ci ret = wl1251_hw_init_rx_config(wl, 33962306a36Sopenharmony_ci RX_CFG_PROMISCUOUS | RX_CFG_TSF, 34062306a36Sopenharmony_ci RX_FILTER_OPTION_DEF); 34162306a36Sopenharmony_ci /* RX_CONFIG_OPTION_ANY_DST_ANY_BSS, 34262306a36Sopenharmony_ci RX_FILTER_OPTION_FILTER_ALL); */ 34362306a36Sopenharmony_ci if (ret < 0) 34462306a36Sopenharmony_ci goto out_free_data_path; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* TX queues config */ 34762306a36Sopenharmony_ci ret = wl1251_hw_init_tx_queue_config(wl); 34862306a36Sopenharmony_ci if (ret < 0) 34962306a36Sopenharmony_ci goto out_free_data_path; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* PHY layer config */ 35262306a36Sopenharmony_ci ret = wl1251_hw_init_phy_config(wl); 35362306a36Sopenharmony_ci if (ret < 0) 35462306a36Sopenharmony_ci goto out_free_data_path; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Initialize connection monitoring thresholds */ 35762306a36Sopenharmony_ci ret = wl1251_acx_conn_monit_params(wl); 35862306a36Sopenharmony_ci if (ret < 0) 35962306a36Sopenharmony_ci goto out_free_data_path; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Beacon filtering */ 36262306a36Sopenharmony_ci ret = wl1251_hw_init_beacon_filter(wl); 36362306a36Sopenharmony_ci if (ret < 0) 36462306a36Sopenharmony_ci goto out_free_data_path; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* Bluetooth WLAN coexistence */ 36762306a36Sopenharmony_ci ret = wl1251_hw_init_pta(wl); 36862306a36Sopenharmony_ci if (ret < 0) 36962306a36Sopenharmony_ci goto out_free_data_path; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Energy detection */ 37262306a36Sopenharmony_ci ret = wl1251_hw_init_energy_detection(wl); 37362306a36Sopenharmony_ci if (ret < 0) 37462306a36Sopenharmony_ci goto out_free_data_path; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* Beacons and broadcast settings */ 37762306a36Sopenharmony_ci ret = wl1251_hw_init_beacon_broadcast(wl); 37862306a36Sopenharmony_ci if (ret < 0) 37962306a36Sopenharmony_ci goto out_free_data_path; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* Enable rx data path */ 38262306a36Sopenharmony_ci ret = wl1251_cmd_data_path_rx(wl, wl->channel, 1); 38362306a36Sopenharmony_ci if (ret < 0) 38462306a36Sopenharmony_ci goto out_free_data_path; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Enable tx data path */ 38762306a36Sopenharmony_ci ret = wl1251_cmd_data_path_tx(wl, wl->channel, 1); 38862306a36Sopenharmony_ci if (ret < 0) 38962306a36Sopenharmony_ci goto out_free_data_path; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* Default power state */ 39262306a36Sopenharmony_ci ret = wl1251_hw_init_power_auth(wl); 39362306a36Sopenharmony_ci if (ret < 0) 39462306a36Sopenharmony_ci goto out_free_data_path; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci wl_mem_map = wl->target_mem_map; 39762306a36Sopenharmony_ci wl1251_info("%d tx blocks at 0x%x, %d rx blocks at 0x%x", 39862306a36Sopenharmony_ci wl_mem_map->num_tx_mem_blocks, 39962306a36Sopenharmony_ci wl->data_path->tx_control_addr, 40062306a36Sopenharmony_ci wl_mem_map->num_rx_mem_blocks, 40162306a36Sopenharmony_ci wl->data_path->rx_control_addr); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci out_free_data_path: 40662306a36Sopenharmony_ci kfree(wl->data_path); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci out_free_memmap: 40962306a36Sopenharmony_ci kfree(wl->target_mem_map); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return ret; 41262306a36Sopenharmony_ci} 413