xref: /linux/drivers/net/wireless/ath/ath11k/reg.c (revision 4f6b838c378a52ea3ae0b15f12ca8a20849072fa)
1d5c65159SKalle Valo // SPDX-License-Identifier: BSD-3-Clause-Clear
2d5c65159SKalle Valo /*
3d5c65159SKalle Valo  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
4d5c65159SKalle Valo  */
5d5c65159SKalle Valo #include "core.h"
6d5c65159SKalle Valo #include "debug.h"
7d5c65159SKalle Valo 
8d5c65159SKalle Valo /* World regdom to be used in case default regd from fw is unavailable */
9d5c65159SKalle Valo #define ATH11K_2GHZ_CH01_11      REG_RULE(2412 - 10, 2462 + 10, 40, 0, 20, 0)
10d5c65159SKalle Valo #define ATH11K_5GHZ_5150_5350    REG_RULE(5150 - 10, 5350 + 10, 80, 0, 30,\
11d5c65159SKalle Valo 					  NL80211_RRF_NO_IR)
12d5c65159SKalle Valo #define ATH11K_5GHZ_5725_5850    REG_RULE(5725 - 10, 5850 + 10, 80, 0, 30,\
13d5c65159SKalle Valo 					  NL80211_RRF_NO_IR)
14d5c65159SKalle Valo 
15d5c65159SKalle Valo #define ETSI_WEATHER_RADAR_BAND_LOW		5590
16d5c65159SKalle Valo #define ETSI_WEATHER_RADAR_BAND_HIGH		5650
17d5c65159SKalle Valo #define ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT	600000
18d5c65159SKalle Valo 
19d5c65159SKalle Valo static const struct ieee80211_regdomain ath11k_world_regd = {
20d5c65159SKalle Valo 	.n_reg_rules = 3,
21d5c65159SKalle Valo 	.alpha2 =  "00",
22d5c65159SKalle Valo 	.reg_rules = {
23d5c65159SKalle Valo 		ATH11K_2GHZ_CH01_11,
24d5c65159SKalle Valo 		ATH11K_5GHZ_5150_5350,
25d5c65159SKalle Valo 		ATH11K_5GHZ_5725_5850,
26d5c65159SKalle Valo 	}
27d5c65159SKalle Valo };
28d5c65159SKalle Valo 
29d5c65159SKalle Valo static bool ath11k_regdom_changes(struct ath11k *ar, char *alpha2)
30d5c65159SKalle Valo {
31d5c65159SKalle Valo 	const struct ieee80211_regdomain *regd;
32d5c65159SKalle Valo 
33d5c65159SKalle Valo 	regd = rcu_dereference_rtnl(ar->hw->wiphy->regd);
34d5c65159SKalle Valo 	/* This can happen during wiphy registration where the previous
35d5c65159SKalle Valo 	 * user request is received before we update the regd received
36d5c65159SKalle Valo 	 * from firmware.
37d5c65159SKalle Valo 	 */
38d5c65159SKalle Valo 	if (!regd)
39d5c65159SKalle Valo 		return true;
40d5c65159SKalle Valo 
41d5c65159SKalle Valo 	return memcmp(regd->alpha2, alpha2, 2) != 0;
42d5c65159SKalle Valo }
43d5c65159SKalle Valo 
44d5c65159SKalle Valo static void
45d5c65159SKalle Valo ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
46d5c65159SKalle Valo {
47d5c65159SKalle Valo 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
48d5c65159SKalle Valo 	struct wmi_init_country_params init_country_param;
49d5c65159SKalle Valo 	struct ath11k *ar = hw->priv;
50d5c65159SKalle Valo 	int ret;
51d5c65159SKalle Valo 
52d5c65159SKalle Valo 	ath11k_dbg(ar->ab, ATH11K_DBG_REG,
53d5c65159SKalle Valo 		   "Regulatory Notification received for %s\n", wiphy_name(wiphy));
54d5c65159SKalle Valo 
55d5c65159SKalle Valo 	/* Currently supporting only General User Hints. Cell base user
56d5c65159SKalle Valo 	 * hints to be handled later.
57d5c65159SKalle Valo 	 * Hints from other sources like Core, Beacons are not expected for
58d5c65159SKalle Valo 	 * self managed wiphy's
59d5c65159SKalle Valo 	 */
60d5c65159SKalle Valo 	if (!(request->initiator == NL80211_REGDOM_SET_BY_USER &&
61d5c65159SKalle Valo 	      request->user_reg_hint_type == NL80211_USER_REG_HINT_USER)) {
62d5c65159SKalle Valo 		ath11k_warn(ar->ab, "Unexpected Regulatory event for this wiphy\n");
63d5c65159SKalle Valo 		return;
64d5c65159SKalle Valo 	}
65d5c65159SKalle Valo 
66d5c65159SKalle Valo 	if (!IS_ENABLED(CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS)) {
67d5c65159SKalle Valo 		ath11k_dbg(ar->ab, ATH11K_DBG_REG,
68d5c65159SKalle Valo 			   "Country Setting is not allowed\n");
69d5c65159SKalle Valo 		return;
70d5c65159SKalle Valo 	}
71d5c65159SKalle Valo 
72d5c65159SKalle Valo 	if (!ath11k_regdom_changes(ar, request->alpha2)) {
73d5c65159SKalle Valo 		ath11k_dbg(ar->ab, ATH11K_DBG_REG, "Country is already set\n");
74d5c65159SKalle Valo 		return;
75d5c65159SKalle Valo 	}
76d5c65159SKalle Valo 
77d5c65159SKalle Valo 	/* Set the country code to the firmware and wait for
78d5c65159SKalle Valo 	 * the WMI_REG_CHAN_LIST_CC EVENT for updating the
79d5c65159SKalle Valo 	 * reg info
80d5c65159SKalle Valo 	 */
81d5c65159SKalle Valo 	init_country_param.flags = ALPHA_IS_SET;
82d5c65159SKalle Valo 	memcpy(&init_country_param.cc_info.alpha2, request->alpha2, 2);
83d5c65159SKalle Valo 
84d5c65159SKalle Valo 	ret = ath11k_wmi_send_init_country_cmd(ar, init_country_param);
85d5c65159SKalle Valo 	if (ret)
86d5c65159SKalle Valo 		ath11k_warn(ar->ab,
87d5c65159SKalle Valo 			    "INIT Country code set to fw failed : %d\n", ret);
88d5c65159SKalle Valo }
89d5c65159SKalle Valo 
90d5c65159SKalle Valo int ath11k_reg_update_chan_list(struct ath11k *ar)
91d5c65159SKalle Valo {
92d5c65159SKalle Valo 	struct ieee80211_supported_band **bands;
93d5c65159SKalle Valo 	struct scan_chan_list_params *params;
94d5c65159SKalle Valo 	struct ieee80211_channel *channel;
95d5c65159SKalle Valo 	struct ieee80211_hw *hw = ar->hw;
96d5c65159SKalle Valo 	struct channel_param *ch;
97d5c65159SKalle Valo 	enum nl80211_band band;
98d5c65159SKalle Valo 	int num_channels = 0;
99d5c65159SKalle Valo 	int params_len;
100d5c65159SKalle Valo 	int i, ret;
101d5c65159SKalle Valo 
102d5c65159SKalle Valo 	bands = hw->wiphy->bands;
103d5c65159SKalle Valo 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
104d5c65159SKalle Valo 		if (!bands[band])
105d5c65159SKalle Valo 			continue;
106d5c65159SKalle Valo 
107d5c65159SKalle Valo 		for (i = 0; i < bands[band]->n_channels; i++) {
108d5c65159SKalle Valo 			if (bands[band]->channels[i].flags &
109d5c65159SKalle Valo 			    IEEE80211_CHAN_DISABLED)
110d5c65159SKalle Valo 				continue;
111d5c65159SKalle Valo 
112d5c65159SKalle Valo 			num_channels++;
113d5c65159SKalle Valo 		}
114d5c65159SKalle Valo 	}
115d5c65159SKalle Valo 
116d5c65159SKalle Valo 	if (WARN_ON(!num_channels))
117d5c65159SKalle Valo 		return -EINVAL;
118d5c65159SKalle Valo 
119d5c65159SKalle Valo 	params_len = sizeof(struct scan_chan_list_params) +
120d5c65159SKalle Valo 			num_channels * sizeof(struct channel_param);
121d5c65159SKalle Valo 	params = kzalloc(params_len, GFP_KERNEL);
122d5c65159SKalle Valo 
123d5c65159SKalle Valo 	if (!params)
124d5c65159SKalle Valo 		return -ENOMEM;
125d5c65159SKalle Valo 
126d5c65159SKalle Valo 	params->pdev_id = ar->pdev->pdev_id;
127d5c65159SKalle Valo 	params->nallchans = num_channels;
128d5c65159SKalle Valo 
129d5c65159SKalle Valo 	ch = params->ch_param;
130d5c65159SKalle Valo 
131d5c65159SKalle Valo 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
132d5c65159SKalle Valo 		if (!bands[band])
133d5c65159SKalle Valo 			continue;
134d5c65159SKalle Valo 
135d5c65159SKalle Valo 		for (i = 0; i < bands[band]->n_channels; i++) {
136d5c65159SKalle Valo 			channel = &bands[band]->channels[i];
137d5c65159SKalle Valo 
138d5c65159SKalle Valo 			if (channel->flags & IEEE80211_CHAN_DISABLED)
139d5c65159SKalle Valo 				continue;
140d5c65159SKalle Valo 
141d5c65159SKalle Valo 			/* TODO: Set to true/false based on some condition? */
142d5c65159SKalle Valo 			ch->allow_ht = true;
143d5c65159SKalle Valo 			ch->allow_vht = true;
1449f056ed8SJohn Crispin 			ch->allow_he = true;
145d5c65159SKalle Valo 
146d5c65159SKalle Valo 			ch->dfs_set =
147d5c65159SKalle Valo 				!!(channel->flags & IEEE80211_CHAN_RADAR);
148d5c65159SKalle Valo 			ch->is_chan_passive = !!(channel->flags &
149d5c65159SKalle Valo 						IEEE80211_CHAN_NO_IR);
150d5c65159SKalle Valo 			ch->is_chan_passive |= ch->dfs_set;
151d5c65159SKalle Valo 			ch->mhz = channel->center_freq;
152d5c65159SKalle Valo 			ch->cfreq1 = channel->center_freq;
153d5c65159SKalle Valo 			ch->minpower = 0;
154d5c65159SKalle Valo 			ch->maxpower = channel->max_power * 2;
155d5c65159SKalle Valo 			ch->maxregpower = channel->max_reg_power * 2;
156d5c65159SKalle Valo 			ch->antennamax = channel->max_antenna_gain * 2;
157d5c65159SKalle Valo 
158d5c65159SKalle Valo 			/* TODO: Use appropriate phymodes */
159d5c65159SKalle Valo 			if (channel->band == NL80211_BAND_2GHZ)
160d5c65159SKalle Valo 				ch->phy_mode = MODE_11G;
161d5c65159SKalle Valo 			else
162d5c65159SKalle Valo 				ch->phy_mode = MODE_11A;
163d5c65159SKalle Valo 
164d387503dSPradeep Kumar Chitrapu 			if (channel->band == NL80211_BAND_6GHZ &&
165d387503dSPradeep Kumar Chitrapu 			    cfg80211_channel_is_psc(channel))
166d387503dSPradeep Kumar Chitrapu 				ch->psc_channel = true;
167d387503dSPradeep Kumar Chitrapu 
168d5c65159SKalle Valo 			ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
169d5c65159SKalle Valo 				   "mac channel [%d/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
170d5c65159SKalle Valo 				   i, params->nallchans,
171d5c65159SKalle Valo 				   ch->mhz, ch->maxpower, ch->maxregpower,
172d5c65159SKalle Valo 				   ch->antennamax, ch->phy_mode);
173d5c65159SKalle Valo 
174d5c65159SKalle Valo 			ch++;
175d5c65159SKalle Valo 			/* TODO: use quarrter/half rate, cfreq12, dfs_cfreq2
176d5c65159SKalle Valo 			 * set_agile, reg_class_idx
177d5c65159SKalle Valo 			 */
178d5c65159SKalle Valo 		}
179d5c65159SKalle Valo 	}
180d5c65159SKalle Valo 
181d5c65159SKalle Valo 	ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params);
182d5c65159SKalle Valo 	kfree(params);
183d5c65159SKalle Valo 
184d5c65159SKalle Valo 	return ret;
185d5c65159SKalle Valo }
186d5c65159SKalle Valo 
187d5c65159SKalle Valo static void ath11k_copy_regd(struct ieee80211_regdomain *regd_orig,
188d5c65159SKalle Valo 			     struct ieee80211_regdomain *regd_copy)
189d5c65159SKalle Valo {
190d5c65159SKalle Valo 	u8 i;
191d5c65159SKalle Valo 
192d5c65159SKalle Valo 	/* The caller should have checked error conditions */
193d5c65159SKalle Valo 	memcpy(regd_copy, regd_orig, sizeof(*regd_orig));
194d5c65159SKalle Valo 
195d5c65159SKalle Valo 	for (i = 0; i < regd_orig->n_reg_rules; i++)
196d5c65159SKalle Valo 		memcpy(&regd_copy->reg_rules[i], &regd_orig->reg_rules[i],
197d5c65159SKalle Valo 		       sizeof(struct ieee80211_reg_rule));
198d5c65159SKalle Valo }
199d5c65159SKalle Valo 
200d5c65159SKalle Valo int ath11k_regd_update(struct ath11k *ar, bool init)
201d5c65159SKalle Valo {
202d5c65159SKalle Valo 	struct ieee80211_regdomain *regd, *regd_copy = NULL;
203d5c65159SKalle Valo 	int ret, regd_len, pdev_id;
204d5c65159SKalle Valo 	struct ath11k_base *ab;
205d5c65159SKalle Valo 
206d5c65159SKalle Valo 	ab = ar->ab;
207d5c65159SKalle Valo 	pdev_id = ar->pdev_idx;
208d5c65159SKalle Valo 
209*df648808SWen Gong 	spin_lock_bh(&ab->base_lock);
210d5c65159SKalle Valo 
211d5c65159SKalle Valo 	if (init) {
212d5c65159SKalle Valo 		/* Apply the regd received during init through
213d5c65159SKalle Valo 		 * WMI_REG_CHAN_LIST_CC event. In case of failure to
214d5c65159SKalle Valo 		 * receive the regd, initialize with a default world
215d5c65159SKalle Valo 		 * regulatory.
216d5c65159SKalle Valo 		 */
217d5c65159SKalle Valo 		if (ab->default_regd[pdev_id]) {
218d5c65159SKalle Valo 			regd = ab->default_regd[pdev_id];
219d5c65159SKalle Valo 		} else {
220d5c65159SKalle Valo 			ath11k_warn(ab,
221d5c65159SKalle Valo 				    "failed to receive default regd during init\n");
222d5c65159SKalle Valo 			regd = (struct ieee80211_regdomain *)&ath11k_world_regd;
223d5c65159SKalle Valo 		}
224d5c65159SKalle Valo 	} else {
225d5c65159SKalle Valo 		regd = ab->new_regd[pdev_id];
226d5c65159SKalle Valo 	}
227d5c65159SKalle Valo 
228d5c65159SKalle Valo 	if (!regd) {
229d5c65159SKalle Valo 		ret = -EINVAL;
230*df648808SWen Gong 		spin_unlock_bh(&ab->base_lock);
231d5c65159SKalle Valo 		goto err;
232d5c65159SKalle Valo 	}
233d5c65159SKalle Valo 
234d5c65159SKalle Valo 	regd_len = sizeof(*regd) + (regd->n_reg_rules *
235d5c65159SKalle Valo 		sizeof(struct ieee80211_reg_rule));
236d5c65159SKalle Valo 
237d5c65159SKalle Valo 	regd_copy = kzalloc(regd_len, GFP_ATOMIC);
238d5c65159SKalle Valo 	if (regd_copy)
239d5c65159SKalle Valo 		ath11k_copy_regd(regd, regd_copy);
240d5c65159SKalle Valo 
241*df648808SWen Gong 	spin_unlock_bh(&ab->base_lock);
242d5c65159SKalle Valo 
243d5c65159SKalle Valo 	if (!regd_copy) {
244d5c65159SKalle Valo 		ret = -ENOMEM;
245d5c65159SKalle Valo 		goto err;
246d5c65159SKalle Valo 	}
247d5c65159SKalle Valo 
248d5c65159SKalle Valo 	rtnl_lock();
249d5c65159SKalle Valo 	ret = regulatory_set_wiphy_regd_sync_rtnl(ar->hw->wiphy, regd_copy);
250d5c65159SKalle Valo 	rtnl_unlock();
251d5c65159SKalle Valo 
252d5c65159SKalle Valo 	kfree(regd_copy);
253d5c65159SKalle Valo 
254d5c65159SKalle Valo 	if (ret)
255d5c65159SKalle Valo 		goto err;
256d5c65159SKalle Valo 
257d5c65159SKalle Valo 	if (ar->state == ATH11K_STATE_ON) {
258d5c65159SKalle Valo 		ret = ath11k_reg_update_chan_list(ar);
259d5c65159SKalle Valo 		if (ret)
260d5c65159SKalle Valo 			goto err;
261d5c65159SKalle Valo 	}
262d5c65159SKalle Valo 
263d5c65159SKalle Valo 	return 0;
264d5c65159SKalle Valo err:
265d5c65159SKalle Valo 	ath11k_warn(ab, "failed to perform regd update : %d\n", ret);
266d5c65159SKalle Valo 	return ret;
267d5c65159SKalle Valo }
268d5c65159SKalle Valo 
269d5c65159SKalle Valo static enum nl80211_dfs_regions
270d5c65159SKalle Valo ath11k_map_fw_dfs_region(enum ath11k_dfs_region dfs_region)
271d5c65159SKalle Valo {
272d5c65159SKalle Valo 	switch (dfs_region) {
273d5c65159SKalle Valo 	case ATH11K_DFS_REG_FCC:
274d5c65159SKalle Valo 	case ATH11K_DFS_REG_CN:
275d5c65159SKalle Valo 		return NL80211_DFS_FCC;
276d5c65159SKalle Valo 	case ATH11K_DFS_REG_ETSI:
277d5c65159SKalle Valo 	case ATH11K_DFS_REG_KR:
278d5c65159SKalle Valo 		return NL80211_DFS_ETSI;
279d5c65159SKalle Valo 	case ATH11K_DFS_REG_MKK:
280d5c65159SKalle Valo 		return NL80211_DFS_JP;
281d5c65159SKalle Valo 	default:
282d5c65159SKalle Valo 		return NL80211_DFS_UNSET;
283d5c65159SKalle Valo 	}
284d5c65159SKalle Valo }
285d5c65159SKalle Valo 
286d5c65159SKalle Valo static u32 ath11k_map_fw_reg_flags(u16 reg_flags)
287d5c65159SKalle Valo {
288d5c65159SKalle Valo 	u32 flags = 0;
289d5c65159SKalle Valo 
290d5c65159SKalle Valo 	if (reg_flags & REGULATORY_CHAN_NO_IR)
291d5c65159SKalle Valo 		flags = NL80211_RRF_NO_IR;
292d5c65159SKalle Valo 
293d5c65159SKalle Valo 	if (reg_flags & REGULATORY_CHAN_RADAR)
294d5c65159SKalle Valo 		flags |= NL80211_RRF_DFS;
295d5c65159SKalle Valo 
296d5c65159SKalle Valo 	if (reg_flags & REGULATORY_CHAN_NO_OFDM)
297d5c65159SKalle Valo 		flags |= NL80211_RRF_NO_OFDM;
298d5c65159SKalle Valo 
299d5c65159SKalle Valo 	if (reg_flags & REGULATORY_CHAN_INDOOR_ONLY)
300d5c65159SKalle Valo 		flags |= NL80211_RRF_NO_OUTDOOR;
301d5c65159SKalle Valo 
302d5c65159SKalle Valo 	if (reg_flags & REGULATORY_CHAN_NO_HT40)
303d5c65159SKalle Valo 		flags |= NL80211_RRF_NO_HT40;
304d5c65159SKalle Valo 
305d5c65159SKalle Valo 	if (reg_flags & REGULATORY_CHAN_NO_80MHZ)
306d5c65159SKalle Valo 		flags |= NL80211_RRF_NO_80MHZ;
307d5c65159SKalle Valo 
308d5c65159SKalle Valo 	if (reg_flags & REGULATORY_CHAN_NO_160MHZ)
309d5c65159SKalle Valo 		flags |= NL80211_RRF_NO_160MHZ;
310d5c65159SKalle Valo 
311d5c65159SKalle Valo 	return flags;
312d5c65159SKalle Valo }
313d5c65159SKalle Valo 
314d5c65159SKalle Valo static bool
315d5c65159SKalle Valo ath11k_reg_can_intersect(struct ieee80211_reg_rule *rule1,
316d5c65159SKalle Valo 			 struct ieee80211_reg_rule *rule2)
317d5c65159SKalle Valo {
318d5c65159SKalle Valo 	u32 start_freq1, end_freq1;
319d5c65159SKalle Valo 	u32 start_freq2, end_freq2;
320d5c65159SKalle Valo 
321d5c65159SKalle Valo 	start_freq1 = rule1->freq_range.start_freq_khz;
322d5c65159SKalle Valo 	start_freq2 = rule2->freq_range.start_freq_khz;
323d5c65159SKalle Valo 
324d5c65159SKalle Valo 	end_freq1 = rule1->freq_range.end_freq_khz;
325d5c65159SKalle Valo 	end_freq2 = rule2->freq_range.end_freq_khz;
326d5c65159SKalle Valo 
327d5c65159SKalle Valo 	if ((start_freq1 >= start_freq2 &&
328d5c65159SKalle Valo 	     start_freq1 < end_freq2) ||
329d5c65159SKalle Valo 	    (start_freq2 > start_freq1 &&
330d5c65159SKalle Valo 	     start_freq2 < end_freq1))
331d5c65159SKalle Valo 		return true;
332d5c65159SKalle Valo 
333d5c65159SKalle Valo 	/* TODO: Should we restrict intersection feasibility
334d5c65159SKalle Valo 	 *  based on min bandwidth of the intersected region also,
335d5c65159SKalle Valo 	 *  say the intersected rule should have a  min bandwidth
336d5c65159SKalle Valo 	 * of 20MHz?
337d5c65159SKalle Valo 	 */
338d5c65159SKalle Valo 
339d5c65159SKalle Valo 	return false;
340d5c65159SKalle Valo }
341d5c65159SKalle Valo 
342d5c65159SKalle Valo static void ath11k_reg_intersect_rules(struct ieee80211_reg_rule *rule1,
343d5c65159SKalle Valo 				       struct ieee80211_reg_rule *rule2,
344d5c65159SKalle Valo 				       struct ieee80211_reg_rule *new_rule)
345d5c65159SKalle Valo {
346d5c65159SKalle Valo 	u32 start_freq1, end_freq1;
347d5c65159SKalle Valo 	u32 start_freq2, end_freq2;
348d5c65159SKalle Valo 	u32 freq_diff, max_bw;
349d5c65159SKalle Valo 
350d5c65159SKalle Valo 	start_freq1 = rule1->freq_range.start_freq_khz;
351d5c65159SKalle Valo 	start_freq2 = rule2->freq_range.start_freq_khz;
352d5c65159SKalle Valo 
353d5c65159SKalle Valo 	end_freq1 = rule1->freq_range.end_freq_khz;
354d5c65159SKalle Valo 	end_freq2 = rule2->freq_range.end_freq_khz;
355d5c65159SKalle Valo 
356d5c65159SKalle Valo 	new_rule->freq_range.start_freq_khz = max_t(u32, start_freq1,
357d5c65159SKalle Valo 						    start_freq2);
358d5c65159SKalle Valo 	new_rule->freq_range.end_freq_khz = min_t(u32, end_freq1, end_freq2);
359d5c65159SKalle Valo 
360d5c65159SKalle Valo 	freq_diff = new_rule->freq_range.end_freq_khz -
361d5c65159SKalle Valo 			new_rule->freq_range.start_freq_khz;
362d5c65159SKalle Valo 	max_bw = min_t(u32, rule1->freq_range.max_bandwidth_khz,
363d5c65159SKalle Valo 		       rule2->freq_range.max_bandwidth_khz);
364d5c65159SKalle Valo 	new_rule->freq_range.max_bandwidth_khz = min_t(u32, max_bw, freq_diff);
365d5c65159SKalle Valo 
366d5c65159SKalle Valo 	new_rule->power_rule.max_antenna_gain =
367d5c65159SKalle Valo 		min_t(u32, rule1->power_rule.max_antenna_gain,
368d5c65159SKalle Valo 		      rule2->power_rule.max_antenna_gain);
369d5c65159SKalle Valo 
370d5c65159SKalle Valo 	new_rule->power_rule.max_eirp = min_t(u32, rule1->power_rule.max_eirp,
371d5c65159SKalle Valo 					      rule2->power_rule.max_eirp);
372d5c65159SKalle Valo 
373d5c65159SKalle Valo 	/* Use the flags of both the rules */
374d5c65159SKalle Valo 	new_rule->flags = rule1->flags | rule2->flags;
375d5c65159SKalle Valo 
376d5c65159SKalle Valo 	/* To be safe, lts use the max cac timeout of both rules */
377d5c65159SKalle Valo 	new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms,
378d5c65159SKalle Valo 				     rule2->dfs_cac_ms);
379d5c65159SKalle Valo }
380d5c65159SKalle Valo 
381d5c65159SKalle Valo static struct ieee80211_regdomain *
382d5c65159SKalle Valo ath11k_regd_intersect(struct ieee80211_regdomain *default_regd,
383d5c65159SKalle Valo 		      struct ieee80211_regdomain *curr_regd)
384d5c65159SKalle Valo {
385d5c65159SKalle Valo 	u8 num_old_regd_rules, num_curr_regd_rules, num_new_regd_rules;
386d5c65159SKalle Valo 	struct ieee80211_reg_rule *old_rule, *curr_rule, *new_rule;
387d5c65159SKalle Valo 	struct ieee80211_regdomain *new_regd = NULL;
388d5c65159SKalle Valo 	u8 i, j, k;
389d5c65159SKalle Valo 
390d5c65159SKalle Valo 	num_old_regd_rules = default_regd->n_reg_rules;
391d5c65159SKalle Valo 	num_curr_regd_rules = curr_regd->n_reg_rules;
392d5c65159SKalle Valo 	num_new_regd_rules = 0;
393d5c65159SKalle Valo 
394d5c65159SKalle Valo 	/* Find the number of intersecting rules to allocate new regd memory */
395d5c65159SKalle Valo 	for (i = 0; i < num_old_regd_rules; i++) {
396d5c65159SKalle Valo 		old_rule = default_regd->reg_rules + i;
397d5c65159SKalle Valo 		for (j = 0; j < num_curr_regd_rules; j++) {
398d5c65159SKalle Valo 			curr_rule = curr_regd->reg_rules + j;
399d5c65159SKalle Valo 
400d5c65159SKalle Valo 			if (ath11k_reg_can_intersect(old_rule, curr_rule))
401d5c65159SKalle Valo 				num_new_regd_rules++;
402d5c65159SKalle Valo 		}
403d5c65159SKalle Valo 	}
404d5c65159SKalle Valo 
405d5c65159SKalle Valo 	if (!num_new_regd_rules)
406d5c65159SKalle Valo 		return NULL;
407d5c65159SKalle Valo 
408d5c65159SKalle Valo 	new_regd = kzalloc(sizeof(*new_regd) + (num_new_regd_rules *
409d5c65159SKalle Valo 			sizeof(struct ieee80211_reg_rule)),
410d5c65159SKalle Valo 			GFP_ATOMIC);
411d5c65159SKalle Valo 
412d5c65159SKalle Valo 	if (!new_regd)
413d5c65159SKalle Valo 		return NULL;
414d5c65159SKalle Valo 
415d5c65159SKalle Valo 	/* We set the new country and dfs region directly and only trim
416d5c65159SKalle Valo 	 * the freq, power, antenna gain by intersecting with the
417d5c65159SKalle Valo 	 * default regdomain. Also MAX of the dfs cac timeout is selected.
418d5c65159SKalle Valo 	 */
419d5c65159SKalle Valo 	new_regd->n_reg_rules = num_new_regd_rules;
420d5c65159SKalle Valo 	memcpy(new_regd->alpha2, curr_regd->alpha2, sizeof(new_regd->alpha2));
421d5c65159SKalle Valo 	new_regd->dfs_region = curr_regd->dfs_region;
422d5c65159SKalle Valo 	new_rule = new_regd->reg_rules;
423d5c65159SKalle Valo 
424d5c65159SKalle Valo 	for (i = 0, k = 0; i < num_old_regd_rules; i++) {
425d5c65159SKalle Valo 		old_rule = default_regd->reg_rules + i;
426d5c65159SKalle Valo 		for (j = 0; j < num_curr_regd_rules; j++) {
427d5c65159SKalle Valo 			curr_rule = curr_regd->reg_rules + j;
428d5c65159SKalle Valo 
429d5c65159SKalle Valo 			if (ath11k_reg_can_intersect(old_rule, curr_rule))
430d5c65159SKalle Valo 				ath11k_reg_intersect_rules(old_rule, curr_rule,
431d5c65159SKalle Valo 							   (new_rule + k++));
432d5c65159SKalle Valo 		}
433d5c65159SKalle Valo 	}
434d5c65159SKalle Valo 	return new_regd;
435d5c65159SKalle Valo }
436d5c65159SKalle Valo 
437d5c65159SKalle Valo static const char *
438d5c65159SKalle Valo ath11k_reg_get_regdom_str(enum nl80211_dfs_regions dfs_region)
439d5c65159SKalle Valo {
440d5c65159SKalle Valo 	switch (dfs_region) {
441d5c65159SKalle Valo 	case NL80211_DFS_FCC:
442d5c65159SKalle Valo 		return "FCC";
443d5c65159SKalle Valo 	case NL80211_DFS_ETSI:
444d5c65159SKalle Valo 		return "ETSI";
445d5c65159SKalle Valo 	case NL80211_DFS_JP:
446d5c65159SKalle Valo 		return "JP";
447d5c65159SKalle Valo 	default:
448d5c65159SKalle Valo 		return "UNSET";
449d5c65159SKalle Valo 	}
450d5c65159SKalle Valo }
451d5c65159SKalle Valo 
452d5c65159SKalle Valo static u16
453d5c65159SKalle Valo ath11k_reg_adjust_bw(u16 start_freq, u16 end_freq, u16 max_bw)
454d5c65159SKalle Valo {
455d5c65159SKalle Valo 	u16 bw;
456d5c65159SKalle Valo 
457d5c65159SKalle Valo 	bw = end_freq - start_freq;
458d5c65159SKalle Valo 	bw = min_t(u16, bw, max_bw);
459d5c65159SKalle Valo 
460d5c65159SKalle Valo 	if (bw >= 80 && bw < 160)
461d5c65159SKalle Valo 		bw = 80;
462d5c65159SKalle Valo 	else if (bw >= 40 && bw < 80)
463d5c65159SKalle Valo 		bw = 40;
464d5c65159SKalle Valo 	else if (bw < 40)
465d5c65159SKalle Valo 		bw = 20;
466d5c65159SKalle Valo 
467d5c65159SKalle Valo 	return bw;
468d5c65159SKalle Valo }
469d5c65159SKalle Valo 
470d5c65159SKalle Valo static void
471d5c65159SKalle Valo ath11k_reg_update_rule(struct ieee80211_reg_rule *reg_rule, u32 start_freq,
472d5c65159SKalle Valo 		       u32 end_freq, u32 bw, u32 ant_gain, u32 reg_pwr,
473d5c65159SKalle Valo 		       u32 reg_flags)
474d5c65159SKalle Valo {
475d5c65159SKalle Valo 	reg_rule->freq_range.start_freq_khz = MHZ_TO_KHZ(start_freq);
476d5c65159SKalle Valo 	reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(end_freq);
477d5c65159SKalle Valo 	reg_rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw);
478d5c65159SKalle Valo 	reg_rule->power_rule.max_antenna_gain = DBI_TO_MBI(ant_gain);
479d5c65159SKalle Valo 	reg_rule->power_rule.max_eirp = DBM_TO_MBM(reg_pwr);
480d5c65159SKalle Valo 	reg_rule->flags = reg_flags;
481d5c65159SKalle Valo }
482d5c65159SKalle Valo 
483d5c65159SKalle Valo static void
484d5c65159SKalle Valo ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,
485d5c65159SKalle Valo 				     struct ieee80211_regdomain *regd,
486d5c65159SKalle Valo 				     struct cur_reg_rule *reg_rule,
487d5c65159SKalle Valo 				     u8 *rule_idx, u32 flags, u16 max_bw)
488d5c65159SKalle Valo {
489d5c65159SKalle Valo 	u32 end_freq;
490d5c65159SKalle Valo 	u16 bw;
491d5c65159SKalle Valo 	u8 i;
492d5c65159SKalle Valo 
493d5c65159SKalle Valo 	i = *rule_idx;
494d5c65159SKalle Valo 
495d5c65159SKalle Valo 	bw = ath11k_reg_adjust_bw(reg_rule->start_freq,
496d5c65159SKalle Valo 				  ETSI_WEATHER_RADAR_BAND_LOW, max_bw);
497d5c65159SKalle Valo 
498d5c65159SKalle Valo 	ath11k_reg_update_rule(regd->reg_rules + i, reg_rule->start_freq,
499d5c65159SKalle Valo 			       ETSI_WEATHER_RADAR_BAND_LOW, bw,
500d5c65159SKalle Valo 			       reg_rule->ant_gain, reg_rule->reg_power,
501d5c65159SKalle Valo 			       flags);
502d5c65159SKalle Valo 
503d5c65159SKalle Valo 	ath11k_dbg(ab, ATH11K_DBG_REG,
504d5c65159SKalle Valo 		   "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
505d5c65159SKalle Valo 		   i + 1, reg_rule->start_freq, ETSI_WEATHER_RADAR_BAND_LOW,
506d5c65159SKalle Valo 		   bw, reg_rule->ant_gain, reg_rule->reg_power,
507d5c65159SKalle Valo 		   regd->reg_rules[i].dfs_cac_ms,
508d5c65159SKalle Valo 		   flags);
509d5c65159SKalle Valo 
510d5c65159SKalle Valo 	if (reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_HIGH)
511d5c65159SKalle Valo 		end_freq = ETSI_WEATHER_RADAR_BAND_HIGH;
512d5c65159SKalle Valo 	else
513d5c65159SKalle Valo 		end_freq = reg_rule->end_freq;
514d5c65159SKalle Valo 
515d5c65159SKalle Valo 	bw = ath11k_reg_adjust_bw(ETSI_WEATHER_RADAR_BAND_LOW, end_freq,
516d5c65159SKalle Valo 				  max_bw);
517d5c65159SKalle Valo 
518d5c65159SKalle Valo 	i++;
519d5c65159SKalle Valo 
520d5c65159SKalle Valo 	ath11k_reg_update_rule(regd->reg_rules + i,
521d5c65159SKalle Valo 			       ETSI_WEATHER_RADAR_BAND_LOW, end_freq, bw,
522d5c65159SKalle Valo 			       reg_rule->ant_gain, reg_rule->reg_power,
523d5c65159SKalle Valo 			       flags);
524d5c65159SKalle Valo 
525d5c65159SKalle Valo 	regd->reg_rules[i].dfs_cac_ms = ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT;
526d5c65159SKalle Valo 
527d5c65159SKalle Valo 	ath11k_dbg(ab, ATH11K_DBG_REG,
528d5c65159SKalle Valo 		   "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
529d5c65159SKalle Valo 		   i + 1, ETSI_WEATHER_RADAR_BAND_LOW, end_freq,
530d5c65159SKalle Valo 		   bw, reg_rule->ant_gain, reg_rule->reg_power,
531d5c65159SKalle Valo 		   regd->reg_rules[i].dfs_cac_ms,
532d5c65159SKalle Valo 		   flags);
533d5c65159SKalle Valo 
534d5c65159SKalle Valo 	if (end_freq == reg_rule->end_freq) {
535d5c65159SKalle Valo 		regd->n_reg_rules--;
536d5c65159SKalle Valo 		*rule_idx = i;
537d5c65159SKalle Valo 		return;
538d5c65159SKalle Valo 	}
539d5c65159SKalle Valo 
540d5c65159SKalle Valo 	bw = ath11k_reg_adjust_bw(ETSI_WEATHER_RADAR_BAND_HIGH,
541d5c65159SKalle Valo 				  reg_rule->end_freq, max_bw);
542d5c65159SKalle Valo 
543d5c65159SKalle Valo 	i++;
544d5c65159SKalle Valo 
545d5c65159SKalle Valo 	ath11k_reg_update_rule(regd->reg_rules + i, ETSI_WEATHER_RADAR_BAND_HIGH,
546d5c65159SKalle Valo 			       reg_rule->end_freq, bw,
547d5c65159SKalle Valo 			       reg_rule->ant_gain, reg_rule->reg_power,
548d5c65159SKalle Valo 			       flags);
549d5c65159SKalle Valo 
550d5c65159SKalle Valo 	ath11k_dbg(ab, ATH11K_DBG_REG,
551d5c65159SKalle Valo 		   "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
552d5c65159SKalle Valo 		   i + 1, ETSI_WEATHER_RADAR_BAND_HIGH, reg_rule->end_freq,
553d5c65159SKalle Valo 		   bw, reg_rule->ant_gain, reg_rule->reg_power,
554d5c65159SKalle Valo 		   regd->reg_rules[i].dfs_cac_ms,
555d5c65159SKalle Valo 		   flags);
556d5c65159SKalle Valo 
557d5c65159SKalle Valo 	*rule_idx = i;
558d5c65159SKalle Valo }
559d5c65159SKalle Valo 
560d5c65159SKalle Valo struct ieee80211_regdomain *
561d5c65159SKalle Valo ath11k_reg_build_regd(struct ath11k_base *ab,
562d5c65159SKalle Valo 		      struct cur_regulatory_info *reg_info, bool intersect)
563d5c65159SKalle Valo {
564d5c65159SKalle Valo 	struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL;
565d5c65159SKalle Valo 	struct cur_reg_rule *reg_rule;
566d5c65159SKalle Valo 	u8 i = 0, j = 0;
567d5c65159SKalle Valo 	u8 num_rules;
568d5c65159SKalle Valo 	u16 max_bw;
569d5c65159SKalle Valo 	u32 flags;
570d5c65159SKalle Valo 	char alpha2[3];
571d5c65159SKalle Valo 
572d5c65159SKalle Valo 	num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules;
573d5c65159SKalle Valo 
574d5c65159SKalle Valo 	if (!num_rules)
575d5c65159SKalle Valo 		goto ret;
576d5c65159SKalle Valo 
577d5c65159SKalle Valo 	/* Add max additional rules to accommodate weather radar band */
578d5c65159SKalle Valo 	if (reg_info->dfs_region == ATH11K_DFS_REG_ETSI)
579d5c65159SKalle Valo 		num_rules += 2;
580d5c65159SKalle Valo 
581d5c65159SKalle Valo 	tmp_regd =  kzalloc(sizeof(*tmp_regd) +
582d5c65159SKalle Valo 			(num_rules * sizeof(struct ieee80211_reg_rule)),
583d5c65159SKalle Valo 			GFP_ATOMIC);
584d5c65159SKalle Valo 	if (!tmp_regd)
585d5c65159SKalle Valo 		goto ret;
586d5c65159SKalle Valo 
587d5c65159SKalle Valo 	tmp_regd->n_reg_rules = num_rules;
588d5c65159SKalle Valo 	memcpy(tmp_regd->alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1);
589d5c65159SKalle Valo 	memcpy(alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1);
590d5c65159SKalle Valo 	alpha2[2] = '\0';
591d5c65159SKalle Valo 	tmp_regd->dfs_region = ath11k_map_fw_dfs_region(reg_info->dfs_region);
592d5c65159SKalle Valo 
593d5c65159SKalle Valo 	ath11k_dbg(ab, ATH11K_DBG_REG,
594d5c65159SKalle Valo 		   "\r\nCountry %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n",
595d5c65159SKalle Valo 		   alpha2, ath11k_reg_get_regdom_str(tmp_regd->dfs_region),
596d5c65159SKalle Valo 		   reg_info->dfs_region, num_rules);
597d5c65159SKalle Valo 	/* Update reg_rules[] below. Firmware is expected to
598d5c65159SKalle Valo 	 * send these rules in order(2G rules first and then 5G)
599d5c65159SKalle Valo 	 */
600d5c65159SKalle Valo 	for (; i < tmp_regd->n_reg_rules; i++) {
601d5c65159SKalle Valo 		if (reg_info->num_2g_reg_rules &&
602d5c65159SKalle Valo 		    (i < reg_info->num_2g_reg_rules)) {
603d5c65159SKalle Valo 			reg_rule = reg_info->reg_rules_2g_ptr + i;
604d5c65159SKalle Valo 			max_bw = min_t(u16, reg_rule->max_bw,
605d5c65159SKalle Valo 				       reg_info->max_bw_2g);
606d5c65159SKalle Valo 			flags = 0;
607d5c65159SKalle Valo 		} else if (reg_info->num_5g_reg_rules &&
608d5c65159SKalle Valo 			   (j < reg_info->num_5g_reg_rules)) {
609d5c65159SKalle Valo 			reg_rule = reg_info->reg_rules_5g_ptr + j++;
610d5c65159SKalle Valo 			max_bw = min_t(u16, reg_rule->max_bw,
611d5c65159SKalle Valo 				       reg_info->max_bw_5g);
612d5c65159SKalle Valo 
613d5c65159SKalle Valo 			/* FW doesn't pass NL80211_RRF_AUTO_BW flag for
614d5c65159SKalle Valo 			 * BW Auto correction, we can enable this by default
615d5c65159SKalle Valo 			 * for all 5G rules here. The regulatory core performs
616d5c65159SKalle Valo 			 * BW correction if required and applies flags as
617d5c65159SKalle Valo 			 * per other BW rule flags we pass from here
618d5c65159SKalle Valo 			 */
619d5c65159SKalle Valo 			flags = NL80211_RRF_AUTO_BW;
620d5c65159SKalle Valo 		} else {
621d5c65159SKalle Valo 			break;
622d5c65159SKalle Valo 		}
623d5c65159SKalle Valo 
624d5c65159SKalle Valo 		flags |= ath11k_map_fw_reg_flags(reg_rule->flags);
625d5c65159SKalle Valo 
626d5c65159SKalle Valo 		ath11k_reg_update_rule(tmp_regd->reg_rules + i,
627d5c65159SKalle Valo 				       reg_rule->start_freq,
628d5c65159SKalle Valo 				       reg_rule->end_freq, max_bw,
629d5c65159SKalle Valo 				       reg_rule->ant_gain, reg_rule->reg_power,
630d5c65159SKalle Valo 				       flags);
631d5c65159SKalle Valo 
632d5c65159SKalle Valo 		/* Update dfs cac timeout if the dfs domain is ETSI and the
633d5c65159SKalle Valo 		 * new rule covers weather radar band.
634d5c65159SKalle Valo 		 * Default value of '0' corresponds to 60s timeout, so no
635d5c65159SKalle Valo 		 * need to update that for other rules.
636d5c65159SKalle Valo 		 */
637d5c65159SKalle Valo 		if (flags & NL80211_RRF_DFS &&
638d5c65159SKalle Valo 		    reg_info->dfs_region == ATH11K_DFS_REG_ETSI &&
639d5c65159SKalle Valo 		    (reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_LOW &&
640d5c65159SKalle Valo 		    reg_rule->start_freq < ETSI_WEATHER_RADAR_BAND_HIGH)){
641d5c65159SKalle Valo 			ath11k_reg_update_weather_radar_band(ab, tmp_regd,
642d5c65159SKalle Valo 							     reg_rule, &i,
643d5c65159SKalle Valo 							     flags, max_bw);
644d5c65159SKalle Valo 			continue;
645d5c65159SKalle Valo 		}
646d5c65159SKalle Valo 
647d5c65159SKalle Valo 		ath11k_dbg(ab, ATH11K_DBG_REG,
648d5c65159SKalle Valo 			   "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
649d5c65159SKalle Valo 			   i + 1, reg_rule->start_freq, reg_rule->end_freq,
650d5c65159SKalle Valo 			   max_bw, reg_rule->ant_gain, reg_rule->reg_power,
651d5c65159SKalle Valo 			   tmp_regd->reg_rules[i].dfs_cac_ms,
652d5c65159SKalle Valo 			   flags);
653d5c65159SKalle Valo 	}
654d5c65159SKalle Valo 
655d5c65159SKalle Valo 	if (intersect) {
656d5c65159SKalle Valo 		default_regd = ab->default_regd[reg_info->phy_id];
657d5c65159SKalle Valo 
658d5c65159SKalle Valo 		/* Get a new regd by intersecting the received regd with
659d5c65159SKalle Valo 		 * our default regd.
660d5c65159SKalle Valo 		 */
661d5c65159SKalle Valo 		new_regd = ath11k_regd_intersect(default_regd, tmp_regd);
662d5c65159SKalle Valo 		kfree(tmp_regd);
663d5c65159SKalle Valo 		if (!new_regd) {
664d5c65159SKalle Valo 			ath11k_warn(ab, "Unable to create intersected regdomain\n");
665d5c65159SKalle Valo 			goto ret;
666d5c65159SKalle Valo 		}
667d5c65159SKalle Valo 	} else {
668d5c65159SKalle Valo 		new_regd = tmp_regd;
669d5c65159SKalle Valo 	}
670d5c65159SKalle Valo 
671d5c65159SKalle Valo ret:
672d5c65159SKalle Valo 	return new_regd;
673d5c65159SKalle Valo }
674d5c65159SKalle Valo 
675d5c65159SKalle Valo void ath11k_regd_update_work(struct work_struct *work)
676d5c65159SKalle Valo {
677d5c65159SKalle Valo 	struct ath11k *ar = container_of(work, struct ath11k,
678d5c65159SKalle Valo 					 regd_update_work);
679d5c65159SKalle Valo 	int ret;
680d5c65159SKalle Valo 
681d5c65159SKalle Valo 	ret = ath11k_regd_update(ar, false);
682d5c65159SKalle Valo 	if (ret) {
683d5c65159SKalle Valo 		/* Firmware has already moved to the new regd. We need
684d5c65159SKalle Valo 		 * to maintain channel consistency across FW, Host driver
685d5c65159SKalle Valo 		 * and userspace. Hence as a fallback mechanism we can set
686d5c65159SKalle Valo 		 * the prev or default country code to the firmware.
687d5c65159SKalle Valo 		 */
688d5c65159SKalle Valo 		/* TODO: Implement Fallback Mechanism */
689d5c65159SKalle Valo 	}
690d5c65159SKalle Valo }
691d5c65159SKalle Valo 
692d5c65159SKalle Valo void ath11k_reg_init(struct ath11k *ar)
693d5c65159SKalle Valo {
694d5c65159SKalle Valo 	ar->hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED;
695d5c65159SKalle Valo 	ar->hw->wiphy->reg_notifier = ath11k_reg_notifier;
696d5c65159SKalle Valo }
697d5c65159SKalle Valo 
698d5c65159SKalle Valo void ath11k_reg_free(struct ath11k_base *ab)
699d5c65159SKalle Valo {
700d5c65159SKalle Valo 	int i;
701d5c65159SKalle Valo 
702b1cc29e9SAnilkumar Kolli 	for (i = 0; i < ab->hw_params.max_radios; i++) {
703d5c65159SKalle Valo 		kfree(ab->default_regd[i]);
704d5c65159SKalle Valo 		kfree(ab->new_regd[i]);
705d5c65159SKalle Valo 	}
706d5c65159SKalle Valo }
707