18e84c258SEugene Krasnikov /*
28e84c258SEugene Krasnikov  * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
38e84c258SEugene Krasnikov  *
48e84c258SEugene Krasnikov  * Permission to use, copy, modify, and/or distribute this software for any
58e84c258SEugene Krasnikov  * purpose with or without fee is hereby granted, provided that the above
68e84c258SEugene Krasnikov  * copyright notice and this permission notice appear in all copies.
78e84c258SEugene Krasnikov  *
88e84c258SEugene Krasnikov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98e84c258SEugene Krasnikov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
108e84c258SEugene Krasnikov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
118e84c258SEugene Krasnikov  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
128e84c258SEugene Krasnikov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
138e84c258SEugene Krasnikov  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
148e84c258SEugene Krasnikov  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
158e84c258SEugene Krasnikov  */
168e84c258SEugene Krasnikov 
178e84c258SEugene Krasnikov #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
188e84c258SEugene Krasnikov 
198e84c258SEugene Krasnikov #include <linux/module.h>
204bda7fafSPontus Fuchs #include <linux/firmware.h>
218e84c258SEugene Krasnikov #include <linux/platform_device.h>
2205ddce49SBjorn Andersson #include <linux/of_address.h>
2305ddce49SBjorn Andersson #include <linux/of_device.h>
24f303a931SBjorn Andersson #include <linux/of_irq.h>
255052de8dSBjorn Andersson #include <linux/rpmsg.h>
26f303a931SBjorn Andersson #include <linux/soc/qcom/smem_state.h>
27f303a931SBjorn Andersson #include <linux/soc/qcom/wcnss_ctrl.h>
288e84c258SEugene Krasnikov #include "wcn36xx.h"
298e84c258SEugene Krasnikov 
308e84c258SEugene Krasnikov unsigned int wcn36xx_dbg_mask;
318e84c258SEugene Krasnikov module_param_named(debug_mask, wcn36xx_dbg_mask, uint, 0644);
328e84c258SEugene Krasnikov MODULE_PARM_DESC(debug_mask, "Debugging mask");
338e84c258SEugene Krasnikov 
348e84c258SEugene Krasnikov #define CHAN2G(_freq, _idx) { \
3557fbcce3SJohannes Berg 	.band = NL80211_BAND_2GHZ, \
368e84c258SEugene Krasnikov 	.center_freq = (_freq), \
378e84c258SEugene Krasnikov 	.hw_value = (_idx), \
388e84c258SEugene Krasnikov 	.max_power = 25, \
398e84c258SEugene Krasnikov }
408e84c258SEugene Krasnikov 
418e84c258SEugene Krasnikov #define CHAN5G(_freq, _idx) { \
4257fbcce3SJohannes Berg 	.band = NL80211_BAND_5GHZ, \
438e84c258SEugene Krasnikov 	.center_freq = (_freq), \
448e84c258SEugene Krasnikov 	.hw_value = (_idx), \
458e84c258SEugene Krasnikov 	.max_power = 25, \
468e84c258SEugene Krasnikov }
478e84c258SEugene Krasnikov 
488e84c258SEugene Krasnikov /* The wcn firmware expects channel values to matching
498e84c258SEugene Krasnikov  * their mnemonic values. So use these for .hw_value. */
508e84c258SEugene Krasnikov static struct ieee80211_channel wcn_2ghz_channels[] = {
518e84c258SEugene Krasnikov 	CHAN2G(2412, 1), /* Channel 1 */
528e84c258SEugene Krasnikov 	CHAN2G(2417, 2), /* Channel 2 */
538e84c258SEugene Krasnikov 	CHAN2G(2422, 3), /* Channel 3 */
548e84c258SEugene Krasnikov 	CHAN2G(2427, 4), /* Channel 4 */
558e84c258SEugene Krasnikov 	CHAN2G(2432, 5), /* Channel 5 */
568e84c258SEugene Krasnikov 	CHAN2G(2437, 6), /* Channel 6 */
578e84c258SEugene Krasnikov 	CHAN2G(2442, 7), /* Channel 7 */
588e84c258SEugene Krasnikov 	CHAN2G(2447, 8), /* Channel 8 */
598e84c258SEugene Krasnikov 	CHAN2G(2452, 9), /* Channel 9 */
608e84c258SEugene Krasnikov 	CHAN2G(2457, 10), /* Channel 10 */
618e84c258SEugene Krasnikov 	CHAN2G(2462, 11), /* Channel 11 */
628e84c258SEugene Krasnikov 	CHAN2G(2467, 12), /* Channel 12 */
638e84c258SEugene Krasnikov 	CHAN2G(2472, 13), /* Channel 13 */
648e84c258SEugene Krasnikov 	CHAN2G(2484, 14)  /* Channel 14 */
658e84c258SEugene Krasnikov 
668e84c258SEugene Krasnikov };
678e84c258SEugene Krasnikov 
688e84c258SEugene Krasnikov static struct ieee80211_channel wcn_5ghz_channels[] = {
698e84c258SEugene Krasnikov 	CHAN5G(5180, 36),
708e84c258SEugene Krasnikov 	CHAN5G(5200, 40),
718e84c258SEugene Krasnikov 	CHAN5G(5220, 44),
728e84c258SEugene Krasnikov 	CHAN5G(5240, 48),
738e84c258SEugene Krasnikov 	CHAN5G(5260, 52),
748e84c258SEugene Krasnikov 	CHAN5G(5280, 56),
758e84c258SEugene Krasnikov 	CHAN5G(5300, 60),
768e84c258SEugene Krasnikov 	CHAN5G(5320, 64),
778e84c258SEugene Krasnikov 	CHAN5G(5500, 100),
788e84c258SEugene Krasnikov 	CHAN5G(5520, 104),
798e84c258SEugene Krasnikov 	CHAN5G(5540, 108),
808e84c258SEugene Krasnikov 	CHAN5G(5560, 112),
818e84c258SEugene Krasnikov 	CHAN5G(5580, 116),
828e84c258SEugene Krasnikov 	CHAN5G(5600, 120),
838e84c258SEugene Krasnikov 	CHAN5G(5620, 124),
848e84c258SEugene Krasnikov 	CHAN5G(5640, 128),
858e84c258SEugene Krasnikov 	CHAN5G(5660, 132),
868e84c258SEugene Krasnikov 	CHAN5G(5700, 140),
878e84c258SEugene Krasnikov 	CHAN5G(5745, 149),
888e84c258SEugene Krasnikov 	CHAN5G(5765, 153),
898e84c258SEugene Krasnikov 	CHAN5G(5785, 157),
908e84c258SEugene Krasnikov 	CHAN5G(5805, 161),
918e84c258SEugene Krasnikov 	CHAN5G(5825, 165)
928e84c258SEugene Krasnikov };
938e84c258SEugene Krasnikov 
948e84c258SEugene Krasnikov #define RATE(_bitrate, _hw_rate, _flags) { \
958e84c258SEugene Krasnikov 	.bitrate        = (_bitrate),                   \
968e84c258SEugene Krasnikov 	.flags          = (_flags),                     \
978e84c258SEugene Krasnikov 	.hw_value       = (_hw_rate),                   \
988e84c258SEugene Krasnikov 	.hw_value_short = (_hw_rate)  \
998e84c258SEugene Krasnikov }
1008e84c258SEugene Krasnikov 
1018e84c258SEugene Krasnikov static struct ieee80211_rate wcn_2ghz_rates[] = {
1028e84c258SEugene Krasnikov 	RATE(10, HW_RATE_INDEX_1MBPS, 0),
1038e84c258SEugene Krasnikov 	RATE(20, HW_RATE_INDEX_2MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
1048e84c258SEugene Krasnikov 	RATE(55, HW_RATE_INDEX_5_5MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
1058e84c258SEugene Krasnikov 	RATE(110, HW_RATE_INDEX_11MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
1068e84c258SEugene Krasnikov 	RATE(60, HW_RATE_INDEX_6MBPS, 0),
1078e84c258SEugene Krasnikov 	RATE(90, HW_RATE_INDEX_9MBPS, 0),
1088e84c258SEugene Krasnikov 	RATE(120, HW_RATE_INDEX_12MBPS, 0),
1098e84c258SEugene Krasnikov 	RATE(180, HW_RATE_INDEX_18MBPS, 0),
1108e84c258SEugene Krasnikov 	RATE(240, HW_RATE_INDEX_24MBPS, 0),
1118e84c258SEugene Krasnikov 	RATE(360, HW_RATE_INDEX_36MBPS, 0),
1128e84c258SEugene Krasnikov 	RATE(480, HW_RATE_INDEX_48MBPS, 0),
1138e84c258SEugene Krasnikov 	RATE(540, HW_RATE_INDEX_54MBPS, 0)
1148e84c258SEugene Krasnikov };
1158e84c258SEugene Krasnikov 
1168e84c258SEugene Krasnikov static struct ieee80211_rate wcn_5ghz_rates[] = {
1178e84c258SEugene Krasnikov 	RATE(60, HW_RATE_INDEX_6MBPS, 0),
1188e84c258SEugene Krasnikov 	RATE(90, HW_RATE_INDEX_9MBPS, 0),
1198e84c258SEugene Krasnikov 	RATE(120, HW_RATE_INDEX_12MBPS, 0),
1208e84c258SEugene Krasnikov 	RATE(180, HW_RATE_INDEX_18MBPS, 0),
1218e84c258SEugene Krasnikov 	RATE(240, HW_RATE_INDEX_24MBPS, 0),
1228e84c258SEugene Krasnikov 	RATE(360, HW_RATE_INDEX_36MBPS, 0),
1238e84c258SEugene Krasnikov 	RATE(480, HW_RATE_INDEX_48MBPS, 0),
1248e84c258SEugene Krasnikov 	RATE(540, HW_RATE_INDEX_54MBPS, 0)
1258e84c258SEugene Krasnikov };
1268e84c258SEugene Krasnikov 
1278e84c258SEugene Krasnikov static struct ieee80211_supported_band wcn_band_2ghz = {
1288e84c258SEugene Krasnikov 	.channels	= wcn_2ghz_channels,
1298e84c258SEugene Krasnikov 	.n_channels	= ARRAY_SIZE(wcn_2ghz_channels),
1308e84c258SEugene Krasnikov 	.bitrates	= wcn_2ghz_rates,
1318e84c258SEugene Krasnikov 	.n_bitrates	= ARRAY_SIZE(wcn_2ghz_rates),
1328e84c258SEugene Krasnikov 	.ht_cap		= {
1338e84c258SEugene Krasnikov 		.cap =	IEEE80211_HT_CAP_GRN_FLD |
1348e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_SGI_20 |
1358e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_DSSSCCK40 |
1368e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_LSIG_TXOP_PROT,
1378e84c258SEugene Krasnikov 		.ht_supported = true,
1388e84c258SEugene Krasnikov 		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
1398e84c258SEugene Krasnikov 		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
1408e84c258SEugene Krasnikov 		.mcs = {
1418e84c258SEugene Krasnikov 			.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
1428e84c258SEugene Krasnikov 			.rx_highest = cpu_to_le16(72),
1438e84c258SEugene Krasnikov 			.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
1448e84c258SEugene Krasnikov 		}
1458e84c258SEugene Krasnikov 	}
1468e84c258SEugene Krasnikov };
1478e84c258SEugene Krasnikov 
1488e84c258SEugene Krasnikov static struct ieee80211_supported_band wcn_band_5ghz = {
1498e84c258SEugene Krasnikov 	.channels	= wcn_5ghz_channels,
1508e84c258SEugene Krasnikov 	.n_channels	= ARRAY_SIZE(wcn_5ghz_channels),
1518e84c258SEugene Krasnikov 	.bitrates	= wcn_5ghz_rates,
1528e84c258SEugene Krasnikov 	.n_bitrates	= ARRAY_SIZE(wcn_5ghz_rates),
1538e84c258SEugene Krasnikov 	.ht_cap		= {
1548e84c258SEugene Krasnikov 		.cap =	IEEE80211_HT_CAP_GRN_FLD |
1558e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_SGI_20 |
1568e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_DSSSCCK40 |
1578e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_LSIG_TXOP_PROT |
1588e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_SGI_40 |
1598e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_SUP_WIDTH_20_40,
1608e84c258SEugene Krasnikov 		.ht_supported = true,
1618e84c258SEugene Krasnikov 		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
1628e84c258SEugene Krasnikov 		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
1638e84c258SEugene Krasnikov 		.mcs = {
1648e84c258SEugene Krasnikov 			.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
1658e84c258SEugene Krasnikov 			.rx_highest = cpu_to_le16(72),
1668e84c258SEugene Krasnikov 			.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
1678e84c258SEugene Krasnikov 		}
1688e84c258SEugene Krasnikov 	}
1698e84c258SEugene Krasnikov };
1708e84c258SEugene Krasnikov 
1718e84c258SEugene Krasnikov #ifdef CONFIG_PM
1728e84c258SEugene Krasnikov 
1738e84c258SEugene Krasnikov static const struct wiphy_wowlan_support wowlan_support = {
1748e84c258SEugene Krasnikov 	.flags = WIPHY_WOWLAN_ANY
1758e84c258SEugene Krasnikov };
1768e84c258SEugene Krasnikov 
1778e84c258SEugene Krasnikov #endif
1788e84c258SEugene Krasnikov 
1798e84c258SEugene Krasnikov static inline u8 get_sta_index(struct ieee80211_vif *vif,
1808e84c258SEugene Krasnikov 			       struct wcn36xx_sta *sta_priv)
1818e84c258SEugene Krasnikov {
1828e84c258SEugene Krasnikov 	return NL80211_IFTYPE_STATION == vif->type ?
1838e84c258SEugene Krasnikov 	       sta_priv->bss_sta_index :
1848e84c258SEugene Krasnikov 	       sta_priv->sta_index;
1858e84c258SEugene Krasnikov }
1868e84c258SEugene Krasnikov 
1872be6636aSPontus Fuchs static const char * const wcn36xx_caps_names[] = {
1882be6636aSPontus Fuchs 	"MCC",				/* 0 */
1892be6636aSPontus Fuchs 	"P2P",				/* 1 */
1902be6636aSPontus Fuchs 	"DOT11AC",			/* 2 */
1912be6636aSPontus Fuchs 	"SLM_SESSIONIZATION",		/* 3 */
1922be6636aSPontus Fuchs 	"DOT11AC_OPMODE",		/* 4 */
1932be6636aSPontus Fuchs 	"SAP32STA",			/* 5 */
1942be6636aSPontus Fuchs 	"TDLS",				/* 6 */
1952be6636aSPontus Fuchs 	"P2P_GO_NOA_DECOUPLE_INIT_SCAN",/* 7 */
1962be6636aSPontus Fuchs 	"WLANACTIVE_OFFLOAD",		/* 8 */
1972be6636aSPontus Fuchs 	"BEACON_OFFLOAD",		/* 9 */
1982be6636aSPontus Fuchs 	"SCAN_OFFLOAD",			/* 10 */
1992be6636aSPontus Fuchs 	"ROAM_OFFLOAD",			/* 11 */
2002be6636aSPontus Fuchs 	"BCN_MISS_OFFLOAD",		/* 12 */
2012be6636aSPontus Fuchs 	"STA_POWERSAVE",		/* 13 */
2022be6636aSPontus Fuchs 	"STA_ADVANCED_PWRSAVE",		/* 14 */
2032be6636aSPontus Fuchs 	"AP_UAPSD",			/* 15 */
2042be6636aSPontus Fuchs 	"AP_DFS",			/* 16 */
2052be6636aSPontus Fuchs 	"BLOCKACK",			/* 17 */
2062be6636aSPontus Fuchs 	"PHY_ERR",			/* 18 */
2072be6636aSPontus Fuchs 	"BCN_FILTER",			/* 19 */
2082be6636aSPontus Fuchs 	"RTT",				/* 20 */
2092be6636aSPontus Fuchs 	"RATECTRL",			/* 21 */
210ffc03c33SBjorn Andersson 	"WOW",				/* 22 */
211ffc03c33SBjorn Andersson 	"WLAN_ROAM_SCAN_OFFLOAD",	/* 23 */
212ffc03c33SBjorn Andersson 	"SPECULATIVE_PS_POLL",		/* 24 */
213ffc03c33SBjorn Andersson 	"SCAN_SCH",			/* 25 */
214ffc03c33SBjorn Andersson 	"IBSS_HEARTBEAT_OFFLOAD",	/* 26 */
215ffc03c33SBjorn Andersson 	"WLAN_SCAN_OFFLOAD",		/* 27 */
216ffc03c33SBjorn Andersson 	"WLAN_PERIODIC_TX_PTRN",	/* 28 */
217ffc03c33SBjorn Andersson 	"ADVANCE_TDLS",			/* 29 */
218ffc03c33SBjorn Andersson 	"BATCH_SCAN",			/* 30 */
219ffc03c33SBjorn Andersson 	"FW_IN_TX_PATH",		/* 31 */
220ffc03c33SBjorn Andersson 	"EXTENDED_NSOFFLOAD_SLOT",	/* 32 */
221ffc03c33SBjorn Andersson 	"CH_SWITCH_V1",			/* 33 */
222ffc03c33SBjorn Andersson 	"HT40_OBSS_SCAN",		/* 34 */
223ffc03c33SBjorn Andersson 	"UPDATE_CHANNEL_LIST",		/* 35 */
224ffc03c33SBjorn Andersson 	"WLAN_MCADDR_FLT",		/* 36 */
225ffc03c33SBjorn Andersson 	"WLAN_CH144",			/* 37 */
226ffc03c33SBjorn Andersson 	"NAN",				/* 38 */
227ffc03c33SBjorn Andersson 	"TDLS_SCAN_COEXISTENCE",	/* 39 */
228ffc03c33SBjorn Andersson 	"LINK_LAYER_STATS_MEAS",	/* 40 */
229ffc03c33SBjorn Andersson 	"MU_MIMO",			/* 41 */
230ffc03c33SBjorn Andersson 	"EXTENDED_SCAN",		/* 42 */
231ffc03c33SBjorn Andersson 	"DYNAMIC_WMM_PS",		/* 43 */
232ffc03c33SBjorn Andersson 	"MAC_SPOOFED_SCAN",		/* 44 */
233ffc03c33SBjorn Andersson 	"BMU_ERROR_GENERIC_RECOVERY",	/* 45 */
234ffc03c33SBjorn Andersson 	"DISA",				/* 46 */
235ffc03c33SBjorn Andersson 	"FW_STATS",			/* 47 */
236ffc03c33SBjorn Andersson 	"WPS_PRBRSP_TMPL",		/* 48 */
237ffc03c33SBjorn Andersson 	"BCN_IE_FLT_DELTA",		/* 49 */
238ffc03c33SBjorn Andersson 	"TDLS_OFF_CHANNEL",		/* 51 */
239ffc03c33SBjorn Andersson 	"RTT3",				/* 52 */
240ffc03c33SBjorn Andersson 	"MGMT_FRAME_LOGGING",		/* 53 */
241ffc03c33SBjorn Andersson 	"ENHANCED_TXBD_COMPLETION",	/* 54 */
242ffc03c33SBjorn Andersson 	"LOGGING_ENHANCEMENT",		/* 55 */
243ffc03c33SBjorn Andersson 	"EXT_SCAN_ENHANCED",		/* 56 */
244ffc03c33SBjorn Andersson 	"MEMORY_DUMP_SUPPORTED",	/* 57 */
245ffc03c33SBjorn Andersson 	"PER_PKT_STATS_SUPPORTED",	/* 58 */
246ffc03c33SBjorn Andersson 	"EXT_LL_STAT",			/* 60 */
247ffc03c33SBjorn Andersson 	"WIFI_CONFIG",			/* 61 */
248ffc03c33SBjorn Andersson 	"ANTENNA_DIVERSITY_SELECTION",	/* 62 */
2492be6636aSPontus Fuchs };
2502be6636aSPontus Fuchs 
2512be6636aSPontus Fuchs static const char *wcn36xx_get_cap_name(enum place_holder_in_cap_bitmap x)
2522be6636aSPontus Fuchs {
2532be6636aSPontus Fuchs 	if (x >= ARRAY_SIZE(wcn36xx_caps_names))
2542be6636aSPontus Fuchs 		return "UNKNOWN";
2552be6636aSPontus Fuchs 	return wcn36xx_caps_names[x];
2562be6636aSPontus Fuchs }
2572be6636aSPontus Fuchs 
2582be6636aSPontus Fuchs static void wcn36xx_feat_caps_info(struct wcn36xx *wcn)
2592be6636aSPontus Fuchs {
2602be6636aSPontus Fuchs 	int i;
2612be6636aSPontus Fuchs 
2622be6636aSPontus Fuchs 	for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) {
2632be6636aSPontus Fuchs 		if (get_feat_caps(wcn->fw_feat_caps, i))
2646b8a127bSRamon Fried 			wcn36xx_dbg(WCN36XX_DBG_MAC, "FW Cap %s\n", wcn36xx_get_cap_name(i));
2652be6636aSPontus Fuchs 	}
2662be6636aSPontus Fuchs }
2672be6636aSPontus Fuchs 
2688e84c258SEugene Krasnikov static int wcn36xx_start(struct ieee80211_hw *hw)
2698e84c258SEugene Krasnikov {
2708e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
2718e84c258SEugene Krasnikov 	int ret;
2728e84c258SEugene Krasnikov 
2738e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start\n");
2748e84c258SEugene Krasnikov 
2758e84c258SEugene Krasnikov 	/* SMD initialization */
2768e84c258SEugene Krasnikov 	ret = wcn36xx_smd_open(wcn);
2778e84c258SEugene Krasnikov 	if (ret) {
2788e84c258SEugene Krasnikov 		wcn36xx_err("Failed to open smd channel: %d\n", ret);
2798e84c258SEugene Krasnikov 		goto out_err;
2808e84c258SEugene Krasnikov 	}
2818e84c258SEugene Krasnikov 
2828e84c258SEugene Krasnikov 	/* Allocate memory pools for Mgmt BD headers and Data BD headers */
2838e84c258SEugene Krasnikov 	ret = wcn36xx_dxe_allocate_mem_pools(wcn);
2848e84c258SEugene Krasnikov 	if (ret) {
2858e84c258SEugene Krasnikov 		wcn36xx_err("Failed to alloc DXE mempool: %d\n", ret);
2868e84c258SEugene Krasnikov 		goto out_smd_close;
2878e84c258SEugene Krasnikov 	}
2888e84c258SEugene Krasnikov 
2898e84c258SEugene Krasnikov 	ret = wcn36xx_dxe_alloc_ctl_blks(wcn);
2908e84c258SEugene Krasnikov 	if (ret) {
2918e84c258SEugene Krasnikov 		wcn36xx_err("Failed to alloc DXE ctl blocks: %d\n", ret);
2928e84c258SEugene Krasnikov 		goto out_free_dxe_pool;
2938e84c258SEugene Krasnikov 	}
2948e84c258SEugene Krasnikov 
2958e84c258SEugene Krasnikov 	wcn->hal_buf = kmalloc(WCN36XX_HAL_BUF_SIZE, GFP_KERNEL);
2968e84c258SEugene Krasnikov 	if (!wcn->hal_buf) {
2978e84c258SEugene Krasnikov 		wcn36xx_err("Failed to allocate smd buf\n");
2988e84c258SEugene Krasnikov 		ret = -ENOMEM;
2998e84c258SEugene Krasnikov 		goto out_free_dxe_ctl;
3008e84c258SEugene Krasnikov 	}
3018e84c258SEugene Krasnikov 
3028e84c258SEugene Krasnikov 	ret = wcn36xx_smd_load_nv(wcn);
3038e84c258SEugene Krasnikov 	if (ret) {
3048e84c258SEugene Krasnikov 		wcn36xx_err("Failed to push NV to chip\n");
3058e84c258SEugene Krasnikov 		goto out_free_smd_buf;
3068e84c258SEugene Krasnikov 	}
3078e84c258SEugene Krasnikov 
3088e84c258SEugene Krasnikov 	ret = wcn36xx_smd_start(wcn);
3098e84c258SEugene Krasnikov 	if (ret) {
3108e84c258SEugene Krasnikov 		wcn36xx_err("Failed to start chip\n");
3118e84c258SEugene Krasnikov 		goto out_free_smd_buf;
3128e84c258SEugene Krasnikov 	}
3138e84c258SEugene Krasnikov 
314f2ed5d24SPontus Fuchs 	if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
315f2ed5d24SPontus Fuchs 		ret = wcn36xx_smd_feature_caps_exchange(wcn);
316f2ed5d24SPontus Fuchs 		if (ret)
317f2ed5d24SPontus Fuchs 			wcn36xx_warn("Exchange feature caps failed\n");
318f2ed5d24SPontus Fuchs 		else
319f2ed5d24SPontus Fuchs 			wcn36xx_feat_caps_info(wcn);
320f2ed5d24SPontus Fuchs 	}
321f2ed5d24SPontus Fuchs 
3228e84c258SEugene Krasnikov 	/* DMA channel initialization */
3238e84c258SEugene Krasnikov 	ret = wcn36xx_dxe_init(wcn);
3248e84c258SEugene Krasnikov 	if (ret) {
3258e84c258SEugene Krasnikov 		wcn36xx_err("DXE init failed\n");
3268e84c258SEugene Krasnikov 		goto out_smd_stop;
3278e84c258SEugene Krasnikov 	}
3288e84c258SEugene Krasnikov 
3298e84c258SEugene Krasnikov 	wcn36xx_debugfs_init(wcn);
3308e84c258SEugene Krasnikov 
3318e84c258SEugene Krasnikov 	INIT_LIST_HEAD(&wcn->vif_list);
3324b12462aSBob Copeland 	spin_lock_init(&wcn->dxe_lock);
3334b12462aSBob Copeland 
3348e84c258SEugene Krasnikov 	return 0;
3358e84c258SEugene Krasnikov 
3368e84c258SEugene Krasnikov out_smd_stop:
3378e84c258SEugene Krasnikov 	wcn36xx_smd_stop(wcn);
3388e84c258SEugene Krasnikov out_free_smd_buf:
3398e84c258SEugene Krasnikov 	kfree(wcn->hal_buf);
3408e84c258SEugene Krasnikov out_free_dxe_ctl:
3418e84c258SEugene Krasnikov 	wcn36xx_dxe_free_ctl_blks(wcn);
3424aa2d31fSChristophe Jaillet out_free_dxe_pool:
3434aa2d31fSChristophe Jaillet 	wcn36xx_dxe_free_mem_pools(wcn);
3448e84c258SEugene Krasnikov out_smd_close:
3458e84c258SEugene Krasnikov 	wcn36xx_smd_close(wcn);
3468e84c258SEugene Krasnikov out_err:
3478e84c258SEugene Krasnikov 	return ret;
3488e84c258SEugene Krasnikov }
3498e84c258SEugene Krasnikov 
3508e84c258SEugene Krasnikov static void wcn36xx_stop(struct ieee80211_hw *hw)
3518e84c258SEugene Krasnikov {
3528e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
3538e84c258SEugene Krasnikov 
3548e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n");
3558e84c258SEugene Krasnikov 
35680c764d3SDaniel Mack 	cancel_work_sync(&wcn->scan_work);
35780c764d3SDaniel Mack 
35880c764d3SDaniel Mack 	mutex_lock(&wcn->scan_lock);
35980c764d3SDaniel Mack 	if (wcn->scan_req) {
36080c764d3SDaniel Mack 		struct cfg80211_scan_info scan_info = {
36180c764d3SDaniel Mack 			.aborted = true,
36280c764d3SDaniel Mack 		};
36380c764d3SDaniel Mack 
36480c764d3SDaniel Mack 		ieee80211_scan_completed(wcn->hw, &scan_info);
36580c764d3SDaniel Mack 	}
36680c764d3SDaniel Mack 	wcn->scan_req = NULL;
36780c764d3SDaniel Mack 	mutex_unlock(&wcn->scan_lock);
36880c764d3SDaniel Mack 
3698e84c258SEugene Krasnikov 	wcn36xx_debugfs_exit(wcn);
3708e84c258SEugene Krasnikov 	wcn36xx_smd_stop(wcn);
3718e84c258SEugene Krasnikov 	wcn36xx_dxe_deinit(wcn);
3728e84c258SEugene Krasnikov 	wcn36xx_smd_close(wcn);
3738e84c258SEugene Krasnikov 
3748e84c258SEugene Krasnikov 	wcn36xx_dxe_free_mem_pools(wcn);
3758e84c258SEugene Krasnikov 	wcn36xx_dxe_free_ctl_blks(wcn);
3768e84c258SEugene Krasnikov 
3778e84c258SEugene Krasnikov 	kfree(wcn->hal_buf);
3788e84c258SEugene Krasnikov }
3798e84c258SEugene Krasnikov 
3808e84c258SEugene Krasnikov static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed)
3818e84c258SEugene Krasnikov {
3828e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
3838e84c258SEugene Krasnikov 	struct ieee80211_vif *vif = NULL;
3848e84c258SEugene Krasnikov 	struct wcn36xx_vif *tmp;
3858e84c258SEugene Krasnikov 
3868e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed);
3878e84c258SEugene Krasnikov 
38839efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
38939efc7ccSBjorn Andersson 
3908e84c258SEugene Krasnikov 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
3918e84c258SEugene Krasnikov 		int ch = WCN36XX_HW_CHANNEL(wcn);
3928e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n",
3938e84c258SEugene Krasnikov 			    ch);
3948e84c258SEugene Krasnikov 		list_for_each_entry(tmp, &wcn->vif_list, list) {
395ce75877fSPontus Fuchs 			vif = wcn36xx_priv_to_vif(tmp);
3968e84c258SEugene Krasnikov 			wcn36xx_smd_switch_channel(wcn, vif, ch);
3978e84c258SEugene Krasnikov 		}
3988e84c258SEugene Krasnikov 	}
3998e84c258SEugene Krasnikov 
4000856655aSLoic Poulain 	if (changed & IEEE80211_CONF_CHANGE_PS) {
4010856655aSLoic Poulain 		list_for_each_entry(tmp, &wcn->vif_list, list) {
4020856655aSLoic Poulain 			vif = wcn36xx_priv_to_vif(tmp);
4030856655aSLoic Poulain 			if (hw->conf.flags & IEEE80211_CONF_PS) {
4040856655aSLoic Poulain 				if (vif->bss_conf.ps) /* ps allowed ? */
4050856655aSLoic Poulain 					wcn36xx_pmc_enter_bmps_state(wcn, vif);
4060856655aSLoic Poulain 			} else {
4070856655aSLoic Poulain 				wcn36xx_pmc_exit_bmps_state(wcn, vif);
4080856655aSLoic Poulain 			}
4090856655aSLoic Poulain 		}
4100856655aSLoic Poulain 	}
4110856655aSLoic Poulain 
41239efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
41339efc7ccSBjorn Andersson 
4148e84c258SEugene Krasnikov 	return 0;
4158e84c258SEugene Krasnikov }
4168e84c258SEugene Krasnikov 
4178e84c258SEugene Krasnikov static void wcn36xx_configure_filter(struct ieee80211_hw *hw,
4188e84c258SEugene Krasnikov 				     unsigned int changed,
4198e84c258SEugene Krasnikov 				     unsigned int *total, u64 multicast)
4208e84c258SEugene Krasnikov {
42120a779edSPontus Fuchs 	struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp;
42220a779edSPontus Fuchs 	struct wcn36xx *wcn = hw->priv;
42320a779edSPontus Fuchs 	struct wcn36xx_vif *tmp;
42420a779edSPontus Fuchs 	struct ieee80211_vif *vif = NULL;
42520a779edSPontus Fuchs 
4268e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n");
4278e84c258SEugene Krasnikov 
42839efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
42939efc7ccSBjorn Andersson 
43020a779edSPontus Fuchs 	*total &= FIF_ALLMULTI;
43120a779edSPontus Fuchs 
43220a779edSPontus Fuchs 	fp = (void *)(unsigned long)multicast;
43320a779edSPontus Fuchs 	list_for_each_entry(tmp, &wcn->vif_list, list) {
43420a779edSPontus Fuchs 		vif = wcn36xx_priv_to_vif(tmp);
43520a779edSPontus Fuchs 
43620a779edSPontus Fuchs 		/* FW handles MC filtering only when connected as STA */
43720a779edSPontus Fuchs 		if (*total & FIF_ALLMULTI)
43820a779edSPontus Fuchs 			wcn36xx_smd_set_mc_list(wcn, vif, NULL);
43920a779edSPontus Fuchs 		else if (NL80211_IFTYPE_STATION == vif->type && tmp->sta_assoc)
44020a779edSPontus Fuchs 			wcn36xx_smd_set_mc_list(wcn, vif, fp);
44120a779edSPontus Fuchs 	}
44239efc7ccSBjorn Andersson 
44339efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
44420a779edSPontus Fuchs 	kfree(fp);
44520a779edSPontus Fuchs }
44620a779edSPontus Fuchs 
44720a779edSPontus Fuchs static u64 wcn36xx_prepare_multicast(struct ieee80211_hw *hw,
44820a779edSPontus Fuchs 				     struct netdev_hw_addr_list *mc_list)
44920a779edSPontus Fuchs {
45020a779edSPontus Fuchs 	struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp;
45120a779edSPontus Fuchs 	struct netdev_hw_addr *ha;
45220a779edSPontus Fuchs 
45320a779edSPontus Fuchs 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac prepare multicast list\n");
45420a779edSPontus Fuchs 	fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
45520a779edSPontus Fuchs 	if (!fp) {
45620a779edSPontus Fuchs 		wcn36xx_err("Out of memory setting filters.\n");
45720a779edSPontus Fuchs 		return 0;
45820a779edSPontus Fuchs 	}
45920a779edSPontus Fuchs 
46020a779edSPontus Fuchs 	fp->mc_addr_count = 0;
46120a779edSPontus Fuchs 	/* update multicast filtering parameters */
46220a779edSPontus Fuchs 	if (netdev_hw_addr_list_count(mc_list) <=
46320a779edSPontus Fuchs 	    WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS) {
46420a779edSPontus Fuchs 		netdev_hw_addr_list_for_each(ha, mc_list) {
46520a779edSPontus Fuchs 			memcpy(fp->mc_addr[fp->mc_addr_count],
46620a779edSPontus Fuchs 					ha->addr, ETH_ALEN);
46720a779edSPontus Fuchs 			fp->mc_addr_count++;
46820a779edSPontus Fuchs 		}
46920a779edSPontus Fuchs 	}
47020a779edSPontus Fuchs 
47120a779edSPontus Fuchs 	return (u64)(unsigned long)fp;
4728e84c258SEugene Krasnikov }
4738e84c258SEugene Krasnikov 
4748e84c258SEugene Krasnikov static void wcn36xx_tx(struct ieee80211_hw *hw,
4758e84c258SEugene Krasnikov 		       struct ieee80211_tx_control *control,
4768e84c258SEugene Krasnikov 		       struct sk_buff *skb)
4778e84c258SEugene Krasnikov {
4788e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
4798e84c258SEugene Krasnikov 	struct wcn36xx_sta *sta_priv = NULL;
4808e84c258SEugene Krasnikov 
4818e84c258SEugene Krasnikov 	if (control->sta)
482a92e4696SPontus Fuchs 		sta_priv = wcn36xx_sta_to_priv(control->sta);
4838e84c258SEugene Krasnikov 
4848e84c258SEugene Krasnikov 	if (wcn36xx_start_tx(wcn, sta_priv, skb))
4858e84c258SEugene Krasnikov 		ieee80211_free_txskb(wcn->hw, skb);
4868e84c258SEugene Krasnikov }
4878e84c258SEugene Krasnikov 
4888e84c258SEugene Krasnikov static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
4898e84c258SEugene Krasnikov 			   struct ieee80211_vif *vif,
4908e84c258SEugene Krasnikov 			   struct ieee80211_sta *sta,
4918e84c258SEugene Krasnikov 			   struct ieee80211_key_conf *key_conf)
4928e84c258SEugene Krasnikov {
4938e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
494ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
49581c69263SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
4968e84c258SEugene Krasnikov 	int ret = 0;
4978e84c258SEugene Krasnikov 	u8 key[WLAN_MAX_KEY_LEN];
4988e84c258SEugene Krasnikov 
4998e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key\n");
5008e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x\n",
5018e84c258SEugene Krasnikov 		    cmd, key_conf->cipher, key_conf->keyidx,
5028e84c258SEugene Krasnikov 		    key_conf->keylen, key_conf->flags);
5038e84c258SEugene Krasnikov 	wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: ",
5048e84c258SEugene Krasnikov 			 key_conf->key,
5058e84c258SEugene Krasnikov 			 key_conf->keylen);
5068e84c258SEugene Krasnikov 
50739efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
50839efc7ccSBjorn Andersson 
5098e84c258SEugene Krasnikov 	switch (key_conf->cipher) {
5108e84c258SEugene Krasnikov 	case WLAN_CIPHER_SUITE_WEP40:
5118e84c258SEugene Krasnikov 		vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40;
5128e84c258SEugene Krasnikov 		break;
5138e84c258SEugene Krasnikov 	case WLAN_CIPHER_SUITE_WEP104:
5148e84c258SEugene Krasnikov 		vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40;
5158e84c258SEugene Krasnikov 		break;
5168e84c258SEugene Krasnikov 	case WLAN_CIPHER_SUITE_CCMP:
5178e84c258SEugene Krasnikov 		vif_priv->encrypt_type = WCN36XX_HAL_ED_CCMP;
5188e84c258SEugene Krasnikov 		break;
5198e84c258SEugene Krasnikov 	case WLAN_CIPHER_SUITE_TKIP:
5208e84c258SEugene Krasnikov 		vif_priv->encrypt_type = WCN36XX_HAL_ED_TKIP;
5218e84c258SEugene Krasnikov 		break;
5228e84c258SEugene Krasnikov 	default:
5238e84c258SEugene Krasnikov 		wcn36xx_err("Unsupported key type 0x%x\n",
5248e84c258SEugene Krasnikov 			      key_conf->cipher);
5258e84c258SEugene Krasnikov 		ret = -EOPNOTSUPP;
5268e84c258SEugene Krasnikov 		goto out;
5278e84c258SEugene Krasnikov 	}
5288e84c258SEugene Krasnikov 
5298e84c258SEugene Krasnikov 	switch (cmd) {
5308e84c258SEugene Krasnikov 	case SET_KEY:
5318e84c258SEugene Krasnikov 		if (WCN36XX_HAL_ED_TKIP == vif_priv->encrypt_type) {
5328e84c258SEugene Krasnikov 			/*
5338e84c258SEugene Krasnikov 			 * Supplicant is sending key in the wrong order:
5348e84c258SEugene Krasnikov 			 * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b)
5358e84c258SEugene Krasnikov 			 * but HW expects it to be in the order as described in
5368e84c258SEugene Krasnikov 			 * IEEE 802.11 spec (see chapter 11.7) like this:
5378e84c258SEugene Krasnikov 			 * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b)
5388e84c258SEugene Krasnikov 			 */
5398e84c258SEugene Krasnikov 			memcpy(key, key_conf->key, 16);
5408e84c258SEugene Krasnikov 			memcpy(key + 16, key_conf->key + 24, 8);
5418e84c258SEugene Krasnikov 			memcpy(key + 24, key_conf->key + 16, 8);
5428e84c258SEugene Krasnikov 		} else {
5438e84c258SEugene Krasnikov 			memcpy(key, key_conf->key, key_conf->keylen);
5448e84c258SEugene Krasnikov 		}
5458e84c258SEugene Krasnikov 
5468e84c258SEugene Krasnikov 		if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) {
5478e84c258SEugene Krasnikov 			sta_priv->is_data_encrypted = true;
5488e84c258SEugene Krasnikov 			/* Reconfigure bss with encrypt_type */
5498e84c258SEugene Krasnikov 			if (NL80211_IFTYPE_STATION == vif->type)
5508e84c258SEugene Krasnikov 				wcn36xx_smd_config_bss(wcn,
5518e84c258SEugene Krasnikov 						       vif,
5528e84c258SEugene Krasnikov 						       sta,
5538e84c258SEugene Krasnikov 						       sta->addr,
5548e84c258SEugene Krasnikov 						       true);
5558e84c258SEugene Krasnikov 
5568e84c258SEugene Krasnikov 			wcn36xx_smd_set_stakey(wcn,
5578e84c258SEugene Krasnikov 				vif_priv->encrypt_type,
5588e84c258SEugene Krasnikov 				key_conf->keyidx,
5598e84c258SEugene Krasnikov 				key_conf->keylen,
5608e84c258SEugene Krasnikov 				key,
5618e84c258SEugene Krasnikov 				get_sta_index(vif, sta_priv));
5628e84c258SEugene Krasnikov 		} else {
5638e84c258SEugene Krasnikov 			wcn36xx_smd_set_bsskey(wcn,
5648e84c258SEugene Krasnikov 				vif_priv->encrypt_type,
5650fc8bb50SDaniel Mack 				vif_priv->bss_index,
5668e84c258SEugene Krasnikov 				key_conf->keyidx,
5678e84c258SEugene Krasnikov 				key_conf->keylen,
5688e84c258SEugene Krasnikov 				key);
5698e84c258SEugene Krasnikov 			if ((WLAN_CIPHER_SUITE_WEP40 == key_conf->cipher) ||
5708e84c258SEugene Krasnikov 			    (WLAN_CIPHER_SUITE_WEP104 == key_conf->cipher)) {
5718e84c258SEugene Krasnikov 				sta_priv->is_data_encrypted = true;
5728e84c258SEugene Krasnikov 				wcn36xx_smd_set_stakey(wcn,
5738e84c258SEugene Krasnikov 					vif_priv->encrypt_type,
5748e84c258SEugene Krasnikov 					key_conf->keyidx,
5758e84c258SEugene Krasnikov 					key_conf->keylen,
5768e84c258SEugene Krasnikov 					key,
5778e84c258SEugene Krasnikov 					get_sta_index(vif, sta_priv));
5788e84c258SEugene Krasnikov 			}
5798e84c258SEugene Krasnikov 		}
5808e84c258SEugene Krasnikov 		break;
5818e84c258SEugene Krasnikov 	case DISABLE_KEY:
5828e84c258SEugene Krasnikov 		if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) {
5830fc8bb50SDaniel Mack 			if (vif_priv->bss_index != WCN36XX_HAL_BSS_INVALID_IDX)
5848e84c258SEugene Krasnikov 				wcn36xx_smd_remove_bsskey(wcn,
5858e84c258SEugene Krasnikov 					vif_priv->encrypt_type,
5860fc8bb50SDaniel Mack 					vif_priv->bss_index,
5878e84c258SEugene Krasnikov 					key_conf->keyidx);
5880fc8bb50SDaniel Mack 
5890fc8bb50SDaniel Mack 			vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
5908e84c258SEugene Krasnikov 		} else {
5918e84c258SEugene Krasnikov 			sta_priv->is_data_encrypted = false;
5928e84c258SEugene Krasnikov 			/* do not remove key if disassociated */
5938e84c258SEugene Krasnikov 			if (sta_priv->aid)
5948e84c258SEugene Krasnikov 				wcn36xx_smd_remove_stakey(wcn,
5958e84c258SEugene Krasnikov 					vif_priv->encrypt_type,
5968e84c258SEugene Krasnikov 					key_conf->keyidx,
5978e84c258SEugene Krasnikov 					get_sta_index(vif, sta_priv));
5988e84c258SEugene Krasnikov 		}
5998e84c258SEugene Krasnikov 		break;
6008e84c258SEugene Krasnikov 	default:
6018e84c258SEugene Krasnikov 		wcn36xx_err("Unsupported key cmd 0x%x\n", cmd);
6028e84c258SEugene Krasnikov 		ret = -EOPNOTSUPP;
6038e84c258SEugene Krasnikov 		goto out;
6048e84c258SEugene Krasnikov 	}
6058e84c258SEugene Krasnikov 
6068e84c258SEugene Krasnikov out:
60739efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
60839efc7ccSBjorn Andersson 
6098e84c258SEugene Krasnikov 	return ret;
6108e84c258SEugene Krasnikov }
6118e84c258SEugene Krasnikov 
61288603903SBjorn Andersson static void wcn36xx_hw_scan_worker(struct work_struct *work)
6138e84c258SEugene Krasnikov {
61488603903SBjorn Andersson 	struct wcn36xx *wcn = container_of(work, struct wcn36xx, scan_work);
61588603903SBjorn Andersson 	struct cfg80211_scan_request *req = wcn->scan_req;
61688603903SBjorn Andersson 	u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX];
61788603903SBjorn Andersson 	struct cfg80211_scan_info scan_info = {};
61803c95dbeSBjorn Andersson 	bool aborted = false;
61988603903SBjorn Andersson 	int i;
62088603903SBjorn Andersson 
62188603903SBjorn Andersson 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 scan %d channels worker\n", req->n_channels);
62288603903SBjorn Andersson 
62388603903SBjorn Andersson 	for (i = 0; i < req->n_channels; i++)
62488603903SBjorn Andersson 		channels[i] = req->channels[i]->hw_value;
62588603903SBjorn Andersson 
62688603903SBjorn Andersson 	wcn36xx_smd_update_scan_params(wcn, channels, req->n_channels);
6278e84c258SEugene Krasnikov 
6288e84c258SEugene Krasnikov 	wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN);
62988603903SBjorn Andersson 	for (i = 0; i < req->n_channels; i++) {
63003c95dbeSBjorn Andersson 		mutex_lock(&wcn->scan_lock);
63103c95dbeSBjorn Andersson 		aborted = wcn->scan_aborted;
63203c95dbeSBjorn Andersson 		mutex_unlock(&wcn->scan_lock);
63303c95dbeSBjorn Andersson 
63403c95dbeSBjorn Andersson 		if (aborted)
63503c95dbeSBjorn Andersson 			break;
63603c95dbeSBjorn Andersson 
63788603903SBjorn Andersson 		wcn->scan_freq = req->channels[i]->center_freq;
63888603903SBjorn Andersson 		wcn->scan_band = req->channels[i]->band;
63988603903SBjorn Andersson 
64088603903SBjorn Andersson 		wcn36xx_smd_start_scan(wcn, req->channels[i]->hw_value);
64188603903SBjorn Andersson 		msleep(30);
64288603903SBjorn Andersson 		wcn36xx_smd_end_scan(wcn, req->channels[i]->hw_value);
64388603903SBjorn Andersson 
64488603903SBjorn Andersson 		wcn->scan_freq = 0;
64588603903SBjorn Andersson 	}
64688603903SBjorn Andersson 	wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN);
64788603903SBjorn Andersson 
64803c95dbeSBjorn Andersson 	scan_info.aborted = aborted;
64988603903SBjorn Andersson 	ieee80211_scan_completed(wcn->hw, &scan_info);
65088603903SBjorn Andersson 
65188603903SBjorn Andersson 	mutex_lock(&wcn->scan_lock);
65288603903SBjorn Andersson 	wcn->scan_req = NULL;
65388603903SBjorn Andersson 	mutex_unlock(&wcn->scan_lock);
6548e84c258SEugene Krasnikov }
6558e84c258SEugene Krasnikov 
65688603903SBjorn Andersson static int wcn36xx_hw_scan(struct ieee80211_hw *hw,
65788603903SBjorn Andersson 			   struct ieee80211_vif *vif,
65888603903SBjorn Andersson 			   struct ieee80211_scan_request *hw_req)
6598e84c258SEugene Krasnikov {
6608e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
66188603903SBjorn Andersson 	mutex_lock(&wcn->scan_lock);
66288603903SBjorn Andersson 	if (wcn->scan_req) {
66388603903SBjorn Andersson 		mutex_unlock(&wcn->scan_lock);
66488603903SBjorn Andersson 		return -EBUSY;
66588603903SBjorn Andersson 	}
66603c95dbeSBjorn Andersson 
66703c95dbeSBjorn Andersson 	wcn->scan_aborted = false;
66888603903SBjorn Andersson 	wcn->scan_req = &hw_req->req;
6692f3bef4bSLoic Poulain 
67088603903SBjorn Andersson 	mutex_unlock(&wcn->scan_lock);
67188603903SBjorn Andersson 
6722f3bef4bSLoic Poulain 	if (!get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) {
6732f3bef4bSLoic Poulain 		/* legacy manual/sw scan */
67488603903SBjorn Andersson 		schedule_work(&wcn->scan_work);
67588603903SBjorn Andersson 		return 0;
6768e84c258SEugene Krasnikov 	}
6778e84c258SEugene Krasnikov 
6782f3bef4bSLoic Poulain 	return wcn36xx_smd_start_hw_scan(wcn, vif, &hw_req->req);
6792f3bef4bSLoic Poulain }
6802f3bef4bSLoic Poulain 
68103c95dbeSBjorn Andersson static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw,
68203c95dbeSBjorn Andersson 				   struct ieee80211_vif *vif)
68303c95dbeSBjorn Andersson {
68403c95dbeSBjorn Andersson 	struct wcn36xx *wcn = hw->priv;
68503c95dbeSBjorn Andersson 
68603c95dbeSBjorn Andersson 	mutex_lock(&wcn->scan_lock);
68703c95dbeSBjorn Andersson 	wcn->scan_aborted = true;
68803c95dbeSBjorn Andersson 	mutex_unlock(&wcn->scan_lock);
68903c95dbeSBjorn Andersson 
69089722f57SDaniel Mack 	if (get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) {
69189722f57SDaniel Mack 		/* ieee80211_scan_completed will be called on FW scan
69289722f57SDaniel Mack 		 * indication */
6939bfd05e3SLoic Poulain 		wcn36xx_smd_stop_hw_scan(wcn);
69489722f57SDaniel Mack 	} else {
69589722f57SDaniel Mack 		struct cfg80211_scan_info scan_info = {
69689722f57SDaniel Mack 			.aborted = true,
69789722f57SDaniel Mack 		};
6989bfd05e3SLoic Poulain 
69903c95dbeSBjorn Andersson 		cancel_work_sync(&wcn->scan_work);
70089722f57SDaniel Mack 		ieee80211_scan_completed(wcn->hw, &scan_info);
70189722f57SDaniel Mack 	}
70203c95dbeSBjorn Andersson }
70303c95dbeSBjorn Andersson 
7048e84c258SEugene Krasnikov static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta,
70557fbcce3SJohannes Berg 					 enum nl80211_band band)
7068e84c258SEugene Krasnikov {
7078e84c258SEugene Krasnikov 	int i, size;
7088e84c258SEugene Krasnikov 	u16 *rates_table;
709a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
7108e84c258SEugene Krasnikov 	u32 rates = sta->supp_rates[band];
7118e84c258SEugene Krasnikov 
7128e84c258SEugene Krasnikov 	memset(&sta_priv->supported_rates, 0,
7138e84c258SEugene Krasnikov 		sizeof(sta_priv->supported_rates));
7148e84c258SEugene Krasnikov 	sta_priv->supported_rates.op_rate_mode = STA_11n;
7158e84c258SEugene Krasnikov 
7168e84c258SEugene Krasnikov 	size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates);
7178e84c258SEugene Krasnikov 	rates_table = sta_priv->supported_rates.dsss_rates;
71857fbcce3SJohannes Berg 	if (band == NL80211_BAND_2GHZ) {
7198e84c258SEugene Krasnikov 		for (i = 0; i < size; i++) {
7208e84c258SEugene Krasnikov 			if (rates & 0x01) {
7218e84c258SEugene Krasnikov 				rates_table[i] = wcn_2ghz_rates[i].hw_value;
7228e84c258SEugene Krasnikov 				rates = rates >> 1;
7238e84c258SEugene Krasnikov 			}
7248e84c258SEugene Krasnikov 		}
7258e84c258SEugene Krasnikov 	}
7268e84c258SEugene Krasnikov 
7278e84c258SEugene Krasnikov 	size = ARRAY_SIZE(sta_priv->supported_rates.ofdm_rates);
7288e84c258SEugene Krasnikov 	rates_table = sta_priv->supported_rates.ofdm_rates;
7298e84c258SEugene Krasnikov 	for (i = 0; i < size; i++) {
7308e84c258SEugene Krasnikov 		if (rates & 0x01) {
7318e84c258SEugene Krasnikov 			rates_table[i] = wcn_5ghz_rates[i].hw_value;
7328e84c258SEugene Krasnikov 			rates = rates >> 1;
7338e84c258SEugene Krasnikov 		}
7348e84c258SEugene Krasnikov 	}
7358e84c258SEugene Krasnikov 
7368e84c258SEugene Krasnikov 	if (sta->ht_cap.ht_supported) {
7378e84c258SEugene Krasnikov 		BUILD_BUG_ON(sizeof(sta->ht_cap.mcs.rx_mask) >
7388e84c258SEugene Krasnikov 			sizeof(sta_priv->supported_rates.supported_mcs_set));
7398e84c258SEugene Krasnikov 		memcpy(sta_priv->supported_rates.supported_mcs_set,
7408e84c258SEugene Krasnikov 		       sta->ht_cap.mcs.rx_mask,
7418e84c258SEugene Krasnikov 		       sizeof(sta->ht_cap.mcs.rx_mask));
7428e84c258SEugene Krasnikov 	}
7438e84c258SEugene Krasnikov }
7448e84c258SEugene Krasnikov void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates)
7458e84c258SEugene Krasnikov {
7468e84c258SEugene Krasnikov 	u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = {
7478e84c258SEugene Krasnikov 		HW_RATE_INDEX_6MBPS,
7488e84c258SEugene Krasnikov 		HW_RATE_INDEX_9MBPS,
7498e84c258SEugene Krasnikov 		HW_RATE_INDEX_12MBPS,
7508e84c258SEugene Krasnikov 		HW_RATE_INDEX_18MBPS,
7518e84c258SEugene Krasnikov 		HW_RATE_INDEX_24MBPS,
7528e84c258SEugene Krasnikov 		HW_RATE_INDEX_36MBPS,
7538e84c258SEugene Krasnikov 		HW_RATE_INDEX_48MBPS,
7548e84c258SEugene Krasnikov 		HW_RATE_INDEX_54MBPS
7558e84c258SEugene Krasnikov 	};
7568e84c258SEugene Krasnikov 	u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = {
7578e84c258SEugene Krasnikov 		HW_RATE_INDEX_1MBPS,
7588e84c258SEugene Krasnikov 		HW_RATE_INDEX_2MBPS,
7598e84c258SEugene Krasnikov 		HW_RATE_INDEX_5_5MBPS,
7608e84c258SEugene Krasnikov 		HW_RATE_INDEX_11MBPS
7618e84c258SEugene Krasnikov 	};
7628e84c258SEugene Krasnikov 
7638e84c258SEugene Krasnikov 	rates->op_rate_mode = STA_11n;
7648e84c258SEugene Krasnikov 	memcpy(rates->dsss_rates, dsss_rates,
7658e84c258SEugene Krasnikov 		sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES);
7668e84c258SEugene Krasnikov 	memcpy(rates->ofdm_rates, ofdm_rates,
7678e84c258SEugene Krasnikov 		sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES);
7688e84c258SEugene Krasnikov 	rates->supported_mcs_set[0] = 0xFF;
7698e84c258SEugene Krasnikov }
7708e84c258SEugene Krasnikov static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
7718e84c258SEugene Krasnikov 				     struct ieee80211_vif *vif,
7728e84c258SEugene Krasnikov 				     struct ieee80211_bss_conf *bss_conf,
7738e84c258SEugene Krasnikov 				     u32 changed)
7748e84c258SEugene Krasnikov {
7758e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
7768e84c258SEugene Krasnikov 	struct sk_buff *skb = NULL;
7778e84c258SEugene Krasnikov 	u16 tim_off, tim_len;
7788e84c258SEugene Krasnikov 	enum wcn36xx_hal_link_state link_state;
779ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
7808e84c258SEugene Krasnikov 
7818e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n",
7828e84c258SEugene Krasnikov 		    vif, changed);
7838e84c258SEugene Krasnikov 
78439efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
78539efc7ccSBjorn Andersson 
7868e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_BEACON_INFO) {
7878e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC,
7888e84c258SEugene Krasnikov 			    "mac bss changed dtim period %d\n",
7898e84c258SEugene Krasnikov 			    bss_conf->dtim_period);
7908e84c258SEugene Krasnikov 
7918e84c258SEugene Krasnikov 		vif_priv->dtim_period = bss_conf->dtim_period;
7928e84c258SEugene Krasnikov 	}
7938e84c258SEugene Krasnikov 
7948e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_BSSID) {
7958e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n",
7968e84c258SEugene Krasnikov 			    bss_conf->bssid);
7978e84c258SEugene Krasnikov 
7988e84c258SEugene Krasnikov 		if (!is_zero_ether_addr(bss_conf->bssid)) {
7998e84c258SEugene Krasnikov 			vif_priv->is_joining = true;
80090023c03SPontus Fuchs 			vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
8018e84c258SEugene Krasnikov 			wcn36xx_smd_join(wcn, bss_conf->bssid,
8028e84c258SEugene Krasnikov 					 vif->addr, WCN36XX_HW_CHANNEL(wcn));
8038e84c258SEugene Krasnikov 			wcn36xx_smd_config_bss(wcn, vif, NULL,
8048e84c258SEugene Krasnikov 					       bss_conf->bssid, false);
8058e84c258SEugene Krasnikov 		} else {
8068e84c258SEugene Krasnikov 			vif_priv->is_joining = false;
8078e84c258SEugene Krasnikov 			wcn36xx_smd_delete_bss(wcn, vif);
8082716a8acSPontus Fuchs 			vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
8098e84c258SEugene Krasnikov 		}
8108e84c258SEugene Krasnikov 	}
8118e84c258SEugene Krasnikov 
8128e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_SSID) {
8138e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC,
8148e84c258SEugene Krasnikov 			    "mac bss changed ssid\n");
8158e84c258SEugene Krasnikov 		wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid ",
8168e84c258SEugene Krasnikov 				 bss_conf->ssid, bss_conf->ssid_len);
8178e84c258SEugene Krasnikov 
8188e84c258SEugene Krasnikov 		vif_priv->ssid.length = bss_conf->ssid_len;
8198e84c258SEugene Krasnikov 		memcpy(&vif_priv->ssid.ssid,
8208e84c258SEugene Krasnikov 		       bss_conf->ssid,
8218e84c258SEugene Krasnikov 		       bss_conf->ssid_len);
8228e84c258SEugene Krasnikov 	}
8238e84c258SEugene Krasnikov 
8248e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_ASSOC) {
8258e84c258SEugene Krasnikov 		vif_priv->is_joining = false;
8268e84c258SEugene Krasnikov 		if (bss_conf->assoc) {
8278e84c258SEugene Krasnikov 			struct ieee80211_sta *sta;
8288e84c258SEugene Krasnikov 			struct wcn36xx_sta *sta_priv;
8298e84c258SEugene Krasnikov 
8308e84c258SEugene Krasnikov 			wcn36xx_dbg(WCN36XX_DBG_MAC,
8318e84c258SEugene Krasnikov 				    "mac assoc bss %pM vif %pM AID=%d\n",
8328e84c258SEugene Krasnikov 				     bss_conf->bssid,
8338e84c258SEugene Krasnikov 				     vif->addr,
8348e84c258SEugene Krasnikov 				     bss_conf->aid);
8358e84c258SEugene Krasnikov 
836043ce546SPontus Fuchs 			vif_priv->sta_assoc = true;
83739efc7ccSBjorn Andersson 
83839efc7ccSBjorn Andersson 			/*
83939efc7ccSBjorn Andersson 			 * Holding conf_mutex ensures mutal exclusion with
84039efc7ccSBjorn Andersson 			 * wcn36xx_sta_remove() and as such ensures that sta
84139efc7ccSBjorn Andersson 			 * won't be freed while we're operating on it. As such
84239efc7ccSBjorn Andersson 			 * we do not need to hold the rcu_read_lock().
84339efc7ccSBjorn Andersson 			 */
8448e84c258SEugene Krasnikov 			sta = ieee80211_find_sta(vif, bss_conf->bssid);
8458e84c258SEugene Krasnikov 			if (!sta) {
8468e84c258SEugene Krasnikov 				wcn36xx_err("sta %pM is not found\n",
8478e84c258SEugene Krasnikov 					      bss_conf->bssid);
8488e84c258SEugene Krasnikov 				goto out;
8498e84c258SEugene Krasnikov 			}
850a92e4696SPontus Fuchs 			sta_priv = wcn36xx_sta_to_priv(sta);
8518e84c258SEugene Krasnikov 
8528e84c258SEugene Krasnikov 			wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
8538e84c258SEugene Krasnikov 
8548e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn, bss_conf->bssid,
8558e84c258SEugene Krasnikov 				vif->addr,
8568e84c258SEugene Krasnikov 				WCN36XX_HAL_LINK_POSTASSOC_STATE);
8578e84c258SEugene Krasnikov 			wcn36xx_smd_config_bss(wcn, vif, sta,
8588e84c258SEugene Krasnikov 					       bss_conf->bssid,
8598e84c258SEugene Krasnikov 					       true);
8608e84c258SEugene Krasnikov 			sta_priv->aid = bss_conf->aid;
8618e84c258SEugene Krasnikov 			/*
8628e84c258SEugene Krasnikov 			 * config_sta must be called from  because this is the
8638e84c258SEugene Krasnikov 			 * place where AID is available.
8648e84c258SEugene Krasnikov 			 */
8658e84c258SEugene Krasnikov 			wcn36xx_smd_config_sta(wcn, vif, sta);
8668e84c258SEugene Krasnikov 		} else {
8678e84c258SEugene Krasnikov 			wcn36xx_dbg(WCN36XX_DBG_MAC,
8688e84c258SEugene Krasnikov 				    "disassociated bss %pM vif %pM AID=%d\n",
8698e84c258SEugene Krasnikov 				    bss_conf->bssid,
8708e84c258SEugene Krasnikov 				    vif->addr,
8718e84c258SEugene Krasnikov 				    bss_conf->aid);
872043ce546SPontus Fuchs 			vif_priv->sta_assoc = false;
8738e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn,
8748e84c258SEugene Krasnikov 						bss_conf->bssid,
8758e84c258SEugene Krasnikov 						vif->addr,
8768e84c258SEugene Krasnikov 						WCN36XX_HAL_LINK_IDLE_STATE);
8778e84c258SEugene Krasnikov 		}
8788e84c258SEugene Krasnikov 	}
8798e84c258SEugene Krasnikov 
8808e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_AP_PROBE_RESP) {
8818e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n");
8828e84c258SEugene Krasnikov 		skb = ieee80211_proberesp_get(hw, vif);
8838e84c258SEugene Krasnikov 		if (!skb) {
8848e84c258SEugene Krasnikov 			wcn36xx_err("failed to alloc probereq skb\n");
8858e84c258SEugene Krasnikov 			goto out;
8868e84c258SEugene Krasnikov 		}
8878e84c258SEugene Krasnikov 
8888e84c258SEugene Krasnikov 		wcn36xx_smd_update_proberesp_tmpl(wcn, vif, skb);
8898e84c258SEugene Krasnikov 		dev_kfree_skb(skb);
8908e84c258SEugene Krasnikov 	}
8918e84c258SEugene Krasnikov 
892b3e3f871SChun-Yeow Yeoh 	if (changed & BSS_CHANGED_BEACON_ENABLED ||
893b3e3f871SChun-Yeow Yeoh 	    changed & BSS_CHANGED_BEACON) {
8948e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC,
8958e84c258SEugene Krasnikov 			    "mac bss changed beacon enabled %d\n",
8968e84c258SEugene Krasnikov 			    bss_conf->enable_beacon);
8978e84c258SEugene Krasnikov 
8988e84c258SEugene Krasnikov 		if (bss_conf->enable_beacon) {
899908628dbSPontus Fuchs 			vif_priv->dtim_period = bss_conf->dtim_period;
90090023c03SPontus Fuchs 			vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
9018e84c258SEugene Krasnikov 			wcn36xx_smd_config_bss(wcn, vif, NULL,
9028e84c258SEugene Krasnikov 					       vif->addr, false);
9038e84c258SEugene Krasnikov 			skb = ieee80211_beacon_get_tim(hw, vif, &tim_off,
9048e84c258SEugene Krasnikov 						       &tim_len);
9058e84c258SEugene Krasnikov 			if (!skb) {
9068e84c258SEugene Krasnikov 				wcn36xx_err("failed to alloc beacon skb\n");
9078e84c258SEugene Krasnikov 				goto out;
9088e84c258SEugene Krasnikov 			}
9098e84c258SEugene Krasnikov 			wcn36xx_smd_send_beacon(wcn, vif, skb, tim_off, 0);
9108e84c258SEugene Krasnikov 			dev_kfree_skb(skb);
9118e84c258SEugene Krasnikov 
9128e84c258SEugene Krasnikov 			if (vif->type == NL80211_IFTYPE_ADHOC ||
9138e84c258SEugene Krasnikov 			    vif->type == NL80211_IFTYPE_MESH_POINT)
9148e84c258SEugene Krasnikov 				link_state = WCN36XX_HAL_LINK_IBSS_STATE;
9158e84c258SEugene Krasnikov 			else
9168e84c258SEugene Krasnikov 				link_state = WCN36XX_HAL_LINK_AP_STATE;
9178e84c258SEugene Krasnikov 
9188e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
9198e84c258SEugene Krasnikov 						link_state);
9208e84c258SEugene Krasnikov 		} else {
9215443918dSBjorn Andersson 			wcn36xx_smd_delete_bss(wcn, vif);
9228e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
9238e84c258SEugene Krasnikov 						WCN36XX_HAL_LINK_IDLE_STATE);
9248e84c258SEugene Krasnikov 		}
9258e84c258SEugene Krasnikov 	}
9268e84c258SEugene Krasnikov out:
92739efc7ccSBjorn Andersson 
92839efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
92939efc7ccSBjorn Andersson 
9308e84c258SEugene Krasnikov 	return;
9318e84c258SEugene Krasnikov }
9328e84c258SEugene Krasnikov 
9338e84c258SEugene Krasnikov /* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */
9348e84c258SEugene Krasnikov static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
9358e84c258SEugene Krasnikov {
9368e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
9378e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value);
9388e84c258SEugene Krasnikov 
93939efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
9408e84c258SEugene Krasnikov 	wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value);
94139efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
94239efc7ccSBjorn Andersson 
9438e84c258SEugene Krasnikov 	return 0;
9448e84c258SEugene Krasnikov }
9458e84c258SEugene Krasnikov 
9468e84c258SEugene Krasnikov static void wcn36xx_remove_interface(struct ieee80211_hw *hw,
9478e84c258SEugene Krasnikov 				     struct ieee80211_vif *vif)
9488e84c258SEugene Krasnikov {
9498e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
950ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
9518e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif);
9528e84c258SEugene Krasnikov 
95339efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
95439efc7ccSBjorn Andersson 
9558e84c258SEugene Krasnikov 	list_del(&vif_priv->list);
9568e84c258SEugene Krasnikov 	wcn36xx_smd_delete_sta_self(wcn, vif->addr);
95739efc7ccSBjorn Andersson 
95839efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
9598e84c258SEugene Krasnikov }
9608e84c258SEugene Krasnikov 
9618e84c258SEugene Krasnikov static int wcn36xx_add_interface(struct ieee80211_hw *hw,
9628e84c258SEugene Krasnikov 				 struct ieee80211_vif *vif)
9638e84c258SEugene Krasnikov {
9648e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
965ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
9668e84c258SEugene Krasnikov 
9678e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n",
9688e84c258SEugene Krasnikov 		    vif, vif->type);
9698e84c258SEugene Krasnikov 
9708e84c258SEugene Krasnikov 	if (!(NL80211_IFTYPE_STATION == vif->type ||
9718e84c258SEugene Krasnikov 	      NL80211_IFTYPE_AP == vif->type ||
9728e84c258SEugene Krasnikov 	      NL80211_IFTYPE_ADHOC == vif->type ||
9738e84c258SEugene Krasnikov 	      NL80211_IFTYPE_MESH_POINT == vif->type)) {
9748e84c258SEugene Krasnikov 		wcn36xx_warn("Unsupported interface type requested: %d\n",
9758e84c258SEugene Krasnikov 			     vif->type);
9768e84c258SEugene Krasnikov 		return -EOPNOTSUPP;
9778e84c258SEugene Krasnikov 	}
9788e84c258SEugene Krasnikov 
97939efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
98039efc7ccSBjorn Andersson 
9812edfcf2bSDaniel Mack 	vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
9828e84c258SEugene Krasnikov 	list_add(&vif_priv->list, &wcn->vif_list);
9838e84c258SEugene Krasnikov 	wcn36xx_smd_add_sta_self(wcn, vif);
9848e84c258SEugene Krasnikov 
98539efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
98639efc7ccSBjorn Andersson 
9878e84c258SEugene Krasnikov 	return 0;
9888e84c258SEugene Krasnikov }
9898e84c258SEugene Krasnikov 
9908e84c258SEugene Krasnikov static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
9918e84c258SEugene Krasnikov 			   struct ieee80211_sta *sta)
9928e84c258SEugene Krasnikov {
9938e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
994ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
995a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
9968e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n",
9978e84c258SEugene Krasnikov 		    vif, sta->addr);
9988e84c258SEugene Krasnikov 
99939efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
100039efc7ccSBjorn Andersson 
1001e26dc173SBob Copeland 	spin_lock_init(&sta_priv->ampdu_lock);
10028e84c258SEugene Krasnikov 	sta_priv->vif = vif_priv;
10038e84c258SEugene Krasnikov 	/*
10048e84c258SEugene Krasnikov 	 * For STA mode HW will be configured on BSS_CHANGED_ASSOC because
10058e84c258SEugene Krasnikov 	 * at this stage AID is not available yet.
10068e84c258SEugene Krasnikov 	 */
10078e84c258SEugene Krasnikov 	if (NL80211_IFTYPE_STATION != vif->type) {
10088e84c258SEugene Krasnikov 		wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
10098e84c258SEugene Krasnikov 		sta_priv->aid = sta->aid;
10108e84c258SEugene Krasnikov 		wcn36xx_smd_config_sta(wcn, vif, sta);
10118e84c258SEugene Krasnikov 	}
101239efc7ccSBjorn Andersson 
101339efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
101439efc7ccSBjorn Andersson 
10158e84c258SEugene Krasnikov 	return 0;
10168e84c258SEugene Krasnikov }
10178e84c258SEugene Krasnikov 
10188e84c258SEugene Krasnikov static int wcn36xx_sta_remove(struct ieee80211_hw *hw,
10198e84c258SEugene Krasnikov 			      struct ieee80211_vif *vif,
10208e84c258SEugene Krasnikov 			      struct ieee80211_sta *sta)
10218e84c258SEugene Krasnikov {
10228e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
1023a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
10248e84c258SEugene Krasnikov 
10258e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n",
10268e84c258SEugene Krasnikov 		    vif, sta->addr, sta_priv->sta_index);
10278e84c258SEugene Krasnikov 
102839efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
102939efc7ccSBjorn Andersson 
10308e84c258SEugene Krasnikov 	wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index);
10318e84c258SEugene Krasnikov 	sta_priv->vif = NULL;
103239efc7ccSBjorn Andersson 
103339efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
103439efc7ccSBjorn Andersson 
10358e84c258SEugene Krasnikov 	return 0;
10368e84c258SEugene Krasnikov }
10378e84c258SEugene Krasnikov 
10388e84c258SEugene Krasnikov #ifdef CONFIG_PM
10398e84c258SEugene Krasnikov 
10408e84c258SEugene Krasnikov static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow)
10418e84c258SEugene Krasnikov {
10428e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
10438e84c258SEugene Krasnikov 
10448e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n");
10458e84c258SEugene Krasnikov 
10468e84c258SEugene Krasnikov 	flush_workqueue(wcn->hal_ind_wq);
10478e84c258SEugene Krasnikov 	wcn36xx_smd_set_power_params(wcn, true);
10488e84c258SEugene Krasnikov 	return 0;
10498e84c258SEugene Krasnikov }
10508e84c258SEugene Krasnikov 
10518e84c258SEugene Krasnikov static int wcn36xx_resume(struct ieee80211_hw *hw)
10528e84c258SEugene Krasnikov {
10538e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
10548e84c258SEugene Krasnikov 
10558e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n");
10568e84c258SEugene Krasnikov 
10578e84c258SEugene Krasnikov 	flush_workqueue(wcn->hal_ind_wq);
10588e84c258SEugene Krasnikov 	wcn36xx_smd_set_power_params(wcn, false);
10598e84c258SEugene Krasnikov 	return 0;
10608e84c258SEugene Krasnikov }
10618e84c258SEugene Krasnikov 
10628e84c258SEugene Krasnikov #endif
10638e84c258SEugene Krasnikov 
10648e84c258SEugene Krasnikov static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
10658e84c258SEugene Krasnikov 		    struct ieee80211_vif *vif,
106650ea05efSSara Sharon 		    struct ieee80211_ampdu_params *params)
10678e84c258SEugene Krasnikov {
10688e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
1069a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(params->sta);
107050ea05efSSara Sharon 	struct ieee80211_sta *sta = params->sta;
107150ea05efSSara Sharon 	enum ieee80211_ampdu_mlme_action action = params->action;
107250ea05efSSara Sharon 	u16 tid = params->tid;
107350ea05efSSara Sharon 	u16 *ssn = &params->ssn;
10748e84c258SEugene Krasnikov 
10758e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n",
10768e84c258SEugene Krasnikov 		    action, tid);
10778e84c258SEugene Krasnikov 
107839efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
107939efc7ccSBjorn Andersson 
10808e84c258SEugene Krasnikov 	switch (action) {
10818e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_RX_START:
10828e84c258SEugene Krasnikov 		sta_priv->tid = tid;
10838e84c258SEugene Krasnikov 		wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0,
10848e84c258SEugene Krasnikov 			get_sta_index(vif, sta_priv));
10858e84c258SEugene Krasnikov 		wcn36xx_smd_add_ba(wcn);
10868e84c258SEugene Krasnikov 		wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv));
10878e84c258SEugene Krasnikov 		break;
10888e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_RX_STOP:
10898e84c258SEugene Krasnikov 		wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv));
10908e84c258SEugene Krasnikov 		break;
10918e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_START:
1092e26dc173SBob Copeland 		spin_lock_bh(&sta_priv->ampdu_lock);
1093e26dc173SBob Copeland 		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
1094e26dc173SBob Copeland 		spin_unlock_bh(&sta_priv->ampdu_lock);
1095e26dc173SBob Copeland 
10968e84c258SEugene Krasnikov 		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
10978e84c258SEugene Krasnikov 		break;
10988e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_OPERATIONAL:
1099e26dc173SBob Copeland 		spin_lock_bh(&sta_priv->ampdu_lock);
1100e26dc173SBob Copeland 		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL;
1101e26dc173SBob Copeland 		spin_unlock_bh(&sta_priv->ampdu_lock);
1102e26dc173SBob Copeland 
11038e84c258SEugene Krasnikov 		wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1,
11048e84c258SEugene Krasnikov 			get_sta_index(vif, sta_priv));
11058e84c258SEugene Krasnikov 		break;
11068e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
11078e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
11088e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_STOP_CONT:
1109e26dc173SBob Copeland 		spin_lock_bh(&sta_priv->ampdu_lock);
1110e26dc173SBob Copeland 		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE;
1111e26dc173SBob Copeland 		spin_unlock_bh(&sta_priv->ampdu_lock);
1112e26dc173SBob Copeland 
11138e84c258SEugene Krasnikov 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
11148e84c258SEugene Krasnikov 		break;
11158e84c258SEugene Krasnikov 	default:
11168e84c258SEugene Krasnikov 		wcn36xx_err("Unknown AMPDU action\n");
11178e84c258SEugene Krasnikov 	}
11188e84c258SEugene Krasnikov 
111939efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
112039efc7ccSBjorn Andersson 
11218e84c258SEugene Krasnikov 	return 0;
11228e84c258SEugene Krasnikov }
11238e84c258SEugene Krasnikov 
11248e84c258SEugene Krasnikov static const struct ieee80211_ops wcn36xx_ops = {
11258e84c258SEugene Krasnikov 	.start			= wcn36xx_start,
11268e84c258SEugene Krasnikov 	.stop			= wcn36xx_stop,
11278e84c258SEugene Krasnikov 	.add_interface		= wcn36xx_add_interface,
11288e84c258SEugene Krasnikov 	.remove_interface	= wcn36xx_remove_interface,
11298e84c258SEugene Krasnikov #ifdef CONFIG_PM
11308e84c258SEugene Krasnikov 	.suspend		= wcn36xx_suspend,
11318e84c258SEugene Krasnikov 	.resume			= wcn36xx_resume,
11328e84c258SEugene Krasnikov #endif
11338e84c258SEugene Krasnikov 	.config			= wcn36xx_config,
113420a779edSPontus Fuchs 	.prepare_multicast	= wcn36xx_prepare_multicast,
11358e84c258SEugene Krasnikov 	.configure_filter       = wcn36xx_configure_filter,
11368e84c258SEugene Krasnikov 	.tx			= wcn36xx_tx,
11378e84c258SEugene Krasnikov 	.set_key		= wcn36xx_set_key,
113888603903SBjorn Andersson 	.hw_scan		= wcn36xx_hw_scan,
113903c95dbeSBjorn Andersson 	.cancel_hw_scan		= wcn36xx_cancel_hw_scan,
11408e84c258SEugene Krasnikov 	.bss_info_changed	= wcn36xx_bss_info_changed,
11418e84c258SEugene Krasnikov 	.set_rts_threshold	= wcn36xx_set_rts_threshold,
11428e84c258SEugene Krasnikov 	.sta_add		= wcn36xx_sta_add,
11438e84c258SEugene Krasnikov 	.sta_remove		= wcn36xx_sta_remove,
11448e84c258SEugene Krasnikov 	.ampdu_action		= wcn36xx_ampdu_action,
11458e84c258SEugene Krasnikov };
11468e84c258SEugene Krasnikov 
11478e84c258SEugene Krasnikov static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
11488e84c258SEugene Krasnikov {
11498e84c258SEugene Krasnikov 	int ret = 0;
11508e84c258SEugene Krasnikov 
11518e84c258SEugene Krasnikov 	static const u32 cipher_suites[] = {
11528e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_WEP40,
11538e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_WEP104,
11548e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_TKIP,
11558e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_CCMP,
11568e84c258SEugene Krasnikov 	};
11578e84c258SEugene Krasnikov 
115830686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, TIMING_BEACON_ONLY);
115930686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, AMPDU_AGGREGATION);
116030686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, CONNECTION_MONITOR);
116130686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, SUPPORTS_PS);
116230686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, SIGNAL_DBM);
116330686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL);
116488603903SBjorn Andersson 	ieee80211_hw_set(wcn->hw, SINGLE_SCAN_ON_ALL_BANDS);
11658e84c258SEugene Krasnikov 
11668e84c258SEugene Krasnikov 	wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
11678e84c258SEugene Krasnikov 		BIT(NL80211_IFTYPE_AP) |
11688e84c258SEugene Krasnikov 		BIT(NL80211_IFTYPE_ADHOC) |
11698e84c258SEugene Krasnikov 		BIT(NL80211_IFTYPE_MESH_POINT);
11708e84c258SEugene Krasnikov 
117157fbcce3SJohannes Berg 	wcn->hw->wiphy->bands[NL80211_BAND_2GHZ] = &wcn_band_2ghz;
1172fd52bdaeSLoic Poulain 	if (wcn->rf_id != RF_IRIS_WCN3620)
117357fbcce3SJohannes Berg 		wcn->hw->wiphy->bands[NL80211_BAND_5GHZ] = &wcn_band_5ghz;
11748e84c258SEugene Krasnikov 
117588603903SBjorn Andersson 	wcn->hw->wiphy->max_scan_ssids = WCN36XX_MAX_SCAN_SSIDS;
117688603903SBjorn Andersson 	wcn->hw->wiphy->max_scan_ie_len = WCN36XX_MAX_SCAN_IE_LEN;
117788603903SBjorn Andersson 
11788e84c258SEugene Krasnikov 	wcn->hw->wiphy->cipher_suites = cipher_suites;
11798e84c258SEugene Krasnikov 	wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
11808e84c258SEugene Krasnikov 
11818e84c258SEugene Krasnikov #ifdef CONFIG_PM
11828e84c258SEugene Krasnikov 	wcn->hw->wiphy->wowlan = &wowlan_support;
11838e84c258SEugene Krasnikov #endif
11848e84c258SEugene Krasnikov 
11858e84c258SEugene Krasnikov 	wcn->hw->max_listen_interval = 200;
11868e84c258SEugene Krasnikov 
11878e84c258SEugene Krasnikov 	wcn->hw->queues = 4;
11888e84c258SEugene Krasnikov 
11898e84c258SEugene Krasnikov 	SET_IEEE80211_DEV(wcn->hw, wcn->dev);
11908e84c258SEugene Krasnikov 
11918e84c258SEugene Krasnikov 	wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta);
11928e84c258SEugene Krasnikov 	wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif);
11938e84c258SEugene Krasnikov 
1194ae44b502SAndrew Zaborowski 	wiphy_ext_feature_set(wcn->hw->wiphy,
1195ae44b502SAndrew Zaborowski 			      NL80211_EXT_FEATURE_CQM_RSSI_LIST);
1196ae44b502SAndrew Zaborowski 
11978e84c258SEugene Krasnikov 	return ret;
11988e84c258SEugene Krasnikov }
11998e84c258SEugene Krasnikov 
12008e84c258SEugene Krasnikov static int wcn36xx_platform_get_resources(struct wcn36xx *wcn,
12018e84c258SEugene Krasnikov 					  struct platform_device *pdev)
12028e84c258SEugene Krasnikov {
120305ddce49SBjorn Andersson 	struct device_node *mmio_node;
1204fd52bdaeSLoic Poulain 	struct device_node *iris_node;
12058e84c258SEugene Krasnikov 	struct resource *res;
120605ddce49SBjorn Andersson 	int index;
120705ddce49SBjorn Andersson 	int ret;
120805ddce49SBjorn Andersson 
12098e84c258SEugene Krasnikov 	/* Set TX IRQ */
1210f303a931SBjorn Andersson 	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tx");
12118e84c258SEugene Krasnikov 	if (!res) {
12128e84c258SEugene Krasnikov 		wcn36xx_err("failed to get tx_irq\n");
12138e84c258SEugene Krasnikov 		return -ENOENT;
12148e84c258SEugene Krasnikov 	}
12158e84c258SEugene Krasnikov 	wcn->tx_irq = res->start;
12168e84c258SEugene Krasnikov 
12178e84c258SEugene Krasnikov 	/* Set RX IRQ */
1218f303a931SBjorn Andersson 	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "rx");
12198e84c258SEugene Krasnikov 	if (!res) {
12208e84c258SEugene Krasnikov 		wcn36xx_err("failed to get rx_irq\n");
12218e84c258SEugene Krasnikov 		return -ENOENT;
12228e84c258SEugene Krasnikov 	}
12238e84c258SEugene Krasnikov 	wcn->rx_irq = res->start;
12248e84c258SEugene Krasnikov 
1225f303a931SBjorn Andersson 	/* Acquire SMSM tx enable handle */
1226f303a931SBjorn Andersson 	wcn->tx_enable_state = qcom_smem_state_get(&pdev->dev,
1227f303a931SBjorn Andersson 			"tx-enable", &wcn->tx_enable_state_bit);
1228f303a931SBjorn Andersson 	if (IS_ERR(wcn->tx_enable_state)) {
1229f303a931SBjorn Andersson 		wcn36xx_err("failed to get tx-enable state\n");
1230f303a931SBjorn Andersson 		return PTR_ERR(wcn->tx_enable_state);
1231f303a931SBjorn Andersson 	}
1232f303a931SBjorn Andersson 
1233f303a931SBjorn Andersson 	/* Acquire SMSM tx rings empty handle */
1234f303a931SBjorn Andersson 	wcn->tx_rings_empty_state = qcom_smem_state_get(&pdev->dev,
1235f303a931SBjorn Andersson 			"tx-rings-empty", &wcn->tx_rings_empty_state_bit);
1236f303a931SBjorn Andersson 	if (IS_ERR(wcn->tx_rings_empty_state)) {
1237f303a931SBjorn Andersson 		wcn36xx_err("failed to get tx-rings-empty state\n");
1238f303a931SBjorn Andersson 		return PTR_ERR(wcn->tx_rings_empty_state);
1239f303a931SBjorn Andersson 	}
1240f303a931SBjorn Andersson 
124105ddce49SBjorn Andersson 	mmio_node = of_parse_phandle(pdev->dev.parent->of_node, "qcom,mmio", 0);
124205ddce49SBjorn Andersson 	if (!mmio_node) {
124305ddce49SBjorn Andersson 		wcn36xx_err("failed to acquire qcom,mmio reference\n");
124405ddce49SBjorn Andersson 		return -EINVAL;
12458e84c258SEugene Krasnikov 	}
124605ddce49SBjorn Andersson 
12476f10b4e1SBjorn Andersson 	wcn->is_pronto = !!of_device_is_compatible(mmio_node, "qcom,pronto");
12486f10b4e1SBjorn Andersson 
124905ddce49SBjorn Andersson 	/* Map the CCU memory */
125005ddce49SBjorn Andersson 	index = of_property_match_string(mmio_node, "reg-names", "ccu");
125105ddce49SBjorn Andersson 	wcn->ccu_base = of_iomap(mmio_node, index);
125205ddce49SBjorn Andersson 	if (!wcn->ccu_base) {
125305ddce49SBjorn Andersson 		wcn36xx_err("failed to map ccu memory\n");
125405ddce49SBjorn Andersson 		ret = -ENOMEM;
125505ddce49SBjorn Andersson 		goto put_mmio_node;
12568e84c258SEugene Krasnikov 	}
125705ddce49SBjorn Andersson 
125805ddce49SBjorn Andersson 	/* Map the DXE memory */
125905ddce49SBjorn Andersson 	index = of_property_match_string(mmio_node, "reg-names", "dxe");
126005ddce49SBjorn Andersson 	wcn->dxe_base = of_iomap(mmio_node, index);
126105ddce49SBjorn Andersson 	if (!wcn->dxe_base) {
126205ddce49SBjorn Andersson 		wcn36xx_err("failed to map dxe memory\n");
126305ddce49SBjorn Andersson 		ret = -ENOMEM;
126405ddce49SBjorn Andersson 		goto unmap_ccu;
126505ddce49SBjorn Andersson 	}
126605ddce49SBjorn Andersson 
1267fd52bdaeSLoic Poulain 	/* External RF module */
12681967c128SJohan Hovold 	iris_node = of_get_child_by_name(mmio_node, "iris");
1269fd52bdaeSLoic Poulain 	if (iris_node) {
1270fd52bdaeSLoic Poulain 		if (of_device_is_compatible(iris_node, "qcom,wcn3620"))
1271fd52bdaeSLoic Poulain 			wcn->rf_id = RF_IRIS_WCN3620;
1272fd52bdaeSLoic Poulain 		of_node_put(iris_node);
1273fd52bdaeSLoic Poulain 	}
1274fd52bdaeSLoic Poulain 
127505ddce49SBjorn Andersson 	of_node_put(mmio_node);
12768e84c258SEugene Krasnikov 	return 0;
127705ddce49SBjorn Andersson 
127805ddce49SBjorn Andersson unmap_ccu:
127905ddce49SBjorn Andersson 	iounmap(wcn->ccu_base);
128005ddce49SBjorn Andersson put_mmio_node:
128105ddce49SBjorn Andersson 	of_node_put(mmio_node);
128205ddce49SBjorn Andersson 	return ret;
12838e84c258SEugene Krasnikov }
12848e84c258SEugene Krasnikov 
12858e84c258SEugene Krasnikov static int wcn36xx_probe(struct platform_device *pdev)
12868e84c258SEugene Krasnikov {
12878e84c258SEugene Krasnikov 	struct ieee80211_hw *hw;
12888e84c258SEugene Krasnikov 	struct wcn36xx *wcn;
1289f303a931SBjorn Andersson 	void *wcnss;
12908e84c258SEugene Krasnikov 	int ret;
1291f303a931SBjorn Andersson 	const u8 *addr;
12928e84c258SEugene Krasnikov 
12938e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n");
12948e84c258SEugene Krasnikov 
1295f303a931SBjorn Andersson 	wcnss = dev_get_drvdata(pdev->dev.parent);
1296f303a931SBjorn Andersson 
12978e84c258SEugene Krasnikov 	hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops);
12988e84c258SEugene Krasnikov 	if (!hw) {
12998e84c258SEugene Krasnikov 		wcn36xx_err("failed to alloc hw\n");
13008e84c258SEugene Krasnikov 		ret = -ENOMEM;
13018e84c258SEugene Krasnikov 		goto out_err;
13028e84c258SEugene Krasnikov 	}
13038e84c258SEugene Krasnikov 	platform_set_drvdata(pdev, hw);
13048e84c258SEugene Krasnikov 	wcn = hw->priv;
13058e84c258SEugene Krasnikov 	wcn->hw = hw;
13068e84c258SEugene Krasnikov 	wcn->dev = &pdev->dev;
13076b8a127bSRamon Fried 	wcn->first_boot = true;
130839efc7ccSBjorn Andersson 	mutex_init(&wcn->conf_mutex);
13098e84c258SEugene Krasnikov 	mutex_init(&wcn->hal_mutex);
131088603903SBjorn Andersson 	mutex_init(&wcn->scan_lock);
131188603903SBjorn Andersson 
131288603903SBjorn Andersson 	INIT_WORK(&wcn->scan_work, wcn36xx_hw_scan_worker);
13138e84c258SEugene Krasnikov 
13145052de8dSBjorn Andersson 	wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process, hw);
1315f303a931SBjorn Andersson 	if (IS_ERR(wcn->smd_channel)) {
1316f303a931SBjorn Andersson 		wcn36xx_err("failed to open WLAN_CTRL channel\n");
1317f303a931SBjorn Andersson 		ret = PTR_ERR(wcn->smd_channel);
1318f303a931SBjorn Andersson 		goto out_wq;
1319f303a931SBjorn Andersson 	}
1320f303a931SBjorn Andersson 
1321f303a931SBjorn Andersson 	addr = of_get_property(pdev->dev.of_node, "local-mac-address", &ret);
1322f303a931SBjorn Andersson 	if (addr && ret != ETH_ALEN) {
1323f303a931SBjorn Andersson 		wcn36xx_err("invalid local-mac-address\n");
1324f303a931SBjorn Andersson 		ret = -EINVAL;
1325f303a931SBjorn Andersson 		goto out_wq;
1326f303a931SBjorn Andersson 	} else if (addr) {
13278e84c258SEugene Krasnikov 		wcn36xx_info("mac address: %pM\n", addr);
13288e84c258SEugene Krasnikov 		SET_IEEE80211_PERM_ADDR(wcn->hw, addr);
13298e84c258SEugene Krasnikov 	}
13308e84c258SEugene Krasnikov 
13318e84c258SEugene Krasnikov 	ret = wcn36xx_platform_get_resources(wcn, pdev);
13328e84c258SEugene Krasnikov 	if (ret)
13338e84c258SEugene Krasnikov 		goto out_wq;
13348e84c258SEugene Krasnikov 
13358e84c258SEugene Krasnikov 	wcn36xx_init_ieee80211(wcn);
13368e84c258SEugene Krasnikov 	ret = ieee80211_register_hw(wcn->hw);
13378e84c258SEugene Krasnikov 	if (ret)
13388e84c258SEugene Krasnikov 		goto out_unmap;
13398e84c258SEugene Krasnikov 
13408e84c258SEugene Krasnikov 	return 0;
13418e84c258SEugene Krasnikov 
13428e84c258SEugene Krasnikov out_unmap:
134305ddce49SBjorn Andersson 	iounmap(wcn->ccu_base);
134405ddce49SBjorn Andersson 	iounmap(wcn->dxe_base);
13458e84c258SEugene Krasnikov out_wq:
13468e84c258SEugene Krasnikov 	ieee80211_free_hw(hw);
13478e84c258SEugene Krasnikov out_err:
13488e84c258SEugene Krasnikov 	return ret;
13498e84c258SEugene Krasnikov }
1350f303a931SBjorn Andersson 
13518e84c258SEugene Krasnikov static int wcn36xx_remove(struct platform_device *pdev)
13528e84c258SEugene Krasnikov {
13538e84c258SEugene Krasnikov 	struct ieee80211_hw *hw = platform_get_drvdata(pdev);
13548e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
13558e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n");
13568e84c258SEugene Krasnikov 
13574bda7fafSPontus Fuchs 	release_firmware(wcn->nv);
13588e84c258SEugene Krasnikov 
13598e84c258SEugene Krasnikov 	ieee80211_unregister_hw(hw);
1360f303a931SBjorn Andersson 
1361f303a931SBjorn Andersson 	qcom_smem_state_put(wcn->tx_enable_state);
1362f303a931SBjorn Andersson 	qcom_smem_state_put(wcn->tx_rings_empty_state);
1363f303a931SBjorn Andersson 
1364efad8396SBjorn Andersson 	rpmsg_destroy_ept(wcn->smd_channel);
1365efad8396SBjorn Andersson 
136605ddce49SBjorn Andersson 	iounmap(wcn->dxe_base);
136705ddce49SBjorn Andersson 	iounmap(wcn->ccu_base);
1368d5362888SBjorn Andersson 
1369d5362888SBjorn Andersson 	mutex_destroy(&wcn->hal_mutex);
13708e84c258SEugene Krasnikov 	ieee80211_free_hw(hw);
13718e84c258SEugene Krasnikov 
13728e84c258SEugene Krasnikov 	return 0;
13738e84c258SEugene Krasnikov }
1374f303a931SBjorn Andersson 
1375f303a931SBjorn Andersson static const struct of_device_id wcn36xx_of_match[] = {
1376f303a931SBjorn Andersson 	{ .compatible = "qcom,wcnss-wlan" },
13778e84c258SEugene Krasnikov 	{}
13788e84c258SEugene Krasnikov };
1379f303a931SBjorn Andersson MODULE_DEVICE_TABLE(of, wcn36xx_of_match);
13808e84c258SEugene Krasnikov 
13818e84c258SEugene Krasnikov static struct platform_driver wcn36xx_driver = {
13828e84c258SEugene Krasnikov 	.probe      = wcn36xx_probe,
13838e84c258SEugene Krasnikov 	.remove     = wcn36xx_remove,
13848e84c258SEugene Krasnikov 	.driver         = {
13858e84c258SEugene Krasnikov 		.name   = "wcn36xx",
1386f303a931SBjorn Andersson 		.of_match_table = wcn36xx_of_match,
13878e84c258SEugene Krasnikov 	},
13888e84c258SEugene Krasnikov };
13898e84c258SEugene Krasnikov 
1390f303a931SBjorn Andersson module_platform_driver(wcn36xx_driver);
13918e84c258SEugene Krasnikov 
13928e84c258SEugene Krasnikov MODULE_LICENSE("Dual BSD/GPL");
13938e84c258SEugene Krasnikov MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com");
13948e84c258SEugene Krasnikov MODULE_FIRMWARE(WLAN_NV_FILE);
1395