xref: /openbmc/linux/drivers/net/wireless/ti/wl1251/init.c (revision c4ee30a2)
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 
1309ed74ba0SDavid Gnedt 	ret = wl1251_acx_group_address_tbl(wl, true, NULL, 0);
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);
313c4ee30a2SColin Ian King 	if (!wl->data_path)
31490921014SLuciano Coelho 		return -ENOMEM;
31590921014SLuciano Coelho 
31690921014SLuciano Coelho 	ret = wl1251_acx_data_path_params(wl, wl->data_path);
31790921014SLuciano Coelho 	if (ret < 0) {
31890921014SLuciano Coelho 		kfree(wl->data_path);
31990921014SLuciano Coelho 		wl->data_path = NULL;
32090921014SLuciano Coelho 		return ret;
32190921014SLuciano Coelho 	}
32290921014SLuciano Coelho 
32390921014SLuciano Coelho 	return 0;
32490921014SLuciano Coelho }
32590921014SLuciano Coelho 
32690921014SLuciano Coelho 
32790921014SLuciano Coelho int wl1251_hw_init(struct wl1251 *wl)
32890921014SLuciano Coelho {
32990921014SLuciano Coelho 	struct wl1251_acx_mem_map *wl_mem_map;
33090921014SLuciano Coelho 	int ret;
33190921014SLuciano Coelho 
33290921014SLuciano Coelho 	ret = wl1251_hw_init_hwenc_config(wl);
33390921014SLuciano Coelho 	if (ret < 0)
33490921014SLuciano Coelho 		return ret;
33590921014SLuciano Coelho 
33690921014SLuciano Coelho 	/* Template settings */
33790921014SLuciano Coelho 	ret = wl1251_hw_init_templates_config(wl);
33890921014SLuciano Coelho 	if (ret < 0)
33990921014SLuciano Coelho 		return ret;
34090921014SLuciano Coelho 
34190921014SLuciano Coelho 	/* Default memory configuration */
34290921014SLuciano Coelho 	ret = wl1251_hw_init_mem_config(wl);
34390921014SLuciano Coelho 	if (ret < 0)
34490921014SLuciano Coelho 		return ret;
34590921014SLuciano Coelho 
34690921014SLuciano Coelho 	/* Default data path configuration  */
34790921014SLuciano Coelho 	ret = wl1251_hw_init_data_path_config(wl);
34890921014SLuciano Coelho 	if (ret < 0)
34990921014SLuciano Coelho 		goto out_free_memmap;
35090921014SLuciano Coelho 
35190921014SLuciano Coelho 	/* RX config */
35290921014SLuciano Coelho 	ret = wl1251_hw_init_rx_config(wl,
35390921014SLuciano Coelho 				       RX_CFG_PROMISCUOUS | RX_CFG_TSF,
35490921014SLuciano Coelho 				       RX_FILTER_OPTION_DEF);
35590921014SLuciano Coelho 	/* RX_CONFIG_OPTION_ANY_DST_ANY_BSS,
35690921014SLuciano Coelho 	   RX_FILTER_OPTION_FILTER_ALL); */
35790921014SLuciano Coelho 	if (ret < 0)
35890921014SLuciano Coelho 		goto out_free_data_path;
35990921014SLuciano Coelho 
36090921014SLuciano Coelho 	/* TX queues config */
36190921014SLuciano Coelho 	ret = wl1251_hw_init_tx_queue_config(wl);
36290921014SLuciano Coelho 	if (ret < 0)
36390921014SLuciano Coelho 		goto out_free_data_path;
36490921014SLuciano Coelho 
36590921014SLuciano Coelho 	/* PHY layer config */
36690921014SLuciano Coelho 	ret = wl1251_hw_init_phy_config(wl);
36790921014SLuciano Coelho 	if (ret < 0)
36890921014SLuciano Coelho 		goto out_free_data_path;
36990921014SLuciano Coelho 
37090921014SLuciano Coelho 	/* Initialize connection monitoring thresholds */
37190921014SLuciano Coelho 	ret = wl1251_acx_conn_monit_params(wl);
37290921014SLuciano Coelho 	if (ret < 0)
37390921014SLuciano Coelho 		goto out_free_data_path;
37490921014SLuciano Coelho 
37590921014SLuciano Coelho 	/* Beacon filtering */
37690921014SLuciano Coelho 	ret = wl1251_hw_init_beacon_filter(wl);
37790921014SLuciano Coelho 	if (ret < 0)
37890921014SLuciano Coelho 		goto out_free_data_path;
37990921014SLuciano Coelho 
38090921014SLuciano Coelho 	/* Bluetooth WLAN coexistence */
38190921014SLuciano Coelho 	ret = wl1251_hw_init_pta(wl);
38290921014SLuciano Coelho 	if (ret < 0)
38390921014SLuciano Coelho 		goto out_free_data_path;
38490921014SLuciano Coelho 
38590921014SLuciano Coelho 	/* Energy detection */
38690921014SLuciano Coelho 	ret = wl1251_hw_init_energy_detection(wl);
38790921014SLuciano Coelho 	if (ret < 0)
38890921014SLuciano Coelho 		goto out_free_data_path;
38990921014SLuciano Coelho 
39090921014SLuciano Coelho 	/* Beacons and boradcast settings */
39190921014SLuciano Coelho 	ret = wl1251_hw_init_beacon_broadcast(wl);
39290921014SLuciano Coelho 	if (ret < 0)
39390921014SLuciano Coelho 		goto out_free_data_path;
39490921014SLuciano Coelho 
3959281691fSDavid Gnedt 	/* Enable rx data path */
3969281691fSDavid Gnedt 	ret = wl1251_cmd_data_path_rx(wl, wl->channel, 1);
3979281691fSDavid Gnedt 	if (ret < 0)
3989281691fSDavid Gnedt 		goto out_free_data_path;
3999281691fSDavid Gnedt 
4009281691fSDavid Gnedt 	/* Enable tx data path */
4019281691fSDavid Gnedt 	ret = wl1251_cmd_data_path_tx(wl, wl->channel, 1);
40290921014SLuciano Coelho 	if (ret < 0)
40390921014SLuciano Coelho 		goto out_free_data_path;
40490921014SLuciano Coelho 
40590921014SLuciano Coelho 	/* Default power state */
40690921014SLuciano Coelho 	ret = wl1251_hw_init_power_auth(wl);
40790921014SLuciano Coelho 	if (ret < 0)
40890921014SLuciano Coelho 		goto out_free_data_path;
40990921014SLuciano Coelho 
41090921014SLuciano Coelho 	wl_mem_map = wl->target_mem_map;
41190921014SLuciano Coelho 	wl1251_info("%d tx blocks at 0x%x, %d rx blocks at 0x%x",
41290921014SLuciano Coelho 		    wl_mem_map->num_tx_mem_blocks,
41390921014SLuciano Coelho 		    wl->data_path->tx_control_addr,
41490921014SLuciano Coelho 		    wl_mem_map->num_rx_mem_blocks,
41590921014SLuciano Coelho 		    wl->data_path->rx_control_addr);
41690921014SLuciano Coelho 
41790921014SLuciano Coelho 	return 0;
41890921014SLuciano Coelho 
41990921014SLuciano Coelho  out_free_data_path:
42090921014SLuciano Coelho 	kfree(wl->data_path);
42190921014SLuciano Coelho 
42290921014SLuciano Coelho  out_free_memmap:
42390921014SLuciano Coelho 	kfree(wl->target_mem_map);
42490921014SLuciano Coelho 
42590921014SLuciano Coelho 	return ret;
42690921014SLuciano Coelho }
427