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))
2642be6636aSPontus Fuchs 			wcn36xx_info("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 
3568e84c258SEugene Krasnikov 	wcn36xx_debugfs_exit(wcn);
3578e84c258SEugene Krasnikov 	wcn36xx_smd_stop(wcn);
3588e84c258SEugene Krasnikov 	wcn36xx_dxe_deinit(wcn);
3598e84c258SEugene Krasnikov 	wcn36xx_smd_close(wcn);
3608e84c258SEugene Krasnikov 
3618e84c258SEugene Krasnikov 	wcn36xx_dxe_free_mem_pools(wcn);
3628e84c258SEugene Krasnikov 	wcn36xx_dxe_free_ctl_blks(wcn);
3638e84c258SEugene Krasnikov 
3648e84c258SEugene Krasnikov 	kfree(wcn->hal_buf);
3658e84c258SEugene Krasnikov }
3668e84c258SEugene Krasnikov 
3678e84c258SEugene Krasnikov static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed)
3688e84c258SEugene Krasnikov {
3698e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
3708e84c258SEugene Krasnikov 	struct ieee80211_vif *vif = NULL;
3718e84c258SEugene Krasnikov 	struct wcn36xx_vif *tmp;
3728e84c258SEugene Krasnikov 
3738e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed);
3748e84c258SEugene Krasnikov 
37539efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
37639efc7ccSBjorn Andersson 
3778e84c258SEugene Krasnikov 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
3788e84c258SEugene Krasnikov 		int ch = WCN36XX_HW_CHANNEL(wcn);
3798e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n",
3808e84c258SEugene Krasnikov 			    ch);
3818e84c258SEugene Krasnikov 		list_for_each_entry(tmp, &wcn->vif_list, list) {
382ce75877fSPontus Fuchs 			vif = wcn36xx_priv_to_vif(tmp);
3838e84c258SEugene Krasnikov 			wcn36xx_smd_switch_channel(wcn, vif, ch);
3848e84c258SEugene Krasnikov 		}
3858e84c258SEugene Krasnikov 	}
3868e84c258SEugene Krasnikov 
3870856655aSLoic Poulain 	if (changed & IEEE80211_CONF_CHANGE_PS) {
3880856655aSLoic Poulain 		list_for_each_entry(tmp, &wcn->vif_list, list) {
3890856655aSLoic Poulain 			vif = wcn36xx_priv_to_vif(tmp);
3900856655aSLoic Poulain 			if (hw->conf.flags & IEEE80211_CONF_PS) {
3910856655aSLoic Poulain 				if (vif->bss_conf.ps) /* ps allowed ? */
3920856655aSLoic Poulain 					wcn36xx_pmc_enter_bmps_state(wcn, vif);
3930856655aSLoic Poulain 			} else {
3940856655aSLoic Poulain 				wcn36xx_pmc_exit_bmps_state(wcn, vif);
3950856655aSLoic Poulain 			}
3960856655aSLoic Poulain 		}
3970856655aSLoic Poulain 	}
3980856655aSLoic Poulain 
39939efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
40039efc7ccSBjorn Andersson 
4018e84c258SEugene Krasnikov 	return 0;
4028e84c258SEugene Krasnikov }
4038e84c258SEugene Krasnikov 
4048e84c258SEugene Krasnikov static void wcn36xx_configure_filter(struct ieee80211_hw *hw,
4058e84c258SEugene Krasnikov 				     unsigned int changed,
4068e84c258SEugene Krasnikov 				     unsigned int *total, u64 multicast)
4078e84c258SEugene Krasnikov {
40820a779edSPontus Fuchs 	struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp;
40920a779edSPontus Fuchs 	struct wcn36xx *wcn = hw->priv;
41020a779edSPontus Fuchs 	struct wcn36xx_vif *tmp;
41120a779edSPontus Fuchs 	struct ieee80211_vif *vif = NULL;
41220a779edSPontus Fuchs 
4138e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n");
4148e84c258SEugene Krasnikov 
41539efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
41639efc7ccSBjorn Andersson 
41720a779edSPontus Fuchs 	*total &= FIF_ALLMULTI;
41820a779edSPontus Fuchs 
41920a779edSPontus Fuchs 	fp = (void *)(unsigned long)multicast;
42020a779edSPontus Fuchs 	list_for_each_entry(tmp, &wcn->vif_list, list) {
42120a779edSPontus Fuchs 		vif = wcn36xx_priv_to_vif(tmp);
42220a779edSPontus Fuchs 
42320a779edSPontus Fuchs 		/* FW handles MC filtering only when connected as STA */
42420a779edSPontus Fuchs 		if (*total & FIF_ALLMULTI)
42520a779edSPontus Fuchs 			wcn36xx_smd_set_mc_list(wcn, vif, NULL);
42620a779edSPontus Fuchs 		else if (NL80211_IFTYPE_STATION == vif->type && tmp->sta_assoc)
42720a779edSPontus Fuchs 			wcn36xx_smd_set_mc_list(wcn, vif, fp);
42820a779edSPontus Fuchs 	}
42939efc7ccSBjorn Andersson 
43039efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
43120a779edSPontus Fuchs 	kfree(fp);
43220a779edSPontus Fuchs }
43320a779edSPontus Fuchs 
43420a779edSPontus Fuchs static u64 wcn36xx_prepare_multicast(struct ieee80211_hw *hw,
43520a779edSPontus Fuchs 				     struct netdev_hw_addr_list *mc_list)
43620a779edSPontus Fuchs {
43720a779edSPontus Fuchs 	struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp;
43820a779edSPontus Fuchs 	struct netdev_hw_addr *ha;
43920a779edSPontus Fuchs 
44020a779edSPontus Fuchs 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac prepare multicast list\n");
44120a779edSPontus Fuchs 	fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
44220a779edSPontus Fuchs 	if (!fp) {
44320a779edSPontus Fuchs 		wcn36xx_err("Out of memory setting filters.\n");
44420a779edSPontus Fuchs 		return 0;
44520a779edSPontus Fuchs 	}
44620a779edSPontus Fuchs 
44720a779edSPontus Fuchs 	fp->mc_addr_count = 0;
44820a779edSPontus Fuchs 	/* update multicast filtering parameters */
44920a779edSPontus Fuchs 	if (netdev_hw_addr_list_count(mc_list) <=
45020a779edSPontus Fuchs 	    WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS) {
45120a779edSPontus Fuchs 		netdev_hw_addr_list_for_each(ha, mc_list) {
45220a779edSPontus Fuchs 			memcpy(fp->mc_addr[fp->mc_addr_count],
45320a779edSPontus Fuchs 					ha->addr, ETH_ALEN);
45420a779edSPontus Fuchs 			fp->mc_addr_count++;
45520a779edSPontus Fuchs 		}
45620a779edSPontus Fuchs 	}
45720a779edSPontus Fuchs 
45820a779edSPontus Fuchs 	return (u64)(unsigned long)fp;
4598e84c258SEugene Krasnikov }
4608e84c258SEugene Krasnikov 
4618e84c258SEugene Krasnikov static void wcn36xx_tx(struct ieee80211_hw *hw,
4628e84c258SEugene Krasnikov 		       struct ieee80211_tx_control *control,
4638e84c258SEugene Krasnikov 		       struct sk_buff *skb)
4648e84c258SEugene Krasnikov {
4658e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
4668e84c258SEugene Krasnikov 	struct wcn36xx_sta *sta_priv = NULL;
4678e84c258SEugene Krasnikov 
4688e84c258SEugene Krasnikov 	if (control->sta)
469a92e4696SPontus Fuchs 		sta_priv = wcn36xx_sta_to_priv(control->sta);
4708e84c258SEugene Krasnikov 
4718e84c258SEugene Krasnikov 	if (wcn36xx_start_tx(wcn, sta_priv, skb))
4728e84c258SEugene Krasnikov 		ieee80211_free_txskb(wcn->hw, skb);
4738e84c258SEugene Krasnikov }
4748e84c258SEugene Krasnikov 
4758e84c258SEugene Krasnikov static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
4768e84c258SEugene Krasnikov 			   struct ieee80211_vif *vif,
4778e84c258SEugene Krasnikov 			   struct ieee80211_sta *sta,
4788e84c258SEugene Krasnikov 			   struct ieee80211_key_conf *key_conf)
4798e84c258SEugene Krasnikov {
4808e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
481ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
48281c69263SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
4838e84c258SEugene Krasnikov 	int ret = 0;
4848e84c258SEugene Krasnikov 	u8 key[WLAN_MAX_KEY_LEN];
4858e84c258SEugene Krasnikov 
4868e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key\n");
4878e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x\n",
4888e84c258SEugene Krasnikov 		    cmd, key_conf->cipher, key_conf->keyidx,
4898e84c258SEugene Krasnikov 		    key_conf->keylen, key_conf->flags);
4908e84c258SEugene Krasnikov 	wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: ",
4918e84c258SEugene Krasnikov 			 key_conf->key,
4928e84c258SEugene Krasnikov 			 key_conf->keylen);
4938e84c258SEugene Krasnikov 
49439efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
49539efc7ccSBjorn Andersson 
4968e84c258SEugene Krasnikov 	switch (key_conf->cipher) {
4978e84c258SEugene Krasnikov 	case WLAN_CIPHER_SUITE_WEP40:
4988e84c258SEugene Krasnikov 		vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40;
4998e84c258SEugene Krasnikov 		break;
5008e84c258SEugene Krasnikov 	case WLAN_CIPHER_SUITE_WEP104:
5018e84c258SEugene Krasnikov 		vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40;
5028e84c258SEugene Krasnikov 		break;
5038e84c258SEugene Krasnikov 	case WLAN_CIPHER_SUITE_CCMP:
5048e84c258SEugene Krasnikov 		vif_priv->encrypt_type = WCN36XX_HAL_ED_CCMP;
5058e84c258SEugene Krasnikov 		break;
5068e84c258SEugene Krasnikov 	case WLAN_CIPHER_SUITE_TKIP:
5078e84c258SEugene Krasnikov 		vif_priv->encrypt_type = WCN36XX_HAL_ED_TKIP;
5088e84c258SEugene Krasnikov 		break;
5098e84c258SEugene Krasnikov 	default:
5108e84c258SEugene Krasnikov 		wcn36xx_err("Unsupported key type 0x%x\n",
5118e84c258SEugene Krasnikov 			      key_conf->cipher);
5128e84c258SEugene Krasnikov 		ret = -EOPNOTSUPP;
5138e84c258SEugene Krasnikov 		goto out;
5148e84c258SEugene Krasnikov 	}
5158e84c258SEugene Krasnikov 
5168e84c258SEugene Krasnikov 	switch (cmd) {
5178e84c258SEugene Krasnikov 	case SET_KEY:
5188e84c258SEugene Krasnikov 		if (WCN36XX_HAL_ED_TKIP == vif_priv->encrypt_type) {
5198e84c258SEugene Krasnikov 			/*
5208e84c258SEugene Krasnikov 			 * Supplicant is sending key in the wrong order:
5218e84c258SEugene Krasnikov 			 * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b)
5228e84c258SEugene Krasnikov 			 * but HW expects it to be in the order as described in
5238e84c258SEugene Krasnikov 			 * IEEE 802.11 spec (see chapter 11.7) like this:
5248e84c258SEugene Krasnikov 			 * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b)
5258e84c258SEugene Krasnikov 			 */
5268e84c258SEugene Krasnikov 			memcpy(key, key_conf->key, 16);
5278e84c258SEugene Krasnikov 			memcpy(key + 16, key_conf->key + 24, 8);
5288e84c258SEugene Krasnikov 			memcpy(key + 24, key_conf->key + 16, 8);
5298e84c258SEugene Krasnikov 		} else {
5308e84c258SEugene Krasnikov 			memcpy(key, key_conf->key, key_conf->keylen);
5318e84c258SEugene Krasnikov 		}
5328e84c258SEugene Krasnikov 
5338e84c258SEugene Krasnikov 		if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) {
5348e84c258SEugene Krasnikov 			sta_priv->is_data_encrypted = true;
5358e84c258SEugene Krasnikov 			/* Reconfigure bss with encrypt_type */
5368e84c258SEugene Krasnikov 			if (NL80211_IFTYPE_STATION == vif->type)
5378e84c258SEugene Krasnikov 				wcn36xx_smd_config_bss(wcn,
5388e84c258SEugene Krasnikov 						       vif,
5398e84c258SEugene Krasnikov 						       sta,
5408e84c258SEugene Krasnikov 						       sta->addr,
5418e84c258SEugene Krasnikov 						       true);
5428e84c258SEugene Krasnikov 
5438e84c258SEugene Krasnikov 			wcn36xx_smd_set_stakey(wcn,
5448e84c258SEugene Krasnikov 				vif_priv->encrypt_type,
5458e84c258SEugene Krasnikov 				key_conf->keyidx,
5468e84c258SEugene Krasnikov 				key_conf->keylen,
5478e84c258SEugene Krasnikov 				key,
5488e84c258SEugene Krasnikov 				get_sta_index(vif, sta_priv));
5498e84c258SEugene Krasnikov 		} else {
5508e84c258SEugene Krasnikov 			wcn36xx_smd_set_bsskey(wcn,
5518e84c258SEugene Krasnikov 				vif_priv->encrypt_type,
5528e84c258SEugene Krasnikov 				key_conf->keyidx,
5538e84c258SEugene Krasnikov 				key_conf->keylen,
5548e84c258SEugene Krasnikov 				key);
5558e84c258SEugene Krasnikov 			if ((WLAN_CIPHER_SUITE_WEP40 == key_conf->cipher) ||
5568e84c258SEugene Krasnikov 			    (WLAN_CIPHER_SUITE_WEP104 == key_conf->cipher)) {
5578e84c258SEugene Krasnikov 				sta_priv->is_data_encrypted = true;
5588e84c258SEugene Krasnikov 				wcn36xx_smd_set_stakey(wcn,
5598e84c258SEugene Krasnikov 					vif_priv->encrypt_type,
5608e84c258SEugene Krasnikov 					key_conf->keyidx,
5618e84c258SEugene Krasnikov 					key_conf->keylen,
5628e84c258SEugene Krasnikov 					key,
5638e84c258SEugene Krasnikov 					get_sta_index(vif, sta_priv));
5648e84c258SEugene Krasnikov 			}
5658e84c258SEugene Krasnikov 		}
5668e84c258SEugene Krasnikov 		break;
5678e84c258SEugene Krasnikov 	case DISABLE_KEY:
5688e84c258SEugene Krasnikov 		if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) {
5692716a8acSPontus Fuchs 			vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
5708e84c258SEugene Krasnikov 			wcn36xx_smd_remove_bsskey(wcn,
5718e84c258SEugene Krasnikov 				vif_priv->encrypt_type,
5728e84c258SEugene Krasnikov 				key_conf->keyidx);
5738e84c258SEugene Krasnikov 		} else {
5748e84c258SEugene Krasnikov 			sta_priv->is_data_encrypted = false;
5758e84c258SEugene Krasnikov 			/* do not remove key if disassociated */
5768e84c258SEugene Krasnikov 			if (sta_priv->aid)
5778e84c258SEugene Krasnikov 				wcn36xx_smd_remove_stakey(wcn,
5788e84c258SEugene Krasnikov 					vif_priv->encrypt_type,
5798e84c258SEugene Krasnikov 					key_conf->keyidx,
5808e84c258SEugene Krasnikov 					get_sta_index(vif, sta_priv));
5818e84c258SEugene Krasnikov 		}
5828e84c258SEugene Krasnikov 		break;
5838e84c258SEugene Krasnikov 	default:
5848e84c258SEugene Krasnikov 		wcn36xx_err("Unsupported key cmd 0x%x\n", cmd);
5858e84c258SEugene Krasnikov 		ret = -EOPNOTSUPP;
5868e84c258SEugene Krasnikov 		goto out;
5878e84c258SEugene Krasnikov 	}
5888e84c258SEugene Krasnikov 
5898e84c258SEugene Krasnikov out:
59039efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
59139efc7ccSBjorn Andersson 
5928e84c258SEugene Krasnikov 	return ret;
5938e84c258SEugene Krasnikov }
5948e84c258SEugene Krasnikov 
59588603903SBjorn Andersson static void wcn36xx_hw_scan_worker(struct work_struct *work)
5968e84c258SEugene Krasnikov {
59788603903SBjorn Andersson 	struct wcn36xx *wcn = container_of(work, struct wcn36xx, scan_work);
59888603903SBjorn Andersson 	struct cfg80211_scan_request *req = wcn->scan_req;
59988603903SBjorn Andersson 	u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX];
60088603903SBjorn Andersson 	struct cfg80211_scan_info scan_info = {};
60103c95dbeSBjorn Andersson 	bool aborted = false;
60288603903SBjorn Andersson 	int i;
60388603903SBjorn Andersson 
60488603903SBjorn Andersson 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 scan %d channels worker\n", req->n_channels);
60588603903SBjorn Andersson 
60688603903SBjorn Andersson 	for (i = 0; i < req->n_channels; i++)
60788603903SBjorn Andersson 		channels[i] = req->channels[i]->hw_value;
60888603903SBjorn Andersson 
60988603903SBjorn Andersson 	wcn36xx_smd_update_scan_params(wcn, channels, req->n_channels);
6108e84c258SEugene Krasnikov 
6118e84c258SEugene Krasnikov 	wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN);
61288603903SBjorn Andersson 	for (i = 0; i < req->n_channels; i++) {
61303c95dbeSBjorn Andersson 		mutex_lock(&wcn->scan_lock);
61403c95dbeSBjorn Andersson 		aborted = wcn->scan_aborted;
61503c95dbeSBjorn Andersson 		mutex_unlock(&wcn->scan_lock);
61603c95dbeSBjorn Andersson 
61703c95dbeSBjorn Andersson 		if (aborted)
61803c95dbeSBjorn Andersson 			break;
61903c95dbeSBjorn Andersson 
62088603903SBjorn Andersson 		wcn->scan_freq = req->channels[i]->center_freq;
62188603903SBjorn Andersson 		wcn->scan_band = req->channels[i]->band;
62288603903SBjorn Andersson 
62388603903SBjorn Andersson 		wcn36xx_smd_start_scan(wcn, req->channels[i]->hw_value);
62488603903SBjorn Andersson 		msleep(30);
62588603903SBjorn Andersson 		wcn36xx_smd_end_scan(wcn, req->channels[i]->hw_value);
62688603903SBjorn Andersson 
62788603903SBjorn Andersson 		wcn->scan_freq = 0;
62888603903SBjorn Andersson 	}
62988603903SBjorn Andersson 	wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN);
63088603903SBjorn Andersson 
63103c95dbeSBjorn Andersson 	scan_info.aborted = aborted;
63288603903SBjorn Andersson 	ieee80211_scan_completed(wcn->hw, &scan_info);
63388603903SBjorn Andersson 
63488603903SBjorn Andersson 	mutex_lock(&wcn->scan_lock);
63588603903SBjorn Andersson 	wcn->scan_req = NULL;
63688603903SBjorn Andersson 	mutex_unlock(&wcn->scan_lock);
6378e84c258SEugene Krasnikov }
6388e84c258SEugene Krasnikov 
63988603903SBjorn Andersson static int wcn36xx_hw_scan(struct ieee80211_hw *hw,
64088603903SBjorn Andersson 			   struct ieee80211_vif *vif,
64188603903SBjorn Andersson 			   struct ieee80211_scan_request *hw_req)
6428e84c258SEugene Krasnikov {
6438e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
64488603903SBjorn Andersson 	mutex_lock(&wcn->scan_lock);
64588603903SBjorn Andersson 	if (wcn->scan_req) {
64688603903SBjorn Andersson 		mutex_unlock(&wcn->scan_lock);
64788603903SBjorn Andersson 		return -EBUSY;
64888603903SBjorn Andersson 	}
64903c95dbeSBjorn Andersson 
65003c95dbeSBjorn Andersson 	wcn->scan_aborted = false;
65188603903SBjorn Andersson 	wcn->scan_req = &hw_req->req;
6522f3bef4bSLoic Poulain 
65388603903SBjorn Andersson 	mutex_unlock(&wcn->scan_lock);
65488603903SBjorn Andersson 
6552f3bef4bSLoic Poulain 	if (!get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) {
6562f3bef4bSLoic Poulain 		/* legacy manual/sw scan */
65788603903SBjorn Andersson 		schedule_work(&wcn->scan_work);
65888603903SBjorn Andersson 		return 0;
6598e84c258SEugene Krasnikov 	}
6608e84c258SEugene Krasnikov 
6612f3bef4bSLoic Poulain 	return wcn36xx_smd_start_hw_scan(wcn, vif, &hw_req->req);
6622f3bef4bSLoic Poulain }
6632f3bef4bSLoic Poulain 
66403c95dbeSBjorn Andersson static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw,
66503c95dbeSBjorn Andersson 				   struct ieee80211_vif *vif)
66603c95dbeSBjorn Andersson {
66703c95dbeSBjorn Andersson 	struct wcn36xx *wcn = hw->priv;
66803c95dbeSBjorn Andersson 
66903c95dbeSBjorn Andersson 	mutex_lock(&wcn->scan_lock);
67003c95dbeSBjorn Andersson 	wcn->scan_aborted = true;
67103c95dbeSBjorn Andersson 	mutex_unlock(&wcn->scan_lock);
67203c95dbeSBjorn Andersson 
6739bfd05e3SLoic Poulain 	/* ieee80211_scan_completed will be called on FW scan indication */
6749bfd05e3SLoic Poulain 	wcn36xx_smd_stop_hw_scan(wcn);
6759bfd05e3SLoic Poulain 
67603c95dbeSBjorn Andersson 	cancel_work_sync(&wcn->scan_work);
67703c95dbeSBjorn Andersson }
67803c95dbeSBjorn Andersson 
6798e84c258SEugene Krasnikov static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta,
68057fbcce3SJohannes Berg 					 enum nl80211_band band)
6818e84c258SEugene Krasnikov {
6828e84c258SEugene Krasnikov 	int i, size;
6838e84c258SEugene Krasnikov 	u16 *rates_table;
684a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
6858e84c258SEugene Krasnikov 	u32 rates = sta->supp_rates[band];
6868e84c258SEugene Krasnikov 
6878e84c258SEugene Krasnikov 	memset(&sta_priv->supported_rates, 0,
6888e84c258SEugene Krasnikov 		sizeof(sta_priv->supported_rates));
6898e84c258SEugene Krasnikov 	sta_priv->supported_rates.op_rate_mode = STA_11n;
6908e84c258SEugene Krasnikov 
6918e84c258SEugene Krasnikov 	size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates);
6928e84c258SEugene Krasnikov 	rates_table = sta_priv->supported_rates.dsss_rates;
69357fbcce3SJohannes Berg 	if (band == NL80211_BAND_2GHZ) {
6948e84c258SEugene Krasnikov 		for (i = 0; i < size; i++) {
6958e84c258SEugene Krasnikov 			if (rates & 0x01) {
6968e84c258SEugene Krasnikov 				rates_table[i] = wcn_2ghz_rates[i].hw_value;
6978e84c258SEugene Krasnikov 				rates = rates >> 1;
6988e84c258SEugene Krasnikov 			}
6998e84c258SEugene Krasnikov 		}
7008e84c258SEugene Krasnikov 	}
7018e84c258SEugene Krasnikov 
7028e84c258SEugene Krasnikov 	size = ARRAY_SIZE(sta_priv->supported_rates.ofdm_rates);
7038e84c258SEugene Krasnikov 	rates_table = sta_priv->supported_rates.ofdm_rates;
7048e84c258SEugene Krasnikov 	for (i = 0; i < size; i++) {
7058e84c258SEugene Krasnikov 		if (rates & 0x01) {
7068e84c258SEugene Krasnikov 			rates_table[i] = wcn_5ghz_rates[i].hw_value;
7078e84c258SEugene Krasnikov 			rates = rates >> 1;
7088e84c258SEugene Krasnikov 		}
7098e84c258SEugene Krasnikov 	}
7108e84c258SEugene Krasnikov 
7118e84c258SEugene Krasnikov 	if (sta->ht_cap.ht_supported) {
7128e84c258SEugene Krasnikov 		BUILD_BUG_ON(sizeof(sta->ht_cap.mcs.rx_mask) >
7138e84c258SEugene Krasnikov 			sizeof(sta_priv->supported_rates.supported_mcs_set));
7148e84c258SEugene Krasnikov 		memcpy(sta_priv->supported_rates.supported_mcs_set,
7158e84c258SEugene Krasnikov 		       sta->ht_cap.mcs.rx_mask,
7168e84c258SEugene Krasnikov 		       sizeof(sta->ht_cap.mcs.rx_mask));
7178e84c258SEugene Krasnikov 	}
7188e84c258SEugene Krasnikov }
7198e84c258SEugene Krasnikov void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates)
7208e84c258SEugene Krasnikov {
7218e84c258SEugene Krasnikov 	u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = {
7228e84c258SEugene Krasnikov 		HW_RATE_INDEX_6MBPS,
7238e84c258SEugene Krasnikov 		HW_RATE_INDEX_9MBPS,
7248e84c258SEugene Krasnikov 		HW_RATE_INDEX_12MBPS,
7258e84c258SEugene Krasnikov 		HW_RATE_INDEX_18MBPS,
7268e84c258SEugene Krasnikov 		HW_RATE_INDEX_24MBPS,
7278e84c258SEugene Krasnikov 		HW_RATE_INDEX_36MBPS,
7288e84c258SEugene Krasnikov 		HW_RATE_INDEX_48MBPS,
7298e84c258SEugene Krasnikov 		HW_RATE_INDEX_54MBPS
7308e84c258SEugene Krasnikov 	};
7318e84c258SEugene Krasnikov 	u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = {
7328e84c258SEugene Krasnikov 		HW_RATE_INDEX_1MBPS,
7338e84c258SEugene Krasnikov 		HW_RATE_INDEX_2MBPS,
7348e84c258SEugene Krasnikov 		HW_RATE_INDEX_5_5MBPS,
7358e84c258SEugene Krasnikov 		HW_RATE_INDEX_11MBPS
7368e84c258SEugene Krasnikov 	};
7378e84c258SEugene Krasnikov 
7388e84c258SEugene Krasnikov 	rates->op_rate_mode = STA_11n;
7398e84c258SEugene Krasnikov 	memcpy(rates->dsss_rates, dsss_rates,
7408e84c258SEugene Krasnikov 		sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES);
7418e84c258SEugene Krasnikov 	memcpy(rates->ofdm_rates, ofdm_rates,
7428e84c258SEugene Krasnikov 		sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES);
7438e84c258SEugene Krasnikov 	rates->supported_mcs_set[0] = 0xFF;
7448e84c258SEugene Krasnikov }
7458e84c258SEugene Krasnikov static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
7468e84c258SEugene Krasnikov 				     struct ieee80211_vif *vif,
7478e84c258SEugene Krasnikov 				     struct ieee80211_bss_conf *bss_conf,
7488e84c258SEugene Krasnikov 				     u32 changed)
7498e84c258SEugene Krasnikov {
7508e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
7518e84c258SEugene Krasnikov 	struct sk_buff *skb = NULL;
7528e84c258SEugene Krasnikov 	u16 tim_off, tim_len;
7538e84c258SEugene Krasnikov 	enum wcn36xx_hal_link_state link_state;
754ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
7558e84c258SEugene Krasnikov 
7568e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n",
7578e84c258SEugene Krasnikov 		    vif, changed);
7588e84c258SEugene Krasnikov 
75939efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
76039efc7ccSBjorn Andersson 
7618e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_BEACON_INFO) {
7628e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC,
7638e84c258SEugene Krasnikov 			    "mac bss changed dtim period %d\n",
7648e84c258SEugene Krasnikov 			    bss_conf->dtim_period);
7658e84c258SEugene Krasnikov 
7668e84c258SEugene Krasnikov 		vif_priv->dtim_period = bss_conf->dtim_period;
7678e84c258SEugene Krasnikov 	}
7688e84c258SEugene Krasnikov 
7698e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_BSSID) {
7708e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n",
7718e84c258SEugene Krasnikov 			    bss_conf->bssid);
7728e84c258SEugene Krasnikov 
7738e84c258SEugene Krasnikov 		if (!is_zero_ether_addr(bss_conf->bssid)) {
7748e84c258SEugene Krasnikov 			vif_priv->is_joining = true;
77590023c03SPontus Fuchs 			vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
7768e84c258SEugene Krasnikov 			wcn36xx_smd_join(wcn, bss_conf->bssid,
7778e84c258SEugene Krasnikov 					 vif->addr, WCN36XX_HW_CHANNEL(wcn));
7788e84c258SEugene Krasnikov 			wcn36xx_smd_config_bss(wcn, vif, NULL,
7798e84c258SEugene Krasnikov 					       bss_conf->bssid, false);
7808e84c258SEugene Krasnikov 		} else {
7818e84c258SEugene Krasnikov 			vif_priv->is_joining = false;
7828e84c258SEugene Krasnikov 			wcn36xx_smd_delete_bss(wcn, vif);
7832716a8acSPontus Fuchs 			vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
7848e84c258SEugene Krasnikov 		}
7858e84c258SEugene Krasnikov 	}
7868e84c258SEugene Krasnikov 
7878e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_SSID) {
7888e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC,
7898e84c258SEugene Krasnikov 			    "mac bss changed ssid\n");
7908e84c258SEugene Krasnikov 		wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid ",
7918e84c258SEugene Krasnikov 				 bss_conf->ssid, bss_conf->ssid_len);
7928e84c258SEugene Krasnikov 
7938e84c258SEugene Krasnikov 		vif_priv->ssid.length = bss_conf->ssid_len;
7948e84c258SEugene Krasnikov 		memcpy(&vif_priv->ssid.ssid,
7958e84c258SEugene Krasnikov 		       bss_conf->ssid,
7968e84c258SEugene Krasnikov 		       bss_conf->ssid_len);
7978e84c258SEugene Krasnikov 	}
7988e84c258SEugene Krasnikov 
7998e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_ASSOC) {
8008e84c258SEugene Krasnikov 		vif_priv->is_joining = false;
8018e84c258SEugene Krasnikov 		if (bss_conf->assoc) {
8028e84c258SEugene Krasnikov 			struct ieee80211_sta *sta;
8038e84c258SEugene Krasnikov 			struct wcn36xx_sta *sta_priv;
8048e84c258SEugene Krasnikov 
8058e84c258SEugene Krasnikov 			wcn36xx_dbg(WCN36XX_DBG_MAC,
8068e84c258SEugene Krasnikov 				    "mac assoc bss %pM vif %pM AID=%d\n",
8078e84c258SEugene Krasnikov 				     bss_conf->bssid,
8088e84c258SEugene Krasnikov 				     vif->addr,
8098e84c258SEugene Krasnikov 				     bss_conf->aid);
8108e84c258SEugene Krasnikov 
811043ce546SPontus Fuchs 			vif_priv->sta_assoc = true;
81239efc7ccSBjorn Andersson 
81339efc7ccSBjorn Andersson 			/*
81439efc7ccSBjorn Andersson 			 * Holding conf_mutex ensures mutal exclusion with
81539efc7ccSBjorn Andersson 			 * wcn36xx_sta_remove() and as such ensures that sta
81639efc7ccSBjorn Andersson 			 * won't be freed while we're operating on it. As such
81739efc7ccSBjorn Andersson 			 * we do not need to hold the rcu_read_lock().
81839efc7ccSBjorn Andersson 			 */
8198e84c258SEugene Krasnikov 			sta = ieee80211_find_sta(vif, bss_conf->bssid);
8208e84c258SEugene Krasnikov 			if (!sta) {
8218e84c258SEugene Krasnikov 				wcn36xx_err("sta %pM is not found\n",
8228e84c258SEugene Krasnikov 					      bss_conf->bssid);
8238e84c258SEugene Krasnikov 				goto out;
8248e84c258SEugene Krasnikov 			}
825a92e4696SPontus Fuchs 			sta_priv = wcn36xx_sta_to_priv(sta);
8268e84c258SEugene Krasnikov 
8278e84c258SEugene Krasnikov 			wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
8288e84c258SEugene Krasnikov 
8298e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn, bss_conf->bssid,
8308e84c258SEugene Krasnikov 				vif->addr,
8318e84c258SEugene Krasnikov 				WCN36XX_HAL_LINK_POSTASSOC_STATE);
8328e84c258SEugene Krasnikov 			wcn36xx_smd_config_bss(wcn, vif, sta,
8338e84c258SEugene Krasnikov 					       bss_conf->bssid,
8348e84c258SEugene Krasnikov 					       true);
8358e84c258SEugene Krasnikov 			sta_priv->aid = bss_conf->aid;
8368e84c258SEugene Krasnikov 			/*
8378e84c258SEugene Krasnikov 			 * config_sta must be called from  because this is the
8388e84c258SEugene Krasnikov 			 * place where AID is available.
8398e84c258SEugene Krasnikov 			 */
8408e84c258SEugene Krasnikov 			wcn36xx_smd_config_sta(wcn, vif, sta);
8418e84c258SEugene Krasnikov 		} else {
8428e84c258SEugene Krasnikov 			wcn36xx_dbg(WCN36XX_DBG_MAC,
8438e84c258SEugene Krasnikov 				    "disassociated bss %pM vif %pM AID=%d\n",
8448e84c258SEugene Krasnikov 				    bss_conf->bssid,
8458e84c258SEugene Krasnikov 				    vif->addr,
8468e84c258SEugene Krasnikov 				    bss_conf->aid);
847043ce546SPontus Fuchs 			vif_priv->sta_assoc = false;
8488e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn,
8498e84c258SEugene Krasnikov 						bss_conf->bssid,
8508e84c258SEugene Krasnikov 						vif->addr,
8518e84c258SEugene Krasnikov 						WCN36XX_HAL_LINK_IDLE_STATE);
8528e84c258SEugene Krasnikov 		}
8538e84c258SEugene Krasnikov 	}
8548e84c258SEugene Krasnikov 
8558e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_AP_PROBE_RESP) {
8568e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n");
8578e84c258SEugene Krasnikov 		skb = ieee80211_proberesp_get(hw, vif);
8588e84c258SEugene Krasnikov 		if (!skb) {
8598e84c258SEugene Krasnikov 			wcn36xx_err("failed to alloc probereq skb\n");
8608e84c258SEugene Krasnikov 			goto out;
8618e84c258SEugene Krasnikov 		}
8628e84c258SEugene Krasnikov 
8638e84c258SEugene Krasnikov 		wcn36xx_smd_update_proberesp_tmpl(wcn, vif, skb);
8648e84c258SEugene Krasnikov 		dev_kfree_skb(skb);
8658e84c258SEugene Krasnikov 	}
8668e84c258SEugene Krasnikov 
867b3e3f871SChun-Yeow Yeoh 	if (changed & BSS_CHANGED_BEACON_ENABLED ||
868b3e3f871SChun-Yeow Yeoh 	    changed & BSS_CHANGED_BEACON) {
8698e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC,
8708e84c258SEugene Krasnikov 			    "mac bss changed beacon enabled %d\n",
8718e84c258SEugene Krasnikov 			    bss_conf->enable_beacon);
8728e84c258SEugene Krasnikov 
8738e84c258SEugene Krasnikov 		if (bss_conf->enable_beacon) {
874908628dbSPontus Fuchs 			vif_priv->dtim_period = bss_conf->dtim_period;
87590023c03SPontus Fuchs 			vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
8768e84c258SEugene Krasnikov 			wcn36xx_smd_config_bss(wcn, vif, NULL,
8778e84c258SEugene Krasnikov 					       vif->addr, false);
8788e84c258SEugene Krasnikov 			skb = ieee80211_beacon_get_tim(hw, vif, &tim_off,
8798e84c258SEugene Krasnikov 						       &tim_len);
8808e84c258SEugene Krasnikov 			if (!skb) {
8818e84c258SEugene Krasnikov 				wcn36xx_err("failed to alloc beacon skb\n");
8828e84c258SEugene Krasnikov 				goto out;
8838e84c258SEugene Krasnikov 			}
8848e84c258SEugene Krasnikov 			wcn36xx_smd_send_beacon(wcn, vif, skb, tim_off, 0);
8858e84c258SEugene Krasnikov 			dev_kfree_skb(skb);
8868e84c258SEugene Krasnikov 
8878e84c258SEugene Krasnikov 			if (vif->type == NL80211_IFTYPE_ADHOC ||
8888e84c258SEugene Krasnikov 			    vif->type == NL80211_IFTYPE_MESH_POINT)
8898e84c258SEugene Krasnikov 				link_state = WCN36XX_HAL_LINK_IBSS_STATE;
8908e84c258SEugene Krasnikov 			else
8918e84c258SEugene Krasnikov 				link_state = WCN36XX_HAL_LINK_AP_STATE;
8928e84c258SEugene Krasnikov 
8938e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
8948e84c258SEugene Krasnikov 						link_state);
8958e84c258SEugene Krasnikov 		} else {
8965443918dSBjorn Andersson 			wcn36xx_smd_delete_bss(wcn, vif);
8978e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
8988e84c258SEugene Krasnikov 						WCN36XX_HAL_LINK_IDLE_STATE);
8998e84c258SEugene Krasnikov 		}
9008e84c258SEugene Krasnikov 	}
9018e84c258SEugene Krasnikov out:
90239efc7ccSBjorn Andersson 
90339efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
90439efc7ccSBjorn Andersson 
9058e84c258SEugene Krasnikov 	return;
9068e84c258SEugene Krasnikov }
9078e84c258SEugene Krasnikov 
9088e84c258SEugene Krasnikov /* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */
9098e84c258SEugene Krasnikov static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
9108e84c258SEugene Krasnikov {
9118e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
9128e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value);
9138e84c258SEugene Krasnikov 
91439efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
9158e84c258SEugene Krasnikov 	wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value);
91639efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
91739efc7ccSBjorn Andersson 
9188e84c258SEugene Krasnikov 	return 0;
9198e84c258SEugene Krasnikov }
9208e84c258SEugene Krasnikov 
9218e84c258SEugene Krasnikov static void wcn36xx_remove_interface(struct ieee80211_hw *hw,
9228e84c258SEugene Krasnikov 				     struct ieee80211_vif *vif)
9238e84c258SEugene Krasnikov {
9248e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
925ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
9268e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif);
9278e84c258SEugene Krasnikov 
92839efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
92939efc7ccSBjorn Andersson 
9308e84c258SEugene Krasnikov 	list_del(&vif_priv->list);
9318e84c258SEugene Krasnikov 	wcn36xx_smd_delete_sta_self(wcn, vif->addr);
93239efc7ccSBjorn Andersson 
93339efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
9348e84c258SEugene Krasnikov }
9358e84c258SEugene Krasnikov 
9368e84c258SEugene Krasnikov static int wcn36xx_add_interface(struct ieee80211_hw *hw,
9378e84c258SEugene Krasnikov 				 struct ieee80211_vif *vif)
9388e84c258SEugene Krasnikov {
9398e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
940ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
9418e84c258SEugene Krasnikov 
9428e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n",
9438e84c258SEugene Krasnikov 		    vif, vif->type);
9448e84c258SEugene Krasnikov 
9458e84c258SEugene Krasnikov 	if (!(NL80211_IFTYPE_STATION == vif->type ||
9468e84c258SEugene Krasnikov 	      NL80211_IFTYPE_AP == vif->type ||
9478e84c258SEugene Krasnikov 	      NL80211_IFTYPE_ADHOC == vif->type ||
9488e84c258SEugene Krasnikov 	      NL80211_IFTYPE_MESH_POINT == vif->type)) {
9498e84c258SEugene Krasnikov 		wcn36xx_warn("Unsupported interface type requested: %d\n",
9508e84c258SEugene Krasnikov 			     vif->type);
9518e84c258SEugene Krasnikov 		return -EOPNOTSUPP;
9528e84c258SEugene Krasnikov 	}
9538e84c258SEugene Krasnikov 
95439efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
95539efc7ccSBjorn Andersson 
9568e84c258SEugene Krasnikov 	list_add(&vif_priv->list, &wcn->vif_list);
9578e84c258SEugene Krasnikov 	wcn36xx_smd_add_sta_self(wcn, vif);
9588e84c258SEugene Krasnikov 
95939efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
96039efc7ccSBjorn Andersson 
9618e84c258SEugene Krasnikov 	return 0;
9628e84c258SEugene Krasnikov }
9638e84c258SEugene Krasnikov 
9648e84c258SEugene Krasnikov static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
9658e84c258SEugene Krasnikov 			   struct ieee80211_sta *sta)
9668e84c258SEugene Krasnikov {
9678e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
968ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
969a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
9708e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n",
9718e84c258SEugene Krasnikov 		    vif, sta->addr);
9728e84c258SEugene Krasnikov 
97339efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
97439efc7ccSBjorn Andersson 
975e26dc173SBob Copeland 	spin_lock_init(&sta_priv->ampdu_lock);
9768e84c258SEugene Krasnikov 	sta_priv->vif = vif_priv;
9778e84c258SEugene Krasnikov 	/*
9788e84c258SEugene Krasnikov 	 * For STA mode HW will be configured on BSS_CHANGED_ASSOC because
9798e84c258SEugene Krasnikov 	 * at this stage AID is not available yet.
9808e84c258SEugene Krasnikov 	 */
9818e84c258SEugene Krasnikov 	if (NL80211_IFTYPE_STATION != vif->type) {
9828e84c258SEugene Krasnikov 		wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
9838e84c258SEugene Krasnikov 		sta_priv->aid = sta->aid;
9848e84c258SEugene Krasnikov 		wcn36xx_smd_config_sta(wcn, vif, sta);
9858e84c258SEugene Krasnikov 	}
98639efc7ccSBjorn Andersson 
98739efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
98839efc7ccSBjorn Andersson 
9898e84c258SEugene Krasnikov 	return 0;
9908e84c258SEugene Krasnikov }
9918e84c258SEugene Krasnikov 
9928e84c258SEugene Krasnikov static int wcn36xx_sta_remove(struct ieee80211_hw *hw,
9938e84c258SEugene Krasnikov 			      struct ieee80211_vif *vif,
9948e84c258SEugene Krasnikov 			      struct ieee80211_sta *sta)
9958e84c258SEugene Krasnikov {
9968e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
997a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
9988e84c258SEugene Krasnikov 
9998e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n",
10008e84c258SEugene Krasnikov 		    vif, sta->addr, sta_priv->sta_index);
10018e84c258SEugene Krasnikov 
100239efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
100339efc7ccSBjorn Andersson 
10048e84c258SEugene Krasnikov 	wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index);
10058e84c258SEugene Krasnikov 	sta_priv->vif = NULL;
100639efc7ccSBjorn Andersson 
100739efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
100839efc7ccSBjorn Andersson 
10098e84c258SEugene Krasnikov 	return 0;
10108e84c258SEugene Krasnikov }
10118e84c258SEugene Krasnikov 
10128e84c258SEugene Krasnikov #ifdef CONFIG_PM
10138e84c258SEugene Krasnikov 
10148e84c258SEugene Krasnikov static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow)
10158e84c258SEugene Krasnikov {
10168e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
10178e84c258SEugene Krasnikov 
10188e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n");
10198e84c258SEugene Krasnikov 
10208e84c258SEugene Krasnikov 	flush_workqueue(wcn->hal_ind_wq);
10218e84c258SEugene Krasnikov 	wcn36xx_smd_set_power_params(wcn, true);
10228e84c258SEugene Krasnikov 	return 0;
10238e84c258SEugene Krasnikov }
10248e84c258SEugene Krasnikov 
10258e84c258SEugene Krasnikov static int wcn36xx_resume(struct ieee80211_hw *hw)
10268e84c258SEugene Krasnikov {
10278e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
10288e84c258SEugene Krasnikov 
10298e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n");
10308e84c258SEugene Krasnikov 
10318e84c258SEugene Krasnikov 	flush_workqueue(wcn->hal_ind_wq);
10328e84c258SEugene Krasnikov 	wcn36xx_smd_set_power_params(wcn, false);
10338e84c258SEugene Krasnikov 	return 0;
10348e84c258SEugene Krasnikov }
10358e84c258SEugene Krasnikov 
10368e84c258SEugene Krasnikov #endif
10378e84c258SEugene Krasnikov 
10388e84c258SEugene Krasnikov static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
10398e84c258SEugene Krasnikov 		    struct ieee80211_vif *vif,
104050ea05efSSara Sharon 		    struct ieee80211_ampdu_params *params)
10418e84c258SEugene Krasnikov {
10428e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
1043a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(params->sta);
104450ea05efSSara Sharon 	struct ieee80211_sta *sta = params->sta;
104550ea05efSSara Sharon 	enum ieee80211_ampdu_mlme_action action = params->action;
104650ea05efSSara Sharon 	u16 tid = params->tid;
104750ea05efSSara Sharon 	u16 *ssn = &params->ssn;
10488e84c258SEugene Krasnikov 
10498e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n",
10508e84c258SEugene Krasnikov 		    action, tid);
10518e84c258SEugene Krasnikov 
105239efc7ccSBjorn Andersson 	mutex_lock(&wcn->conf_mutex);
105339efc7ccSBjorn Andersson 
10548e84c258SEugene Krasnikov 	switch (action) {
10558e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_RX_START:
10568e84c258SEugene Krasnikov 		sta_priv->tid = tid;
10578e84c258SEugene Krasnikov 		wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0,
10588e84c258SEugene Krasnikov 			get_sta_index(vif, sta_priv));
10598e84c258SEugene Krasnikov 		wcn36xx_smd_add_ba(wcn);
10608e84c258SEugene Krasnikov 		wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv));
10618e84c258SEugene Krasnikov 		break;
10628e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_RX_STOP:
10638e84c258SEugene Krasnikov 		wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv));
10648e84c258SEugene Krasnikov 		break;
10658e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_START:
1066e26dc173SBob Copeland 		spin_lock_bh(&sta_priv->ampdu_lock);
1067e26dc173SBob Copeland 		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
1068e26dc173SBob Copeland 		spin_unlock_bh(&sta_priv->ampdu_lock);
1069e26dc173SBob Copeland 
10708e84c258SEugene Krasnikov 		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
10718e84c258SEugene Krasnikov 		break;
10728e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_OPERATIONAL:
1073e26dc173SBob Copeland 		spin_lock_bh(&sta_priv->ampdu_lock);
1074e26dc173SBob Copeland 		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL;
1075e26dc173SBob Copeland 		spin_unlock_bh(&sta_priv->ampdu_lock);
1076e26dc173SBob Copeland 
10778e84c258SEugene Krasnikov 		wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1,
10788e84c258SEugene Krasnikov 			get_sta_index(vif, sta_priv));
10798e84c258SEugene Krasnikov 		break;
10808e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
10818e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
10828e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_STOP_CONT:
1083e26dc173SBob Copeland 		spin_lock_bh(&sta_priv->ampdu_lock);
1084e26dc173SBob Copeland 		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE;
1085e26dc173SBob Copeland 		spin_unlock_bh(&sta_priv->ampdu_lock);
1086e26dc173SBob Copeland 
10878e84c258SEugene Krasnikov 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
10888e84c258SEugene Krasnikov 		break;
10898e84c258SEugene Krasnikov 	default:
10908e84c258SEugene Krasnikov 		wcn36xx_err("Unknown AMPDU action\n");
10918e84c258SEugene Krasnikov 	}
10928e84c258SEugene Krasnikov 
109339efc7ccSBjorn Andersson 	mutex_unlock(&wcn->conf_mutex);
109439efc7ccSBjorn Andersson 
10958e84c258SEugene Krasnikov 	return 0;
10968e84c258SEugene Krasnikov }
10978e84c258SEugene Krasnikov 
10988e84c258SEugene Krasnikov static const struct ieee80211_ops wcn36xx_ops = {
10998e84c258SEugene Krasnikov 	.start			= wcn36xx_start,
11008e84c258SEugene Krasnikov 	.stop			= wcn36xx_stop,
11018e84c258SEugene Krasnikov 	.add_interface		= wcn36xx_add_interface,
11028e84c258SEugene Krasnikov 	.remove_interface	= wcn36xx_remove_interface,
11038e84c258SEugene Krasnikov #ifdef CONFIG_PM
11048e84c258SEugene Krasnikov 	.suspend		= wcn36xx_suspend,
11058e84c258SEugene Krasnikov 	.resume			= wcn36xx_resume,
11068e84c258SEugene Krasnikov #endif
11078e84c258SEugene Krasnikov 	.config			= wcn36xx_config,
110820a779edSPontus Fuchs 	.prepare_multicast	= wcn36xx_prepare_multicast,
11098e84c258SEugene Krasnikov 	.configure_filter       = wcn36xx_configure_filter,
11108e84c258SEugene Krasnikov 	.tx			= wcn36xx_tx,
11118e84c258SEugene Krasnikov 	.set_key		= wcn36xx_set_key,
111288603903SBjorn Andersson 	.hw_scan		= wcn36xx_hw_scan,
111303c95dbeSBjorn Andersson 	.cancel_hw_scan		= wcn36xx_cancel_hw_scan,
11148e84c258SEugene Krasnikov 	.bss_info_changed	= wcn36xx_bss_info_changed,
11158e84c258SEugene Krasnikov 	.set_rts_threshold	= wcn36xx_set_rts_threshold,
11168e84c258SEugene Krasnikov 	.sta_add		= wcn36xx_sta_add,
11178e84c258SEugene Krasnikov 	.sta_remove		= wcn36xx_sta_remove,
11188e84c258SEugene Krasnikov 	.ampdu_action		= wcn36xx_ampdu_action,
11198e84c258SEugene Krasnikov };
11208e84c258SEugene Krasnikov 
11218e84c258SEugene Krasnikov static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
11228e84c258SEugene Krasnikov {
11238e84c258SEugene Krasnikov 	int ret = 0;
11248e84c258SEugene Krasnikov 
11258e84c258SEugene Krasnikov 	static const u32 cipher_suites[] = {
11268e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_WEP40,
11278e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_WEP104,
11288e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_TKIP,
11298e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_CCMP,
11308e84c258SEugene Krasnikov 	};
11318e84c258SEugene Krasnikov 
113230686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, TIMING_BEACON_ONLY);
113330686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, AMPDU_AGGREGATION);
113430686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, CONNECTION_MONITOR);
113530686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, SUPPORTS_PS);
113630686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, SIGNAL_DBM);
113730686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL);
113888603903SBjorn Andersson 	ieee80211_hw_set(wcn->hw, SINGLE_SCAN_ON_ALL_BANDS);
11398e84c258SEugene Krasnikov 
11408e84c258SEugene Krasnikov 	wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
11418e84c258SEugene Krasnikov 		BIT(NL80211_IFTYPE_AP) |
11428e84c258SEugene Krasnikov 		BIT(NL80211_IFTYPE_ADHOC) |
11438e84c258SEugene Krasnikov 		BIT(NL80211_IFTYPE_MESH_POINT);
11448e84c258SEugene Krasnikov 
114557fbcce3SJohannes Berg 	wcn->hw->wiphy->bands[NL80211_BAND_2GHZ] = &wcn_band_2ghz;
1146fd52bdaeSLoic Poulain 	if (wcn->rf_id != RF_IRIS_WCN3620)
114757fbcce3SJohannes Berg 		wcn->hw->wiphy->bands[NL80211_BAND_5GHZ] = &wcn_band_5ghz;
11488e84c258SEugene Krasnikov 
114988603903SBjorn Andersson 	wcn->hw->wiphy->max_scan_ssids = WCN36XX_MAX_SCAN_SSIDS;
115088603903SBjorn Andersson 	wcn->hw->wiphy->max_scan_ie_len = WCN36XX_MAX_SCAN_IE_LEN;
115188603903SBjorn Andersson 
11528e84c258SEugene Krasnikov 	wcn->hw->wiphy->cipher_suites = cipher_suites;
11538e84c258SEugene Krasnikov 	wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
11548e84c258SEugene Krasnikov 
11558e84c258SEugene Krasnikov 	wcn->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
11568e84c258SEugene Krasnikov 
11578e84c258SEugene Krasnikov #ifdef CONFIG_PM
11588e84c258SEugene Krasnikov 	wcn->hw->wiphy->wowlan = &wowlan_support;
11598e84c258SEugene Krasnikov #endif
11608e84c258SEugene Krasnikov 
11618e84c258SEugene Krasnikov 	wcn->hw->max_listen_interval = 200;
11628e84c258SEugene Krasnikov 
11638e84c258SEugene Krasnikov 	wcn->hw->queues = 4;
11648e84c258SEugene Krasnikov 
11658e84c258SEugene Krasnikov 	SET_IEEE80211_DEV(wcn->hw, wcn->dev);
11668e84c258SEugene Krasnikov 
11678e84c258SEugene Krasnikov 	wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta);
11688e84c258SEugene Krasnikov 	wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif);
11698e84c258SEugene Krasnikov 
1170ae44b502SAndrew Zaborowski 	wiphy_ext_feature_set(wcn->hw->wiphy,
1171ae44b502SAndrew Zaborowski 			      NL80211_EXT_FEATURE_CQM_RSSI_LIST);
1172ae44b502SAndrew Zaborowski 
11738e84c258SEugene Krasnikov 	return ret;
11748e84c258SEugene Krasnikov }
11758e84c258SEugene Krasnikov 
11768e84c258SEugene Krasnikov static int wcn36xx_platform_get_resources(struct wcn36xx *wcn,
11778e84c258SEugene Krasnikov 					  struct platform_device *pdev)
11788e84c258SEugene Krasnikov {
117905ddce49SBjorn Andersson 	struct device_node *mmio_node;
1180fd52bdaeSLoic Poulain 	struct device_node *iris_node;
11818e84c258SEugene Krasnikov 	struct resource *res;
118205ddce49SBjorn Andersson 	int index;
118305ddce49SBjorn Andersson 	int ret;
118405ddce49SBjorn Andersson 
11858e84c258SEugene Krasnikov 	/* Set TX IRQ */
1186f303a931SBjorn Andersson 	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tx");
11878e84c258SEugene Krasnikov 	if (!res) {
11888e84c258SEugene Krasnikov 		wcn36xx_err("failed to get tx_irq\n");
11898e84c258SEugene Krasnikov 		return -ENOENT;
11908e84c258SEugene Krasnikov 	}
11918e84c258SEugene Krasnikov 	wcn->tx_irq = res->start;
11928e84c258SEugene Krasnikov 
11938e84c258SEugene Krasnikov 	/* Set RX IRQ */
1194f303a931SBjorn Andersson 	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "rx");
11958e84c258SEugene Krasnikov 	if (!res) {
11968e84c258SEugene Krasnikov 		wcn36xx_err("failed to get rx_irq\n");
11978e84c258SEugene Krasnikov 		return -ENOENT;
11988e84c258SEugene Krasnikov 	}
11998e84c258SEugene Krasnikov 	wcn->rx_irq = res->start;
12008e84c258SEugene Krasnikov 
1201f303a931SBjorn Andersson 	/* Acquire SMSM tx enable handle */
1202f303a931SBjorn Andersson 	wcn->tx_enable_state = qcom_smem_state_get(&pdev->dev,
1203f303a931SBjorn Andersson 			"tx-enable", &wcn->tx_enable_state_bit);
1204f303a931SBjorn Andersson 	if (IS_ERR(wcn->tx_enable_state)) {
1205f303a931SBjorn Andersson 		wcn36xx_err("failed to get tx-enable state\n");
1206f303a931SBjorn Andersson 		return PTR_ERR(wcn->tx_enable_state);
1207f303a931SBjorn Andersson 	}
1208f303a931SBjorn Andersson 
1209f303a931SBjorn Andersson 	/* Acquire SMSM tx rings empty handle */
1210f303a931SBjorn Andersson 	wcn->tx_rings_empty_state = qcom_smem_state_get(&pdev->dev,
1211f303a931SBjorn Andersson 			"tx-rings-empty", &wcn->tx_rings_empty_state_bit);
1212f303a931SBjorn Andersson 	if (IS_ERR(wcn->tx_rings_empty_state)) {
1213f303a931SBjorn Andersson 		wcn36xx_err("failed to get tx-rings-empty state\n");
1214f303a931SBjorn Andersson 		return PTR_ERR(wcn->tx_rings_empty_state);
1215f303a931SBjorn Andersson 	}
1216f303a931SBjorn Andersson 
121705ddce49SBjorn Andersson 	mmio_node = of_parse_phandle(pdev->dev.parent->of_node, "qcom,mmio", 0);
121805ddce49SBjorn Andersson 	if (!mmio_node) {
121905ddce49SBjorn Andersson 		wcn36xx_err("failed to acquire qcom,mmio reference\n");
122005ddce49SBjorn Andersson 		return -EINVAL;
12218e84c258SEugene Krasnikov 	}
122205ddce49SBjorn Andersson 
12236f10b4e1SBjorn Andersson 	wcn->is_pronto = !!of_device_is_compatible(mmio_node, "qcom,pronto");
12246f10b4e1SBjorn Andersson 
122505ddce49SBjorn Andersson 	/* Map the CCU memory */
122605ddce49SBjorn Andersson 	index = of_property_match_string(mmio_node, "reg-names", "ccu");
122705ddce49SBjorn Andersson 	wcn->ccu_base = of_iomap(mmio_node, index);
122805ddce49SBjorn Andersson 	if (!wcn->ccu_base) {
122905ddce49SBjorn Andersson 		wcn36xx_err("failed to map ccu memory\n");
123005ddce49SBjorn Andersson 		ret = -ENOMEM;
123105ddce49SBjorn Andersson 		goto put_mmio_node;
12328e84c258SEugene Krasnikov 	}
123305ddce49SBjorn Andersson 
123405ddce49SBjorn Andersson 	/* Map the DXE memory */
123505ddce49SBjorn Andersson 	index = of_property_match_string(mmio_node, "reg-names", "dxe");
123605ddce49SBjorn Andersson 	wcn->dxe_base = of_iomap(mmio_node, index);
123705ddce49SBjorn Andersson 	if (!wcn->dxe_base) {
123805ddce49SBjorn Andersson 		wcn36xx_err("failed to map dxe memory\n");
123905ddce49SBjorn Andersson 		ret = -ENOMEM;
124005ddce49SBjorn Andersson 		goto unmap_ccu;
124105ddce49SBjorn Andersson 	}
124205ddce49SBjorn Andersson 
1243fd52bdaeSLoic Poulain 	/* External RF module */
12441967c128SJohan Hovold 	iris_node = of_get_child_by_name(mmio_node, "iris");
1245fd52bdaeSLoic Poulain 	if (iris_node) {
1246fd52bdaeSLoic Poulain 		if (of_device_is_compatible(iris_node, "qcom,wcn3620"))
1247fd52bdaeSLoic Poulain 			wcn->rf_id = RF_IRIS_WCN3620;
1248fd52bdaeSLoic Poulain 		of_node_put(iris_node);
1249fd52bdaeSLoic Poulain 	}
1250fd52bdaeSLoic Poulain 
125105ddce49SBjorn Andersson 	of_node_put(mmio_node);
12528e84c258SEugene Krasnikov 	return 0;
125305ddce49SBjorn Andersson 
125405ddce49SBjorn Andersson unmap_ccu:
125505ddce49SBjorn Andersson 	iounmap(wcn->ccu_base);
125605ddce49SBjorn Andersson put_mmio_node:
125705ddce49SBjorn Andersson 	of_node_put(mmio_node);
125805ddce49SBjorn Andersson 	return ret;
12598e84c258SEugene Krasnikov }
12608e84c258SEugene Krasnikov 
12618e84c258SEugene Krasnikov static int wcn36xx_probe(struct platform_device *pdev)
12628e84c258SEugene Krasnikov {
12638e84c258SEugene Krasnikov 	struct ieee80211_hw *hw;
12648e84c258SEugene Krasnikov 	struct wcn36xx *wcn;
1265f303a931SBjorn Andersson 	void *wcnss;
12668e84c258SEugene Krasnikov 	int ret;
1267f303a931SBjorn Andersson 	const u8 *addr;
12688e84c258SEugene Krasnikov 
12698e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n");
12708e84c258SEugene Krasnikov 
1271f303a931SBjorn Andersson 	wcnss = dev_get_drvdata(pdev->dev.parent);
1272f303a931SBjorn Andersson 
12738e84c258SEugene Krasnikov 	hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops);
12748e84c258SEugene Krasnikov 	if (!hw) {
12758e84c258SEugene Krasnikov 		wcn36xx_err("failed to alloc hw\n");
12768e84c258SEugene Krasnikov 		ret = -ENOMEM;
12778e84c258SEugene Krasnikov 		goto out_err;
12788e84c258SEugene Krasnikov 	}
12798e84c258SEugene Krasnikov 	platform_set_drvdata(pdev, hw);
12808e84c258SEugene Krasnikov 	wcn = hw->priv;
12818e84c258SEugene Krasnikov 	wcn->hw = hw;
12828e84c258SEugene Krasnikov 	wcn->dev = &pdev->dev;
128339efc7ccSBjorn Andersson 	mutex_init(&wcn->conf_mutex);
12848e84c258SEugene Krasnikov 	mutex_init(&wcn->hal_mutex);
128588603903SBjorn Andersson 	mutex_init(&wcn->scan_lock);
128688603903SBjorn Andersson 
128788603903SBjorn Andersson 	INIT_WORK(&wcn->scan_work, wcn36xx_hw_scan_worker);
12888e84c258SEugene Krasnikov 
12895052de8dSBjorn Andersson 	wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process, hw);
1290f303a931SBjorn Andersson 	if (IS_ERR(wcn->smd_channel)) {
1291f303a931SBjorn Andersson 		wcn36xx_err("failed to open WLAN_CTRL channel\n");
1292f303a931SBjorn Andersson 		ret = PTR_ERR(wcn->smd_channel);
1293f303a931SBjorn Andersson 		goto out_wq;
1294f303a931SBjorn Andersson 	}
1295f303a931SBjorn Andersson 
1296f303a931SBjorn Andersson 	addr = of_get_property(pdev->dev.of_node, "local-mac-address", &ret);
1297f303a931SBjorn Andersson 	if (addr && ret != ETH_ALEN) {
1298f303a931SBjorn Andersson 		wcn36xx_err("invalid local-mac-address\n");
1299f303a931SBjorn Andersson 		ret = -EINVAL;
1300f303a931SBjorn Andersson 		goto out_wq;
1301f303a931SBjorn Andersson 	} else if (addr) {
13028e84c258SEugene Krasnikov 		wcn36xx_info("mac address: %pM\n", addr);
13038e84c258SEugene Krasnikov 		SET_IEEE80211_PERM_ADDR(wcn->hw, addr);
13048e84c258SEugene Krasnikov 	}
13058e84c258SEugene Krasnikov 
13068e84c258SEugene Krasnikov 	ret = wcn36xx_platform_get_resources(wcn, pdev);
13078e84c258SEugene Krasnikov 	if (ret)
13088e84c258SEugene Krasnikov 		goto out_wq;
13098e84c258SEugene Krasnikov 
13108e84c258SEugene Krasnikov 	wcn36xx_init_ieee80211(wcn);
13118e84c258SEugene Krasnikov 	ret = ieee80211_register_hw(wcn->hw);
13128e84c258SEugene Krasnikov 	if (ret)
13138e84c258SEugene Krasnikov 		goto out_unmap;
13148e84c258SEugene Krasnikov 
13158e84c258SEugene Krasnikov 	return 0;
13168e84c258SEugene Krasnikov 
13178e84c258SEugene Krasnikov out_unmap:
131805ddce49SBjorn Andersson 	iounmap(wcn->ccu_base);
131905ddce49SBjorn Andersson 	iounmap(wcn->dxe_base);
13208e84c258SEugene Krasnikov out_wq:
13218e84c258SEugene Krasnikov 	ieee80211_free_hw(hw);
13228e84c258SEugene Krasnikov out_err:
13238e84c258SEugene Krasnikov 	return ret;
13248e84c258SEugene Krasnikov }
1325f303a931SBjorn Andersson 
13268e84c258SEugene Krasnikov static int wcn36xx_remove(struct platform_device *pdev)
13278e84c258SEugene Krasnikov {
13288e84c258SEugene Krasnikov 	struct ieee80211_hw *hw = platform_get_drvdata(pdev);
13298e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
13308e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n");
13318e84c258SEugene Krasnikov 
13324bda7fafSPontus Fuchs 	release_firmware(wcn->nv);
13338e84c258SEugene Krasnikov 
13348e84c258SEugene Krasnikov 	ieee80211_unregister_hw(hw);
1335f303a931SBjorn Andersson 
1336f303a931SBjorn Andersson 	qcom_smem_state_put(wcn->tx_enable_state);
1337f303a931SBjorn Andersson 	qcom_smem_state_put(wcn->tx_rings_empty_state);
1338f303a931SBjorn Andersson 
1339efad8396SBjorn Andersson 	rpmsg_destroy_ept(wcn->smd_channel);
1340efad8396SBjorn Andersson 
134105ddce49SBjorn Andersson 	iounmap(wcn->dxe_base);
134205ddce49SBjorn Andersson 	iounmap(wcn->ccu_base);
1343d5362888SBjorn Andersson 
1344d5362888SBjorn Andersson 	mutex_destroy(&wcn->hal_mutex);
13458e84c258SEugene Krasnikov 	ieee80211_free_hw(hw);
13468e84c258SEugene Krasnikov 
13478e84c258SEugene Krasnikov 	return 0;
13488e84c258SEugene Krasnikov }
1349f303a931SBjorn Andersson 
1350f303a931SBjorn Andersson static const struct of_device_id wcn36xx_of_match[] = {
1351f303a931SBjorn Andersson 	{ .compatible = "qcom,wcnss-wlan" },
13528e84c258SEugene Krasnikov 	{}
13538e84c258SEugene Krasnikov };
1354f303a931SBjorn Andersson MODULE_DEVICE_TABLE(of, wcn36xx_of_match);
13558e84c258SEugene Krasnikov 
13568e84c258SEugene Krasnikov static struct platform_driver wcn36xx_driver = {
13578e84c258SEugene Krasnikov 	.probe      = wcn36xx_probe,
13588e84c258SEugene Krasnikov 	.remove     = wcn36xx_remove,
13598e84c258SEugene Krasnikov 	.driver         = {
13608e84c258SEugene Krasnikov 		.name   = "wcn36xx",
1361f303a931SBjorn Andersson 		.of_match_table = wcn36xx_of_match,
13628e84c258SEugene Krasnikov 	},
13638e84c258SEugene Krasnikov };
13648e84c258SEugene Krasnikov 
1365f303a931SBjorn Andersson module_platform_driver(wcn36xx_driver);
13668e84c258SEugene Krasnikov 
13678e84c258SEugene Krasnikov MODULE_LICENSE("Dual BSD/GPL");
13688e84c258SEugene Krasnikov MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com");
13698e84c258SEugene Krasnikov MODULE_FIRMWARE(WLAN_NV_FILE);
1370