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