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