18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This file is part of wl1251
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Nokia Corporation
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "init.h"
138c2ecf20Sopenharmony_ci#include "wl12xx_80211.h"
148c2ecf20Sopenharmony_ci#include "acx.h"
158c2ecf20Sopenharmony_ci#include "cmd.h"
168c2ecf20Sopenharmony_ci#include "reg.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ciint wl1251_hw_init_hwenc_config(struct wl1251 *wl)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	int ret;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	ret = wl1251_acx_feature_cfg(wl, 0);
238c2ecf20Sopenharmony_ci	if (ret < 0) {
248c2ecf20Sopenharmony_ci		wl1251_warning("couldn't set feature config");
258c2ecf20Sopenharmony_ci		return ret;
268c2ecf20Sopenharmony_ci	}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	ret = wl1251_acx_default_key(wl, wl->default_key);
298c2ecf20Sopenharmony_ci	if (ret < 0) {
308c2ecf20Sopenharmony_ci		wl1251_warning("couldn't set default key");
318c2ecf20Sopenharmony_ci		return ret;
328c2ecf20Sopenharmony_ci	}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	return 0;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ciint wl1251_hw_init_templates_config(struct wl1251 *wl)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	int ret;
408c2ecf20Sopenharmony_ci	u8 partial_vbm[PARTIAL_VBM_MAX];
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/* send empty templates for fw memory reservation */
438c2ecf20Sopenharmony_ci	ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, NULL,
448c2ecf20Sopenharmony_ci				      sizeof(struct wl12xx_probe_req_template));
458c2ecf20Sopenharmony_ci	if (ret < 0)
468c2ecf20Sopenharmony_ci		return ret;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, NULL,
498c2ecf20Sopenharmony_ci				      sizeof(struct wl12xx_null_data_template));
508c2ecf20Sopenharmony_ci	if (ret < 0)
518c2ecf20Sopenharmony_ci		return ret;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	ret = wl1251_cmd_template_set(wl, CMD_PS_POLL, NULL,
548c2ecf20Sopenharmony_ci				      sizeof(struct wl12xx_ps_poll_template));
558c2ecf20Sopenharmony_ci	if (ret < 0)
568c2ecf20Sopenharmony_ci		return ret;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	ret = wl1251_cmd_template_set(wl, CMD_QOS_NULL_DATA, NULL,
598c2ecf20Sopenharmony_ci				      sizeof
608c2ecf20Sopenharmony_ci				      (struct wl12xx_qos_null_data_template));
618c2ecf20Sopenharmony_ci	if (ret < 0)
628c2ecf20Sopenharmony_ci		return ret;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, NULL,
658c2ecf20Sopenharmony_ci				      sizeof
668c2ecf20Sopenharmony_ci				      (struct wl12xx_probe_resp_template));
678c2ecf20Sopenharmony_ci	if (ret < 0)
688c2ecf20Sopenharmony_ci		return ret;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	ret = wl1251_cmd_template_set(wl, CMD_BEACON, NULL,
718c2ecf20Sopenharmony_ci				      sizeof
728c2ecf20Sopenharmony_ci				      (struct wl12xx_beacon_template));
738c2ecf20Sopenharmony_ci	if (ret < 0)
748c2ecf20Sopenharmony_ci		return ret;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* tim templates, first reserve space then allocate an empty one */
778c2ecf20Sopenharmony_ci	memset(partial_vbm, 0, PARTIAL_VBM_MAX);
788c2ecf20Sopenharmony_ci	ret = wl1251_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, PARTIAL_VBM_MAX, 0);
798c2ecf20Sopenharmony_ci	if (ret < 0)
808c2ecf20Sopenharmony_ci		return ret;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	ret = wl1251_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, 1, 0);
838c2ecf20Sopenharmony_ci	if (ret < 0)
848c2ecf20Sopenharmony_ci		return ret;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return 0;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ciint wl1251_hw_init_rx_config(struct wl1251 *wl, u32 config, u32 filter)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	int ret;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	ret = wl1251_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF);
948c2ecf20Sopenharmony_ci	if (ret < 0)
958c2ecf20Sopenharmony_ci		return ret;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	ret = wl1251_acx_rx_config(wl, config, filter);
988c2ecf20Sopenharmony_ci	if (ret < 0)
998c2ecf20Sopenharmony_ci		return ret;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return 0;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ciint wl1251_hw_init_phy_config(struct wl1251 *wl)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	int ret;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	ret = wl1251_acx_pd_threshold(wl);
1098c2ecf20Sopenharmony_ci	if (ret < 0)
1108c2ecf20Sopenharmony_ci		return ret;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	ret = wl1251_acx_slot(wl, DEFAULT_SLOT_TIME);
1138c2ecf20Sopenharmony_ci	if (ret < 0)
1148c2ecf20Sopenharmony_ci		return ret;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	ret = wl1251_acx_group_address_tbl(wl, true, NULL, 0);
1178c2ecf20Sopenharmony_ci	if (ret < 0)
1188c2ecf20Sopenharmony_ci		return ret;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	ret = wl1251_acx_service_period_timeout(wl);
1218c2ecf20Sopenharmony_ci	if (ret < 0)
1228c2ecf20Sopenharmony_ci		return ret;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	ret = wl1251_acx_rts_threshold(wl, RTS_THRESHOLD_DEF);
1258c2ecf20Sopenharmony_ci	if (ret < 0)
1268c2ecf20Sopenharmony_ci		return ret;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return 0;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ciint wl1251_hw_init_beacon_filter(struct wl1251 *wl)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	int ret;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/* disable beacon filtering at this stage */
1368c2ecf20Sopenharmony_ci	ret = wl1251_acx_beacon_filter_opt(wl, false);
1378c2ecf20Sopenharmony_ci	if (ret < 0)
1388c2ecf20Sopenharmony_ci		return ret;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	ret = wl1251_acx_beacon_filter_table(wl);
1418c2ecf20Sopenharmony_ci	if (ret < 0)
1428c2ecf20Sopenharmony_ci		return ret;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	return 0;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ciint wl1251_hw_init_pta(struct wl1251 *wl)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	int ret;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	ret = wl1251_acx_sg_enable(wl);
1528c2ecf20Sopenharmony_ci	if (ret < 0)
1538c2ecf20Sopenharmony_ci		return ret;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	ret = wl1251_acx_sg_cfg(wl);
1568c2ecf20Sopenharmony_ci	if (ret < 0)
1578c2ecf20Sopenharmony_ci		return ret;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return 0;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ciint wl1251_hw_init_energy_detection(struct wl1251 *wl)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	int ret;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	ret = wl1251_acx_cca_threshold(wl);
1678c2ecf20Sopenharmony_ci	if (ret < 0)
1688c2ecf20Sopenharmony_ci		return ret;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return 0;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ciint wl1251_hw_init_beacon_broadcast(struct wl1251 *wl)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	int ret;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	ret = wl1251_acx_bcn_dtim_options(wl);
1788c2ecf20Sopenharmony_ci	if (ret < 0)
1798c2ecf20Sopenharmony_ci		return ret;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return 0;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ciint wl1251_hw_init_power_auth(struct wl1251 *wl)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	return wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ciint wl1251_hw_init_mem_config(struct wl1251 *wl)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	int ret;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	ret = wl1251_acx_mem_cfg(wl);
1948c2ecf20Sopenharmony_ci	if (ret < 0)
1958c2ecf20Sopenharmony_ci		return ret;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	wl->target_mem_map = kzalloc(sizeof(struct wl1251_acx_mem_map),
1988c2ecf20Sopenharmony_ci					  GFP_KERNEL);
1998c2ecf20Sopenharmony_ci	if (!wl->target_mem_map) {
2008c2ecf20Sopenharmony_ci		wl1251_error("couldn't allocate target memory map");
2018c2ecf20Sopenharmony_ci		return -ENOMEM;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	/* we now ask for the firmware built memory map */
2058c2ecf20Sopenharmony_ci	ret = wl1251_acx_mem_map(wl, wl->target_mem_map,
2068c2ecf20Sopenharmony_ci				 sizeof(struct wl1251_acx_mem_map));
2078c2ecf20Sopenharmony_ci	if (ret < 0) {
2088c2ecf20Sopenharmony_ci		wl1251_error("couldn't retrieve firmware memory map");
2098c2ecf20Sopenharmony_ci		kfree(wl->target_mem_map);
2108c2ecf20Sopenharmony_ci		wl->target_mem_map = NULL;
2118c2ecf20Sopenharmony_ci		return ret;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return 0;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int wl1251_hw_init_txq_fill(u8 qid,
2188c2ecf20Sopenharmony_ci				   struct acx_tx_queue_qos_config *config,
2198c2ecf20Sopenharmony_ci				   u32 num_blocks)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	config->qid = qid;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	switch (qid) {
2248c2ecf20Sopenharmony_ci	case QOS_AC_BE:
2258c2ecf20Sopenharmony_ci		config->high_threshold =
2268c2ecf20Sopenharmony_ci			(QOS_TX_HIGH_BE_DEF * num_blocks) / 100;
2278c2ecf20Sopenharmony_ci		config->low_threshold =
2288c2ecf20Sopenharmony_ci			(QOS_TX_LOW_BE_DEF * num_blocks) / 100;
2298c2ecf20Sopenharmony_ci		break;
2308c2ecf20Sopenharmony_ci	case QOS_AC_BK:
2318c2ecf20Sopenharmony_ci		config->high_threshold =
2328c2ecf20Sopenharmony_ci			(QOS_TX_HIGH_BK_DEF * num_blocks) / 100;
2338c2ecf20Sopenharmony_ci		config->low_threshold =
2348c2ecf20Sopenharmony_ci			(QOS_TX_LOW_BK_DEF * num_blocks) / 100;
2358c2ecf20Sopenharmony_ci		break;
2368c2ecf20Sopenharmony_ci	case QOS_AC_VI:
2378c2ecf20Sopenharmony_ci		config->high_threshold =
2388c2ecf20Sopenharmony_ci			(QOS_TX_HIGH_VI_DEF * num_blocks) / 100;
2398c2ecf20Sopenharmony_ci		config->low_threshold =
2408c2ecf20Sopenharmony_ci			(QOS_TX_LOW_VI_DEF * num_blocks) / 100;
2418c2ecf20Sopenharmony_ci		break;
2428c2ecf20Sopenharmony_ci	case QOS_AC_VO:
2438c2ecf20Sopenharmony_ci		config->high_threshold =
2448c2ecf20Sopenharmony_ci			(QOS_TX_HIGH_VO_DEF * num_blocks) / 100;
2458c2ecf20Sopenharmony_ci		config->low_threshold =
2468c2ecf20Sopenharmony_ci			(QOS_TX_LOW_VO_DEF * num_blocks) / 100;
2478c2ecf20Sopenharmony_ci		break;
2488c2ecf20Sopenharmony_ci	default:
2498c2ecf20Sopenharmony_ci		wl1251_error("Invalid TX queue id: %d", qid);
2508c2ecf20Sopenharmony_ci		return -EINVAL;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	return 0;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic int wl1251_hw_init_tx_queue_config(struct wl1251 *wl)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	struct acx_tx_queue_qos_config *config;
2598c2ecf20Sopenharmony_ci	struct wl1251_acx_mem_map *wl_mem_map = wl->target_mem_map;
2608c2ecf20Sopenharmony_ci	int ret, i;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	wl1251_debug(DEBUG_ACX, "acx tx queue config");
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	config = kzalloc(sizeof(*config), GFP_KERNEL);
2658c2ecf20Sopenharmony_ci	if (!config) {
2668c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2678c2ecf20Sopenharmony_ci		goto out;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_NUM_OF_AC; i++) {
2718c2ecf20Sopenharmony_ci		ret = wl1251_hw_init_txq_fill(i, config,
2728c2ecf20Sopenharmony_ci					      wl_mem_map->num_tx_mem_blocks);
2738c2ecf20Sopenharmony_ci		if (ret < 0)
2748c2ecf20Sopenharmony_ci			goto out;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		ret = wl1251_cmd_configure(wl, ACX_TX_QUEUE_CFG,
2778c2ecf20Sopenharmony_ci					   config, sizeof(*config));
2788c2ecf20Sopenharmony_ci		if (ret < 0)
2798c2ecf20Sopenharmony_ci			goto out;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	wl1251_acx_ac_cfg(wl, AC_BE, CWMIN_BE, CWMAX_BE, AIFS_DIFS, TXOP_BE);
2838c2ecf20Sopenharmony_ci	wl1251_acx_ac_cfg(wl, AC_BK, CWMIN_BK, CWMAX_BK, AIFS_DIFS, TXOP_BK);
2848c2ecf20Sopenharmony_ci	wl1251_acx_ac_cfg(wl, AC_VI, CWMIN_VI, CWMAX_VI, AIFS_DIFS, TXOP_VI);
2858c2ecf20Sopenharmony_ci	wl1251_acx_ac_cfg(wl, AC_VO, CWMIN_VO, CWMAX_VO, AIFS_DIFS, TXOP_VO);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ciout:
2888c2ecf20Sopenharmony_ci	kfree(config);
2898c2ecf20Sopenharmony_ci	return ret;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic int wl1251_hw_init_data_path_config(struct wl1251 *wl)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	int ret;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* asking for the data path parameters */
2978c2ecf20Sopenharmony_ci	wl->data_path = kzalloc(sizeof(struct acx_data_path_params_resp),
2988c2ecf20Sopenharmony_ci				GFP_KERNEL);
2998c2ecf20Sopenharmony_ci	if (!wl->data_path)
3008c2ecf20Sopenharmony_ci		return -ENOMEM;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	ret = wl1251_acx_data_path_params(wl, wl->data_path);
3038c2ecf20Sopenharmony_ci	if (ret < 0) {
3048c2ecf20Sopenharmony_ci		kfree(wl->data_path);
3058c2ecf20Sopenharmony_ci		wl->data_path = NULL;
3068c2ecf20Sopenharmony_ci		return ret;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return 0;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ciint wl1251_hw_init(struct wl1251 *wl)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct wl1251_acx_mem_map *wl_mem_map;
3168c2ecf20Sopenharmony_ci	int ret;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	ret = wl1251_hw_init_hwenc_config(wl);
3198c2ecf20Sopenharmony_ci	if (ret < 0)
3208c2ecf20Sopenharmony_ci		return ret;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/* Template settings */
3238c2ecf20Sopenharmony_ci	ret = wl1251_hw_init_templates_config(wl);
3248c2ecf20Sopenharmony_ci	if (ret < 0)
3258c2ecf20Sopenharmony_ci		return ret;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/* Default memory configuration */
3288c2ecf20Sopenharmony_ci	ret = wl1251_hw_init_mem_config(wl);
3298c2ecf20Sopenharmony_ci	if (ret < 0)
3308c2ecf20Sopenharmony_ci		return ret;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* Default data path configuration  */
3338c2ecf20Sopenharmony_ci	ret = wl1251_hw_init_data_path_config(wl);
3348c2ecf20Sopenharmony_ci	if (ret < 0)
3358c2ecf20Sopenharmony_ci		goto out_free_memmap;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* RX config */
3388c2ecf20Sopenharmony_ci	ret = wl1251_hw_init_rx_config(wl,
3398c2ecf20Sopenharmony_ci				       RX_CFG_PROMISCUOUS | RX_CFG_TSF,
3408c2ecf20Sopenharmony_ci				       RX_FILTER_OPTION_DEF);
3418c2ecf20Sopenharmony_ci	/* RX_CONFIG_OPTION_ANY_DST_ANY_BSS,
3428c2ecf20Sopenharmony_ci	   RX_FILTER_OPTION_FILTER_ALL); */
3438c2ecf20Sopenharmony_ci	if (ret < 0)
3448c2ecf20Sopenharmony_ci		goto out_free_data_path;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/* TX queues config */
3478c2ecf20Sopenharmony_ci	ret = wl1251_hw_init_tx_queue_config(wl);
3488c2ecf20Sopenharmony_ci	if (ret < 0)
3498c2ecf20Sopenharmony_ci		goto out_free_data_path;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* PHY layer config */
3528c2ecf20Sopenharmony_ci	ret = wl1251_hw_init_phy_config(wl);
3538c2ecf20Sopenharmony_ci	if (ret < 0)
3548c2ecf20Sopenharmony_ci		goto out_free_data_path;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* Initialize connection monitoring thresholds */
3578c2ecf20Sopenharmony_ci	ret = wl1251_acx_conn_monit_params(wl);
3588c2ecf20Sopenharmony_ci	if (ret < 0)
3598c2ecf20Sopenharmony_ci		goto out_free_data_path;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* Beacon filtering */
3628c2ecf20Sopenharmony_ci	ret = wl1251_hw_init_beacon_filter(wl);
3638c2ecf20Sopenharmony_ci	if (ret < 0)
3648c2ecf20Sopenharmony_ci		goto out_free_data_path;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	/* Bluetooth WLAN coexistence */
3678c2ecf20Sopenharmony_ci	ret = wl1251_hw_init_pta(wl);
3688c2ecf20Sopenharmony_ci	if (ret < 0)
3698c2ecf20Sopenharmony_ci		goto out_free_data_path;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/* Energy detection */
3728c2ecf20Sopenharmony_ci	ret = wl1251_hw_init_energy_detection(wl);
3738c2ecf20Sopenharmony_ci	if (ret < 0)
3748c2ecf20Sopenharmony_ci		goto out_free_data_path;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* Beacons and boradcast settings */
3778c2ecf20Sopenharmony_ci	ret = wl1251_hw_init_beacon_broadcast(wl);
3788c2ecf20Sopenharmony_ci	if (ret < 0)
3798c2ecf20Sopenharmony_ci		goto out_free_data_path;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	/* Enable rx data path */
3828c2ecf20Sopenharmony_ci	ret = wl1251_cmd_data_path_rx(wl, wl->channel, 1);
3838c2ecf20Sopenharmony_ci	if (ret < 0)
3848c2ecf20Sopenharmony_ci		goto out_free_data_path;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* Enable tx data path */
3878c2ecf20Sopenharmony_ci	ret = wl1251_cmd_data_path_tx(wl, wl->channel, 1);
3888c2ecf20Sopenharmony_ci	if (ret < 0)
3898c2ecf20Sopenharmony_ci		goto out_free_data_path;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/* Default power state */
3928c2ecf20Sopenharmony_ci	ret = wl1251_hw_init_power_auth(wl);
3938c2ecf20Sopenharmony_ci	if (ret < 0)
3948c2ecf20Sopenharmony_ci		goto out_free_data_path;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	wl_mem_map = wl->target_mem_map;
3978c2ecf20Sopenharmony_ci	wl1251_info("%d tx blocks at 0x%x, %d rx blocks at 0x%x",
3988c2ecf20Sopenharmony_ci		    wl_mem_map->num_tx_mem_blocks,
3998c2ecf20Sopenharmony_ci		    wl->data_path->tx_control_addr,
4008c2ecf20Sopenharmony_ci		    wl_mem_map->num_rx_mem_blocks,
4018c2ecf20Sopenharmony_ci		    wl->data_path->rx_control_addr);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	return 0;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci out_free_data_path:
4068c2ecf20Sopenharmony_ci	kfree(wl->data_path);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci out_free_memmap:
4098c2ecf20Sopenharmony_ci	kfree(wl->target_mem_map);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return ret;
4128c2ecf20Sopenharmony_ci}
413