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 
6909bfd05e3SLoic Poulain 	/* ieee80211_scan_completed will be called on FW scan indication */
6919bfd05e3SLoic Poulain 	wcn36xx_smd_stop_hw_scan(wcn);
6929bfd05e3SLoic Poulain 
69303c95dbeSBjorn Andersson 	cancel_work_sync(&wcn->scan_work);
69403c95dbeSBjorn Andersson }
69503c95dbeSBjorn Andersson 
6968e84c258SEugene Krasnikov static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta,
69757fbcce3SJohannes Berg 					 enum nl80211_band band)
6988e84c258SEugene Krasnikov {
6998e84c258SEugene Krasnikov 	int i, size;
7008e84c258SEugene Krasnikov 	u16 *rates_table;
701a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
7028e84c258SEugene Krasnikov 	u32 rates = sta->supp_rates[band];
7038e84c258SEugene Krasnikov 
7048e84c258SEugene Krasnikov 	memset(&sta_priv->supported_rates, 0,
7058e84c258SEugene Krasnikov 		sizeof(sta_priv->supported_rates));
7068e84c258SEugene Krasnikov 	sta_priv->supported_rates.op_rate_mode = STA_11n;
7078e84c258SEugene Krasnikov 
7088e84c258SEugene Krasnikov 	size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates);
7098e84c258SEugene Krasnikov 	rates_table = sta_priv->supported_rates.dsss_rates;
71057fbcce3SJohannes Berg 	if (band == NL80211_BAND_2GHZ) {
7118e84c258SEugene Krasnikov 		for (i = 0; i < size; i++) {
7128e84c258SEugene Krasnikov 			if (rates & 0x01) {
7138e84c258SEugene Krasnikov 				rates_table[i] = wcn_2ghz_rates[i].hw_value;
7148e84c258SEugene Krasnikov 				rates = rates >> 1;
7158e84c258SEugene Krasnikov 			}
7168e84c258SEugene Krasnikov 		}
7178e84c258SEugene Krasnikov 	}
7188e84c258SEugene Krasnikov 
7198e84c258SEugene Krasnikov 	size = ARRAY_SIZE(sta_priv->supported_rates.ofdm_rates);
7208e84c258SEugene Krasnikov 	rates_table = sta_priv->supported_rates.ofdm_rates;
7218e84c258SEugene Krasnikov 	for (i = 0; i < size; i++) {
7228e84c258SEugene Krasnikov 		if (rates & 0x01) {
7238e84c258SEugene Krasnikov 			rates_table[i] = wcn_5ghz_rates[i].hw_value;
7248e84c258SEugene Krasnikov 			rates = rates >> 1;
7258e84c258SEugene Krasnikov 		}
7268e84c258SEugene Krasnikov 	}
7278e84c258SEugene Krasnikov 
7288e84c258SEugene Krasnikov 	if (sta->ht_cap.ht_supported) {
7298e84c258SEugene Krasnikov 		BUILD_BUG_ON(sizeof(sta->ht_cap.mcs.rx_mask) >
7308e84c258SEugene Krasnikov 			sizeof(sta_priv->supported_rates.supported_mcs_set));
7318e84c258SEugene Krasnikov 		memcpy(sta_priv->supported_rates.supported_mcs_set,
7328e84c258SEugene Krasnikov 		       sta->ht_cap.mcs.rx_mask,
7338e84c258SEugene Krasnikov 		       sizeof(sta->ht_cap.mcs.rx_mask));
7348e84c258SEugene Krasnikov 	}
7358e84c258SEugene Krasnikov }
7368e84c258SEugene Krasnikov void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates)
7378e84c258SEugene Krasnikov {
7388e84c258SEugene Krasnikov 	u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = {
7398e84c258SEugene Krasnikov 		HW_RATE_INDEX_6MBPS,
7408e84c258SEugene Krasnikov 		HW_RATE_INDEX_9MBPS,
7418e84c258SEugene Krasnikov 		HW_RATE_INDEX_12MBPS,
7428e84c258SEugene Krasnikov 		HW_RATE_INDEX_18MBPS,
7438e84c258SEugene Krasnikov 		HW_RATE_INDEX_24MBPS,
7448e84c258SEugene Krasnikov 		HW_RATE_INDEX_36MBPS,
7458e84c258SEugene Krasnikov 		HW_RATE_INDEX_48MBPS,
7468e84c258SEugene Krasnikov 		HW_RATE_INDEX_54MBPS
7478e84c258SEugene Krasnikov 	};
7488e84c258SEugene Krasnikov 	u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = {
7498e84c258SEugene Krasnikov 		HW_RATE_INDEX_1MBPS,
7508e84c258SEugene Krasnikov 		HW_RATE_INDEX_2MBPS,
7518e84c258SEugene Krasnikov 		HW_RATE_INDEX_5_5MBPS,
7528e84c258SEugene Krasnikov 		HW_RATE_INDEX_11MBPS
7538e84c258SEugene Krasnikov 	};
7548e84c258SEugene Krasnikov 
7558e84c258SEugene Krasnikov 	rates->op_rate_mode = STA_11n;
7568e84c258SEugene Krasnikov 	memcpy(rates->dsss_rates, dsss_rates,
7578e84c258SEugene Krasnikov 		sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES);
7588e84c258SEugene Krasnikov 	memcpy(rates->ofdm_rates, ofdm_rates,
7598e84c258SEugene Krasnikov 		sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES);
7608e84c258SEugene Krasnikov 	rates->supported_mcs_set[0] = 0xFF;
7618e84c258SEugene Krasnikov }
7628e84c258SEugene Krasnikov static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
7638e84c258SEugene Krasnikov 				     struct ieee80211_vif *vif,
7648e84c258SEugene Krasnikov 				     struct ieee80211_bss_conf *bss_conf,
7658e84c258SEugene Krasnikov 				     u32 changed)
7668e84c258SEugene Krasnikov {
7678e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
7688e84c258SEugene Krasnikov 	struct sk_buff *skb = NULL;
7698e84c258SEugene Krasnikov 	u16 tim_off, tim_len;
7708e84c258SEugene Krasnikov 	enum wcn36xx_hal_link_state link_state;
771ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
7728e84c258SEugene Krasnikov 
7738e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n",
7748e84c258SEugene Krasnikov 		    vif, changed);
7758e84c258SEugene Krasnikov 
77639efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
77739efc7ccSBjorn Andersson 
7788e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_BEACON_INFO) {
7798e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC,
7808e84c258SEugene Krasnikov 			    "mac bss changed dtim period %d\n",
7818e84c258SEugene Krasnikov 			    bss_conf->dtim_period);
7828e84c258SEugene Krasnikov 
7838e84c258SEugene Krasnikov 		vif_priv->dtim_period = bss_conf->dtim_period;
7848e84c258SEugene Krasnikov 	}
7858e84c258SEugene Krasnikov 
7868e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_BSSID) {
7878e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n",
7888e84c258SEugene Krasnikov 			    bss_conf->bssid);
7898e84c258SEugene Krasnikov 
7908e84c258SEugene Krasnikov 		if (!is_zero_ether_addr(bss_conf->bssid)) {
7918e84c258SEugene Krasnikov 			vif_priv->is_joining = true;
79290023c03SPontus Fuchs 			vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
7938e84c258SEugene Krasnikov 			wcn36xx_smd_join(wcn, bss_conf->bssid,
7948e84c258SEugene Krasnikov 					 vif->addr, WCN36XX_HW_CHANNEL(wcn));
7958e84c258SEugene Krasnikov 			wcn36xx_smd_config_bss(wcn, vif, NULL,
7968e84c258SEugene Krasnikov 					       bss_conf->bssid, false);
7978e84c258SEugene Krasnikov 		} else {
7988e84c258SEugene Krasnikov 			vif_priv->is_joining = false;
7998e84c258SEugene Krasnikov 			wcn36xx_smd_delete_bss(wcn, vif);
8002716a8acSPontus Fuchs 			vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
8018e84c258SEugene Krasnikov 		}
8028e84c258SEugene Krasnikov 	}
8038e84c258SEugene Krasnikov 
8048e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_SSID) {
8058e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC,
8068e84c258SEugene Krasnikov 			    "mac bss changed ssid\n");
8078e84c258SEugene Krasnikov 		wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid ",
8088e84c258SEugene Krasnikov 				 bss_conf->ssid, bss_conf->ssid_len);
8098e84c258SEugene Krasnikov 
8108e84c258SEugene Krasnikov 		vif_priv->ssid.length = bss_conf->ssid_len;
8118e84c258SEugene Krasnikov 		memcpy(&vif_priv->ssid.ssid,
8128e84c258SEugene Krasnikov 		       bss_conf->ssid,
8138e84c258SEugene Krasnikov 		       bss_conf->ssid_len);
8148e84c258SEugene Krasnikov 	}
8158e84c258SEugene Krasnikov 
8168e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_ASSOC) {
8178e84c258SEugene Krasnikov 		vif_priv->is_joining = false;
8188e84c258SEugene Krasnikov 		if (bss_conf->assoc) {
8198e84c258SEugene Krasnikov 			struct ieee80211_sta *sta;
8208e84c258SEugene Krasnikov 			struct wcn36xx_sta *sta_priv;
8218e84c258SEugene Krasnikov 
8228e84c258SEugene Krasnikov 			wcn36xx_dbg(WCN36XX_DBG_MAC,
8238e84c258SEugene Krasnikov 				    "mac assoc bss %pM vif %pM AID=%d\n",
8248e84c258SEugene Krasnikov 				     bss_conf->bssid,
8258e84c258SEugene Krasnikov 				     vif->addr,
8268e84c258SEugene Krasnikov 				     bss_conf->aid);
8278e84c258SEugene Krasnikov 
828043ce546SPontus Fuchs 			vif_priv->sta_assoc = true;
82939efc7ccSBjorn Andersson 
83039efc7ccSBjorn Andersson 			/*
83139efc7ccSBjorn Andersson 			 * Holding conf_mutex ensures mutal exclusion with
83239efc7ccSBjorn Andersson 			 * wcn36xx_sta_remove() and as such ensures that sta
83339efc7ccSBjorn Andersson 			 * won't be freed while we're operating on it. As such
83439efc7ccSBjorn Andersson 			 * we do not need to hold the rcu_read_lock().
83539efc7ccSBjorn Andersson 			 */
8368e84c258SEugene Krasnikov 			sta = ieee80211_find_sta(vif, bss_conf->bssid);
8378e84c258SEugene Krasnikov 			if (!sta) {
8388e84c258SEugene Krasnikov 				wcn36xx_err("sta %pM is not found\n",
8398e84c258SEugene Krasnikov 					      bss_conf->bssid);
8408e84c258SEugene Krasnikov 				goto out;
8418e84c258SEugene Krasnikov 			}
842a92e4696SPontus Fuchs 			sta_priv = wcn36xx_sta_to_priv(sta);
8438e84c258SEugene Krasnikov 
8448e84c258SEugene Krasnikov 			wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
8458e84c258SEugene Krasnikov 
8468e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn, bss_conf->bssid,
8478e84c258SEugene Krasnikov 				vif->addr,
8488e84c258SEugene Krasnikov 				WCN36XX_HAL_LINK_POSTASSOC_STATE);
8498e84c258SEugene Krasnikov 			wcn36xx_smd_config_bss(wcn, vif, sta,
8508e84c258SEugene Krasnikov 					       bss_conf->bssid,
8518e84c258SEugene Krasnikov 					       true);
8528e84c258SEugene Krasnikov 			sta_priv->aid = bss_conf->aid;
8538e84c258SEugene Krasnikov 			/*
8548e84c258SEugene Krasnikov 			 * config_sta must be called from  because this is the
8558e84c258SEugene Krasnikov 			 * place where AID is available.
8568e84c258SEugene Krasnikov 			 */
8578e84c258SEugene Krasnikov 			wcn36xx_smd_config_sta(wcn, vif, sta);
8588e84c258SEugene Krasnikov 		} else {
8598e84c258SEugene Krasnikov 			wcn36xx_dbg(WCN36XX_DBG_MAC,
8608e84c258SEugene Krasnikov 				    "disassociated bss %pM vif %pM AID=%d\n",
8618e84c258SEugene Krasnikov 				    bss_conf->bssid,
8628e84c258SEugene Krasnikov 				    vif->addr,
8638e84c258SEugene Krasnikov 				    bss_conf->aid);
864043ce546SPontus Fuchs 			vif_priv->sta_assoc = false;
8658e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn,
8668e84c258SEugene Krasnikov 						bss_conf->bssid,
8678e84c258SEugene Krasnikov 						vif->addr,
8688e84c258SEugene Krasnikov 						WCN36XX_HAL_LINK_IDLE_STATE);
8698e84c258SEugene Krasnikov 		}
8708e84c258SEugene Krasnikov 	}
8718e84c258SEugene Krasnikov 
8728e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_AP_PROBE_RESP) {
8738e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n");
8748e84c258SEugene Krasnikov 		skb = ieee80211_proberesp_get(hw, vif);
8758e84c258SEugene Krasnikov 		if (!skb) {
8768e84c258SEugene Krasnikov 			wcn36xx_err("failed to alloc probereq skb\n");
8778e84c258SEugene Krasnikov 			goto out;
8788e84c258SEugene Krasnikov 		}
8798e84c258SEugene Krasnikov 
8808e84c258SEugene Krasnikov 		wcn36xx_smd_update_proberesp_tmpl(wcn, vif, skb);
8818e84c258SEugene Krasnikov 		dev_kfree_skb(skb);
8828e84c258SEugene Krasnikov 	}
8838e84c258SEugene Krasnikov 
884b3e3f871SChun-Yeow Yeoh 	if (changed & BSS_CHANGED_BEACON_ENABLED ||
885b3e3f871SChun-Yeow Yeoh 	    changed & BSS_CHANGED_BEACON) {
8868e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC,
8878e84c258SEugene Krasnikov 			    "mac bss changed beacon enabled %d\n",
8888e84c258SEugene Krasnikov 			    bss_conf->enable_beacon);
8898e84c258SEugene Krasnikov 
8908e84c258SEugene Krasnikov 		if (bss_conf->enable_beacon) {
891908628dbSPontus Fuchs 			vif_priv->dtim_period = bss_conf->dtim_period;
89290023c03SPontus Fuchs 			vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
8938e84c258SEugene Krasnikov 			wcn36xx_smd_config_bss(wcn, vif, NULL,
8948e84c258SEugene Krasnikov 					       vif->addr, false);
8958e84c258SEugene Krasnikov 			skb = ieee80211_beacon_get_tim(hw, vif, &tim_off,
8968e84c258SEugene Krasnikov 						       &tim_len);
8978e84c258SEugene Krasnikov 			if (!skb) {
8988e84c258SEugene Krasnikov 				wcn36xx_err("failed to alloc beacon skb\n");
8998e84c258SEugene Krasnikov 				goto out;
9008e84c258SEugene Krasnikov 			}
9018e84c258SEugene Krasnikov 			wcn36xx_smd_send_beacon(wcn, vif, skb, tim_off, 0);
9028e84c258SEugene Krasnikov 			dev_kfree_skb(skb);
9038e84c258SEugene Krasnikov 
9048e84c258SEugene Krasnikov 			if (vif->type == NL80211_IFTYPE_ADHOC ||
9058e84c258SEugene Krasnikov 			    vif->type == NL80211_IFTYPE_MESH_POINT)
9068e84c258SEugene Krasnikov 				link_state = WCN36XX_HAL_LINK_IBSS_STATE;
9078e84c258SEugene Krasnikov 			else
9088e84c258SEugene Krasnikov 				link_state = WCN36XX_HAL_LINK_AP_STATE;
9098e84c258SEugene Krasnikov 
9108e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
9118e84c258SEugene Krasnikov 						link_state);
9128e84c258SEugene Krasnikov 		} else {
9135443918dSBjorn Andersson 			wcn36xx_smd_delete_bss(wcn, vif);
9148e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
9158e84c258SEugene Krasnikov 						WCN36XX_HAL_LINK_IDLE_STATE);
9168e84c258SEugene Krasnikov 		}
9178e84c258SEugene Krasnikov 	}
9188e84c258SEugene Krasnikov out:
91939efc7ccSBjorn Andersson 
92039efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
92139efc7ccSBjorn Andersson 
9228e84c258SEugene Krasnikov 	return;
9238e84c258SEugene Krasnikov }
9248e84c258SEugene Krasnikov 
9258e84c258SEugene Krasnikov /* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */
9268e84c258SEugene Krasnikov static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
9278e84c258SEugene Krasnikov {
9288e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
9298e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value);
9308e84c258SEugene Krasnikov 
93139efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
9328e84c258SEugene Krasnikov 	wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value);
93339efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
93439efc7ccSBjorn Andersson 
9358e84c258SEugene Krasnikov 	return 0;
9368e84c258SEugene Krasnikov }
9378e84c258SEugene Krasnikov 
9388e84c258SEugene Krasnikov static void wcn36xx_remove_interface(struct ieee80211_hw *hw,
9398e84c258SEugene Krasnikov 				     struct ieee80211_vif *vif)
9408e84c258SEugene Krasnikov {
9418e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
942ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
9438e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif);
9448e84c258SEugene Krasnikov 
94539efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
94639efc7ccSBjorn Andersson 
9478e84c258SEugene Krasnikov 	list_del(&vif_priv->list);
9488e84c258SEugene Krasnikov 	wcn36xx_smd_delete_sta_self(wcn, vif->addr);
94939efc7ccSBjorn Andersson 
95039efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
9518e84c258SEugene Krasnikov }
9528e84c258SEugene Krasnikov 
9538e84c258SEugene Krasnikov static int wcn36xx_add_interface(struct ieee80211_hw *hw,
9548e84c258SEugene Krasnikov 				 struct ieee80211_vif *vif)
9558e84c258SEugene Krasnikov {
9568e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
957ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
9588e84c258SEugene Krasnikov 
9598e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n",
9608e84c258SEugene Krasnikov 		    vif, vif->type);
9618e84c258SEugene Krasnikov 
9628e84c258SEugene Krasnikov 	if (!(NL80211_IFTYPE_STATION == vif->type ||
9638e84c258SEugene Krasnikov 	      NL80211_IFTYPE_AP == vif->type ||
9648e84c258SEugene Krasnikov 	      NL80211_IFTYPE_ADHOC == vif->type ||
9658e84c258SEugene Krasnikov 	      NL80211_IFTYPE_MESH_POINT == vif->type)) {
9668e84c258SEugene Krasnikov 		wcn36xx_warn("Unsupported interface type requested: %d\n",
9678e84c258SEugene Krasnikov 			     vif->type);
9688e84c258SEugene Krasnikov 		return -EOPNOTSUPP;
9698e84c258SEugene Krasnikov 	}
9708e84c258SEugene Krasnikov 
97139efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
97239efc7ccSBjorn Andersson 
9732edfcf2bSDaniel Mack 	vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
9748e84c258SEugene Krasnikov 	list_add(&vif_priv->list, &wcn->vif_list);
9758e84c258SEugene Krasnikov 	wcn36xx_smd_add_sta_self(wcn, vif);
9768e84c258SEugene Krasnikov 
97739efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
97839efc7ccSBjorn Andersson 
9798e84c258SEugene Krasnikov 	return 0;
9808e84c258SEugene Krasnikov }
9818e84c258SEugene Krasnikov 
9828e84c258SEugene Krasnikov static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
9838e84c258SEugene Krasnikov 			   struct ieee80211_sta *sta)
9848e84c258SEugene Krasnikov {
9858e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
986ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
987a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
9888e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n",
9898e84c258SEugene Krasnikov 		    vif, sta->addr);
9908e84c258SEugene Krasnikov 
99139efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
99239efc7ccSBjorn Andersson 
993e26dc173SBob Copeland 	spin_lock_init(&sta_priv->ampdu_lock);
9948e84c258SEugene Krasnikov 	sta_priv->vif = vif_priv;
9958e84c258SEugene Krasnikov 	/*
9968e84c258SEugene Krasnikov 	 * For STA mode HW will be configured on BSS_CHANGED_ASSOC because
9978e84c258SEugene Krasnikov 	 * at this stage AID is not available yet.
9988e84c258SEugene Krasnikov 	 */
9998e84c258SEugene Krasnikov 	if (NL80211_IFTYPE_STATION != vif->type) {
10008e84c258SEugene Krasnikov 		wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
10018e84c258SEugene Krasnikov 		sta_priv->aid = sta->aid;
10028e84c258SEugene Krasnikov 		wcn36xx_smd_config_sta(wcn, vif, sta);
10038e84c258SEugene Krasnikov 	}
100439efc7ccSBjorn Andersson 
100539efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
100639efc7ccSBjorn Andersson 
10078e84c258SEugene Krasnikov 	return 0;
10088e84c258SEugene Krasnikov }
10098e84c258SEugene Krasnikov 
10108e84c258SEugene Krasnikov static int wcn36xx_sta_remove(struct ieee80211_hw *hw,
10118e84c258SEugene Krasnikov 			      struct ieee80211_vif *vif,
10128e84c258SEugene Krasnikov 			      struct ieee80211_sta *sta)
10138e84c258SEugene Krasnikov {
10148e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
1015a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
10168e84c258SEugene Krasnikov 
10178e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n",
10188e84c258SEugene Krasnikov 		    vif, sta->addr, sta_priv->sta_index);
10198e84c258SEugene Krasnikov 
102039efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
102139efc7ccSBjorn Andersson 
10228e84c258SEugene Krasnikov 	wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index);
10238e84c258SEugene Krasnikov 	sta_priv->vif = NULL;
102439efc7ccSBjorn Andersson 
102539efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
102639efc7ccSBjorn Andersson 
10278e84c258SEugene Krasnikov 	return 0;
10288e84c258SEugene Krasnikov }
10298e84c258SEugene Krasnikov 
10308e84c258SEugene Krasnikov #ifdef CONFIG_PM
10318e84c258SEugene Krasnikov 
10328e84c258SEugene Krasnikov static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow)
10338e84c258SEugene Krasnikov {
10348e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
10358e84c258SEugene Krasnikov 
10368e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n");
10378e84c258SEugene Krasnikov 
10388e84c258SEugene Krasnikov 	flush_workqueue(wcn->hal_ind_wq);
10398e84c258SEugene Krasnikov 	wcn36xx_smd_set_power_params(wcn, true);
10408e84c258SEugene Krasnikov 	return 0;
10418e84c258SEugene Krasnikov }
10428e84c258SEugene Krasnikov 
10438e84c258SEugene Krasnikov static int wcn36xx_resume(struct ieee80211_hw *hw)
10448e84c258SEugene Krasnikov {
10458e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
10468e84c258SEugene Krasnikov 
10478e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n");
10488e84c258SEugene Krasnikov 
10498e84c258SEugene Krasnikov 	flush_workqueue(wcn->hal_ind_wq);
10508e84c258SEugene Krasnikov 	wcn36xx_smd_set_power_params(wcn, false);
10518e84c258SEugene Krasnikov 	return 0;
10528e84c258SEugene Krasnikov }
10538e84c258SEugene Krasnikov 
10548e84c258SEugene Krasnikov #endif
10558e84c258SEugene Krasnikov 
10568e84c258SEugene Krasnikov static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
10578e84c258SEugene Krasnikov 		    struct ieee80211_vif *vif,
105850ea05efSSara Sharon 		    struct ieee80211_ampdu_params *params)
10598e84c258SEugene Krasnikov {
10608e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
1061a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(params->sta);
106250ea05efSSara Sharon 	struct ieee80211_sta *sta = params->sta;
106350ea05efSSara Sharon 	enum ieee80211_ampdu_mlme_action action = params->action;
106450ea05efSSara Sharon 	u16 tid = params->tid;
106550ea05efSSara Sharon 	u16 *ssn = &params->ssn;
10668e84c258SEugene Krasnikov 
10678e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n",
10688e84c258SEugene Krasnikov 		    action, tid);
10698e84c258SEugene Krasnikov 
107039efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
107139efc7ccSBjorn Andersson 
10728e84c258SEugene Krasnikov 	switch (action) {
10738e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_RX_START:
10748e84c258SEugene Krasnikov 		sta_priv->tid = tid;
10758e84c258SEugene Krasnikov 		wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0,
10768e84c258SEugene Krasnikov 			get_sta_index(vif, sta_priv));
10778e84c258SEugene Krasnikov 		wcn36xx_smd_add_ba(wcn);
10788e84c258SEugene Krasnikov 		wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv));
10798e84c258SEugene Krasnikov 		break;
10808e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_RX_STOP:
10818e84c258SEugene Krasnikov 		wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv));
10828e84c258SEugene Krasnikov 		break;
10838e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_START:
1084e26dc173SBob Copeland 		spin_lock_bh(&sta_priv->ampdu_lock);
1085e26dc173SBob Copeland 		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
1086e26dc173SBob Copeland 		spin_unlock_bh(&sta_priv->ampdu_lock);
1087e26dc173SBob Copeland 
10888e84c258SEugene Krasnikov 		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
10898e84c258SEugene Krasnikov 		break;
10908e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_OPERATIONAL:
1091e26dc173SBob Copeland 		spin_lock_bh(&sta_priv->ampdu_lock);
1092e26dc173SBob Copeland 		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL;
1093e26dc173SBob Copeland 		spin_unlock_bh(&sta_priv->ampdu_lock);
1094e26dc173SBob Copeland 
10958e84c258SEugene Krasnikov 		wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1,
10968e84c258SEugene Krasnikov 			get_sta_index(vif, sta_priv));
10978e84c258SEugene Krasnikov 		break;
10988e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
10998e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
11008e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_STOP_CONT:
1101e26dc173SBob Copeland 		spin_lock_bh(&sta_priv->ampdu_lock);
1102e26dc173SBob Copeland 		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE;
1103e26dc173SBob Copeland 		spin_unlock_bh(&sta_priv->ampdu_lock);
1104e26dc173SBob Copeland 
11058e84c258SEugene Krasnikov 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
11068e84c258SEugene Krasnikov 		break;
11078e84c258SEugene Krasnikov 	default:
11088e84c258SEugene Krasnikov 		wcn36xx_err("Unknown AMPDU action\n");
11098e84c258SEugene Krasnikov 	}
11108e84c258SEugene Krasnikov 
111139efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
111239efc7ccSBjorn Andersson 
11138e84c258SEugene Krasnikov 	return 0;
11148e84c258SEugene Krasnikov }
11158e84c258SEugene Krasnikov 
11168e84c258SEugene Krasnikov static const struct ieee80211_ops wcn36xx_ops = {
11178e84c258SEugene Krasnikov 	.start			= wcn36xx_start,
11188e84c258SEugene Krasnikov 	.stop			= wcn36xx_stop,
11198e84c258SEugene Krasnikov 	.add_interface		= wcn36xx_add_interface,
11208e84c258SEugene Krasnikov 	.remove_interface	= wcn36xx_remove_interface,
11218e84c258SEugene Krasnikov #ifdef CONFIG_PM
11228e84c258SEugene Krasnikov 	.suspend		= wcn36xx_suspend,
11238e84c258SEugene Krasnikov 	.resume			= wcn36xx_resume,
11248e84c258SEugene Krasnikov #endif
11258e84c258SEugene Krasnikov 	.config			= wcn36xx_config,
112620a779edSPontus Fuchs 	.prepare_multicast	= wcn36xx_prepare_multicast,
11278e84c258SEugene Krasnikov 	.configure_filter       = wcn36xx_configure_filter,
11288e84c258SEugene Krasnikov 	.tx			= wcn36xx_tx,
11298e84c258SEugene Krasnikov 	.set_key		= wcn36xx_set_key,
113088603903SBjorn Andersson 	.hw_scan		= wcn36xx_hw_scan,
113103c95dbeSBjorn Andersson 	.cancel_hw_scan		= wcn36xx_cancel_hw_scan,
11328e84c258SEugene Krasnikov 	.bss_info_changed	= wcn36xx_bss_info_changed,
11338e84c258SEugene Krasnikov 	.set_rts_threshold	= wcn36xx_set_rts_threshold,
11348e84c258SEugene Krasnikov 	.sta_add		= wcn36xx_sta_add,
11358e84c258SEugene Krasnikov 	.sta_remove		= wcn36xx_sta_remove,
11368e84c258SEugene Krasnikov 	.ampdu_action		= wcn36xx_ampdu_action,
11378e84c258SEugene Krasnikov };
11388e84c258SEugene Krasnikov 
11398e84c258SEugene Krasnikov static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
11408e84c258SEugene Krasnikov {
11418e84c258SEugene Krasnikov 	int ret = 0;
11428e84c258SEugene Krasnikov 
11438e84c258SEugene Krasnikov 	static const u32 cipher_suites[] = {
11448e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_WEP40,
11458e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_WEP104,
11468e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_TKIP,
11478e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_CCMP,
11488e84c258SEugene Krasnikov 	};
11498e84c258SEugene Krasnikov 
115030686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, TIMING_BEACON_ONLY);
115130686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, AMPDU_AGGREGATION);
115230686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, CONNECTION_MONITOR);
115330686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, SUPPORTS_PS);
115430686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, SIGNAL_DBM);
115530686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL);
115688603903SBjorn Andersson 	ieee80211_hw_set(wcn->hw, SINGLE_SCAN_ON_ALL_BANDS);
11578e84c258SEugene Krasnikov 
11588e84c258SEugene Krasnikov 	wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
11598e84c258SEugene Krasnikov 		BIT(NL80211_IFTYPE_AP) |
11608e84c258SEugene Krasnikov 		BIT(NL80211_IFTYPE_ADHOC) |
11618e84c258SEugene Krasnikov 		BIT(NL80211_IFTYPE_MESH_POINT);
11628e84c258SEugene Krasnikov 
116357fbcce3SJohannes Berg 	wcn->hw->wiphy->bands[NL80211_BAND_2GHZ] = &wcn_band_2ghz;
1164fd52bdaeSLoic Poulain 	if (wcn->rf_id != RF_IRIS_WCN3620)
116557fbcce3SJohannes Berg 		wcn->hw->wiphy->bands[NL80211_BAND_5GHZ] = &wcn_band_5ghz;
11668e84c258SEugene Krasnikov 
116788603903SBjorn Andersson 	wcn->hw->wiphy->max_scan_ssids = WCN36XX_MAX_SCAN_SSIDS;
116888603903SBjorn Andersson 	wcn->hw->wiphy->max_scan_ie_len = WCN36XX_MAX_SCAN_IE_LEN;
116988603903SBjorn Andersson 
11708e84c258SEugene Krasnikov 	wcn->hw->wiphy->cipher_suites = cipher_suites;
11718e84c258SEugene Krasnikov 	wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
11728e84c258SEugene Krasnikov 
11738e84c258SEugene Krasnikov #ifdef CONFIG_PM
11748e84c258SEugene Krasnikov 	wcn->hw->wiphy->wowlan = &wowlan_support;
11758e84c258SEugene Krasnikov #endif
11768e84c258SEugene Krasnikov 
11778e84c258SEugene Krasnikov 	wcn->hw->max_listen_interval = 200;
11788e84c258SEugene Krasnikov 
11798e84c258SEugene Krasnikov 	wcn->hw->queues = 4;
11808e84c258SEugene Krasnikov 
11818e84c258SEugene Krasnikov 	SET_IEEE80211_DEV(wcn->hw, wcn->dev);
11828e84c258SEugene Krasnikov 
11838e84c258SEugene Krasnikov 	wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta);
11848e84c258SEugene Krasnikov 	wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif);
11858e84c258SEugene Krasnikov 
1186ae44b502SAndrew Zaborowski 	wiphy_ext_feature_set(wcn->hw->wiphy,
1187ae44b502SAndrew Zaborowski 			      NL80211_EXT_FEATURE_CQM_RSSI_LIST);
1188ae44b502SAndrew Zaborowski 
11898e84c258SEugene Krasnikov 	return ret;
11908e84c258SEugene Krasnikov }
11918e84c258SEugene Krasnikov 
11928e84c258SEugene Krasnikov static int wcn36xx_platform_get_resources(struct wcn36xx *wcn,
11938e84c258SEugene Krasnikov 					  struct platform_device *pdev)
11948e84c258SEugene Krasnikov {
119505ddce49SBjorn Andersson 	struct device_node *mmio_node;
1196fd52bdaeSLoic Poulain 	struct device_node *iris_node;
11978e84c258SEugene Krasnikov 	struct resource *res;
119805ddce49SBjorn Andersson 	int index;
119905ddce49SBjorn Andersson 	int ret;
120005ddce49SBjorn Andersson 
12018e84c258SEugene Krasnikov 	/* Set TX IRQ */
1202f303a931SBjorn Andersson 	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tx");
12038e84c258SEugene Krasnikov 	if (!res) {
12048e84c258SEugene Krasnikov 		wcn36xx_err("failed to get tx_irq\n");
12058e84c258SEugene Krasnikov 		return -ENOENT;
12068e84c258SEugene Krasnikov 	}
12078e84c258SEugene Krasnikov 	wcn->tx_irq = res->start;
12088e84c258SEugene Krasnikov 
12098e84c258SEugene Krasnikov 	/* Set RX IRQ */
1210f303a931SBjorn Andersson 	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "rx");
12118e84c258SEugene Krasnikov 	if (!res) {
12128e84c258SEugene Krasnikov 		wcn36xx_err("failed to get rx_irq\n");
12138e84c258SEugene Krasnikov 		return -ENOENT;
12148e84c258SEugene Krasnikov 	}
12158e84c258SEugene Krasnikov 	wcn->rx_irq = res->start;
12168e84c258SEugene Krasnikov 
1217f303a931SBjorn Andersson 	/* Acquire SMSM tx enable handle */
1218f303a931SBjorn Andersson 	wcn->tx_enable_state = qcom_smem_state_get(&pdev->dev,
1219f303a931SBjorn Andersson 			"tx-enable", &wcn->tx_enable_state_bit);
1220f303a931SBjorn Andersson 	if (IS_ERR(wcn->tx_enable_state)) {
1221f303a931SBjorn Andersson 		wcn36xx_err("failed to get tx-enable state\n");
1222f303a931SBjorn Andersson 		return PTR_ERR(wcn->tx_enable_state);
1223f303a931SBjorn Andersson 	}
1224f303a931SBjorn Andersson 
1225f303a931SBjorn Andersson 	/* Acquire SMSM tx rings empty handle */
1226f303a931SBjorn Andersson 	wcn->tx_rings_empty_state = qcom_smem_state_get(&pdev->dev,
1227f303a931SBjorn Andersson 			"tx-rings-empty", &wcn->tx_rings_empty_state_bit);
1228f303a931SBjorn Andersson 	if (IS_ERR(wcn->tx_rings_empty_state)) {
1229f303a931SBjorn Andersson 		wcn36xx_err("failed to get tx-rings-empty state\n");
1230f303a931SBjorn Andersson 		return PTR_ERR(wcn->tx_rings_empty_state);
1231f303a931SBjorn Andersson 	}
1232f303a931SBjorn Andersson 
123305ddce49SBjorn Andersson 	mmio_node = of_parse_phandle(pdev->dev.parent->of_node, "qcom,mmio", 0);
123405ddce49SBjorn Andersson 	if (!mmio_node) {
123505ddce49SBjorn Andersson 		wcn36xx_err("failed to acquire qcom,mmio reference\n");
123605ddce49SBjorn Andersson 		return -EINVAL;
12378e84c258SEugene Krasnikov 	}
123805ddce49SBjorn Andersson 
12396f10b4e1SBjorn Andersson 	wcn->is_pronto = !!of_device_is_compatible(mmio_node, "qcom,pronto");
12406f10b4e1SBjorn Andersson 
124105ddce49SBjorn Andersson 	/* Map the CCU memory */
124205ddce49SBjorn Andersson 	index = of_property_match_string(mmio_node, "reg-names", "ccu");
124305ddce49SBjorn Andersson 	wcn->ccu_base = of_iomap(mmio_node, index);
124405ddce49SBjorn Andersson 	if (!wcn->ccu_base) {
124505ddce49SBjorn Andersson 		wcn36xx_err("failed to map ccu memory\n");
124605ddce49SBjorn Andersson 		ret = -ENOMEM;
124705ddce49SBjorn Andersson 		goto put_mmio_node;
12488e84c258SEugene Krasnikov 	}
124905ddce49SBjorn Andersson 
125005ddce49SBjorn Andersson 	/* Map the DXE memory */
125105ddce49SBjorn Andersson 	index = of_property_match_string(mmio_node, "reg-names", "dxe");
125205ddce49SBjorn Andersson 	wcn->dxe_base = of_iomap(mmio_node, index);
125305ddce49SBjorn Andersson 	if (!wcn->dxe_base) {
125405ddce49SBjorn Andersson 		wcn36xx_err("failed to map dxe memory\n");
125505ddce49SBjorn Andersson 		ret = -ENOMEM;
125605ddce49SBjorn Andersson 		goto unmap_ccu;
125705ddce49SBjorn Andersson 	}
125805ddce49SBjorn Andersson 
1259fd52bdaeSLoic Poulain 	/* External RF module */
12601967c128SJohan Hovold 	iris_node = of_get_child_by_name(mmio_node, "iris");
1261fd52bdaeSLoic Poulain 	if (iris_node) {
1262fd52bdaeSLoic Poulain 		if (of_device_is_compatible(iris_node, "qcom,wcn3620"))
1263fd52bdaeSLoic Poulain 			wcn->rf_id = RF_IRIS_WCN3620;
1264fd52bdaeSLoic Poulain 		of_node_put(iris_node);
1265fd52bdaeSLoic Poulain 	}
1266fd52bdaeSLoic Poulain 
126705ddce49SBjorn Andersson 	of_node_put(mmio_node);
12688e84c258SEugene Krasnikov 	return 0;
126905ddce49SBjorn Andersson 
127005ddce49SBjorn Andersson unmap_ccu:
127105ddce49SBjorn Andersson 	iounmap(wcn->ccu_base);
127205ddce49SBjorn Andersson put_mmio_node:
127305ddce49SBjorn Andersson 	of_node_put(mmio_node);
127405ddce49SBjorn Andersson 	return ret;
12758e84c258SEugene Krasnikov }
12768e84c258SEugene Krasnikov 
12778e84c258SEugene Krasnikov static int wcn36xx_probe(struct platform_device *pdev)
12788e84c258SEugene Krasnikov {
12798e84c258SEugene Krasnikov 	struct ieee80211_hw *hw;
12808e84c258SEugene Krasnikov 	struct wcn36xx *wcn;
1281f303a931SBjorn Andersson 	void *wcnss;
12828e84c258SEugene Krasnikov 	int ret;
1283f303a931SBjorn Andersson 	const u8 *addr;
12848e84c258SEugene Krasnikov 
12858e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n");
12868e84c258SEugene Krasnikov 
1287f303a931SBjorn Andersson 	wcnss = dev_get_drvdata(pdev->dev.parent);
1288f303a931SBjorn Andersson 
12898e84c258SEugene Krasnikov 	hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops);
12908e84c258SEugene Krasnikov 	if (!hw) {
12918e84c258SEugene Krasnikov 		wcn36xx_err("failed to alloc hw\n");
12928e84c258SEugene Krasnikov 		ret = -ENOMEM;
12938e84c258SEugene Krasnikov 		goto out_err;
12948e84c258SEugene Krasnikov 	}
12958e84c258SEugene Krasnikov 	platform_set_drvdata(pdev, hw);
12968e84c258SEugene Krasnikov 	wcn = hw->priv;
12978e84c258SEugene Krasnikov 	wcn->hw = hw;
12988e84c258SEugene Krasnikov 	wcn->dev = &pdev->dev;
12996b8a127bSRamon Fried 	wcn->first_boot = true;
130039efc7ccSBjorn Andersson 	mutex_init(&wcn->conf_mutex);
13018e84c258SEugene Krasnikov 	mutex_init(&wcn->hal_mutex);
130288603903SBjorn Andersson 	mutex_init(&wcn->scan_lock);
130388603903SBjorn Andersson 
130488603903SBjorn Andersson 	INIT_WORK(&wcn->scan_work, wcn36xx_hw_scan_worker);
13058e84c258SEugene Krasnikov 
13065052de8dSBjorn Andersson 	wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process, hw);
1307f303a931SBjorn Andersson 	if (IS_ERR(wcn->smd_channel)) {
1308f303a931SBjorn Andersson 		wcn36xx_err("failed to open WLAN_CTRL channel\n");
1309f303a931SBjorn Andersson 		ret = PTR_ERR(wcn->smd_channel);
1310f303a931SBjorn Andersson 		goto out_wq;
1311f303a931SBjorn Andersson 	}
1312f303a931SBjorn Andersson 
1313f303a931SBjorn Andersson 	addr = of_get_property(pdev->dev.of_node, "local-mac-address", &ret);
1314f303a931SBjorn Andersson 	if (addr && ret != ETH_ALEN) {
1315f303a931SBjorn Andersson 		wcn36xx_err("invalid local-mac-address\n");
1316f303a931SBjorn Andersson 		ret = -EINVAL;
1317f303a931SBjorn Andersson 		goto out_wq;
1318f303a931SBjorn Andersson 	} else if (addr) {
13198e84c258SEugene Krasnikov 		wcn36xx_info("mac address: %pM\n", addr);
13208e84c258SEugene Krasnikov 		SET_IEEE80211_PERM_ADDR(wcn->hw, addr);
13218e84c258SEugene Krasnikov 	}
13228e84c258SEugene Krasnikov 
13238e84c258SEugene Krasnikov 	ret = wcn36xx_platform_get_resources(wcn, pdev);
13248e84c258SEugene Krasnikov 	if (ret)
13258e84c258SEugene Krasnikov 		goto out_wq;
13268e84c258SEugene Krasnikov 
13278e84c258SEugene Krasnikov 	wcn36xx_init_ieee80211(wcn);
13288e84c258SEugene Krasnikov 	ret = ieee80211_register_hw(wcn->hw);
13298e84c258SEugene Krasnikov 	if (ret)
13308e84c258SEugene Krasnikov 		goto out_unmap;
13318e84c258SEugene Krasnikov 
13328e84c258SEugene Krasnikov 	return 0;
13338e84c258SEugene Krasnikov 
13348e84c258SEugene Krasnikov out_unmap:
133505ddce49SBjorn Andersson 	iounmap(wcn->ccu_base);
133605ddce49SBjorn Andersson 	iounmap(wcn->dxe_base);
13378e84c258SEugene Krasnikov out_wq:
13388e84c258SEugene Krasnikov 	ieee80211_free_hw(hw);
13398e84c258SEugene Krasnikov out_err:
13408e84c258SEugene Krasnikov 	return ret;
13418e84c258SEugene Krasnikov }
1342f303a931SBjorn Andersson 
13438e84c258SEugene Krasnikov static int wcn36xx_remove(struct platform_device *pdev)
13448e84c258SEugene Krasnikov {
13458e84c258SEugene Krasnikov 	struct ieee80211_hw *hw = platform_get_drvdata(pdev);
13468e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
13478e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n");
13488e84c258SEugene Krasnikov 
13494bda7fafSPontus Fuchs 	release_firmware(wcn->nv);
13508e84c258SEugene Krasnikov 
13518e84c258SEugene Krasnikov 	ieee80211_unregister_hw(hw);
1352f303a931SBjorn Andersson 
1353f303a931SBjorn Andersson 	qcom_smem_state_put(wcn->tx_enable_state);
1354f303a931SBjorn Andersson 	qcom_smem_state_put(wcn->tx_rings_empty_state);
1355f303a931SBjorn Andersson 
1356efad8396SBjorn Andersson 	rpmsg_destroy_ept(wcn->smd_channel);
1357efad8396SBjorn Andersson 
135805ddce49SBjorn Andersson 	iounmap(wcn->dxe_base);
135905ddce49SBjorn Andersson 	iounmap(wcn->ccu_base);
1360d5362888SBjorn Andersson 
1361d5362888SBjorn Andersson 	mutex_destroy(&wcn->hal_mutex);
13628e84c258SEugene Krasnikov 	ieee80211_free_hw(hw);
13638e84c258SEugene Krasnikov 
13648e84c258SEugene Krasnikov 	return 0;
13658e84c258SEugene Krasnikov }
1366f303a931SBjorn Andersson 
1367f303a931SBjorn Andersson static const struct of_device_id wcn36xx_of_match[] = {
1368f303a931SBjorn Andersson 	{ .compatible = "qcom,wcnss-wlan" },
13698e84c258SEugene Krasnikov 	{}
13708e84c258SEugene Krasnikov };
1371f303a931SBjorn Andersson MODULE_DEVICE_TABLE(of, wcn36xx_of_match);
13728e84c258SEugene Krasnikov 
13738e84c258SEugene Krasnikov static struct platform_driver wcn36xx_driver = {
13748e84c258SEugene Krasnikov 	.probe      = wcn36xx_probe,
13758e84c258SEugene Krasnikov 	.remove     = wcn36xx_remove,
13768e84c258SEugene Krasnikov 	.driver         = {
13778e84c258SEugene Krasnikov 		.name   = "wcn36xx",
1378f303a931SBjorn Andersson 		.of_match_table = wcn36xx_of_match,
13798e84c258SEugene Krasnikov 	},
13808e84c258SEugene Krasnikov };
13818e84c258SEugene Krasnikov 
1382f303a931SBjorn Andersson module_platform_driver(wcn36xx_driver);
13838e84c258SEugene Krasnikov 
13848e84c258SEugene Krasnikov MODULE_LICENSE("Dual BSD/GPL");
13858e84c258SEugene Krasnikov MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com");
13868e84c258SEugene Krasnikov MODULE_FIRMWARE(WLAN_NV_FILE);
1387