xref: /linux/drivers/net/wireless/mediatek/mt76/mt7921/regd.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
1c5c32d63SJB Tsai // SPDX-License-Identifier: BSD-3-Clause-Clear
2c5c32d63SJB Tsai /* Copyright (C) 2025 MediaTek Inc. */
3c5c32d63SJB Tsai 
4c5c32d63SJB Tsai #include <linux/of.h>
5c5c32d63SJB Tsai #include "mt7921.h"
6c5c32d63SJB Tsai #include "regd.h"
7c5c32d63SJB Tsai #include "mcu.h"
8c5c32d63SJB Tsai 
9fbb493cdSJB Tsai static bool mt7921_disable_clc;
10fbb493cdSJB Tsai module_param_named(disable_clc, mt7921_disable_clc, bool, 0644);
11fbb493cdSJB Tsai MODULE_PARM_DESC(disable_clc, "disable CLC support");
12fbb493cdSJB Tsai 
13*9b80bd9cSCharlie-cy Wu static struct ieee80211_regdomain mt7921_regd_ww = {
14*9b80bd9cSCharlie-cy Wu 	.n_reg_rules = 1,
15*9b80bd9cSCharlie-cy Wu 	.alpha2 =  "00",
16*9b80bd9cSCharlie-cy Wu 	.reg_rules = {
17*9b80bd9cSCharlie-cy Wu 		/* IEEE 802.11b/g, channels 1..11 */
18*9b80bd9cSCharlie-cy Wu 		REG_RULE(2412 - 10, 2462 + 10, 40, 6, 20, 0),
19*9b80bd9cSCharlie-cy Wu 	}
20*9b80bd9cSCharlie-cy Wu };
21*9b80bd9cSCharlie-cy Wu 
mt7921_regd_clc_supported(struct mt792x_dev * dev)22fbb493cdSJB Tsai bool mt7921_regd_clc_supported(struct mt792x_dev *dev)
23fbb493cdSJB Tsai {
24fbb493cdSJB Tsai 	if (mt7921_disable_clc ||
25fbb493cdSJB Tsai 	    mt76_is_usb(&dev->mt76))
26fbb493cdSJB Tsai 		return false;
27fbb493cdSJB Tsai 
28fbb493cdSJB Tsai 	return true;
29fbb493cdSJB Tsai }
30fbb493cdSJB Tsai 
31c5c32d63SJB Tsai static void
mt7921_regd_channel_update(struct wiphy * wiphy,struct mt792x_dev * dev)32c5c32d63SJB Tsai mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
33c5c32d63SJB Tsai {
34c5c32d63SJB Tsai #define IS_UNII_INVALID(idx, sfreq, efreq) \
35c5c32d63SJB Tsai 	(!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq))
36c5c32d63SJB Tsai 	struct ieee80211_supported_band *sband;
37c5c32d63SJB Tsai 	struct mt76_dev *mdev = &dev->mt76;
38c5c32d63SJB Tsai 	struct device_node *np, *band_np;
39c5c32d63SJB Tsai 	struct ieee80211_channel *ch;
40c5c32d63SJB Tsai 	int i, cfreq;
41c5c32d63SJB Tsai 
42c5c32d63SJB Tsai 	np = mt76_find_power_limits_node(mdev);
43c5c32d63SJB Tsai 
44c5c32d63SJB Tsai 	sband = wiphy->bands[NL80211_BAND_5GHZ];
45*9b80bd9cSCharlie-cy Wu 	if (!sband)
46*9b80bd9cSCharlie-cy Wu 		return;
47*9b80bd9cSCharlie-cy Wu 
48c5c32d63SJB Tsai 	band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL;
49c5c32d63SJB Tsai 	for (i = 0; i < sband->n_channels; i++) {
50c5c32d63SJB Tsai 		ch = &sband->channels[i];
51c5c32d63SJB Tsai 		cfreq = ch->center_freq;
52c5c32d63SJB Tsai 
53c5c32d63SJB Tsai 		if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
54c5c32d63SJB Tsai 			ch->flags |= IEEE80211_CHAN_DISABLED;
55c5c32d63SJB Tsai 			continue;
56c5c32d63SJB Tsai 		}
57c5c32d63SJB Tsai 
58c5c32d63SJB Tsai 		/* UNII-4 */
59c5c32d63SJB Tsai 		if (IS_UNII_INVALID(0, 5845, 5925))
60c5c32d63SJB Tsai 			ch->flags |= IEEE80211_CHAN_DISABLED;
61c5c32d63SJB Tsai 	}
62c5c32d63SJB Tsai 
63c5c32d63SJB Tsai 	sband = wiphy->bands[NL80211_BAND_6GHZ];
64c5c32d63SJB Tsai 	if (!sband)
65c5c32d63SJB Tsai 		return;
66c5c32d63SJB Tsai 
67c5c32d63SJB Tsai 	band_np = np ? of_get_child_by_name(np, "txpower-6g") : NULL;
68c5c32d63SJB Tsai 	for (i = 0; i < sband->n_channels; i++) {
69c5c32d63SJB Tsai 		ch = &sband->channels[i];
70c5c32d63SJB Tsai 		cfreq = ch->center_freq;
71c5c32d63SJB Tsai 
72c5c32d63SJB Tsai 		if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
73c5c32d63SJB Tsai 			ch->flags |= IEEE80211_CHAN_DISABLED;
74c5c32d63SJB Tsai 			continue;
75c5c32d63SJB Tsai 		}
76c5c32d63SJB Tsai 
77c5c32d63SJB Tsai 		/* UNII-5/6/7/8 */
78c5c32d63SJB Tsai 		if (IS_UNII_INVALID(1, 5925, 6425) ||
79c5c32d63SJB Tsai 		    IS_UNII_INVALID(2, 6425, 6525) ||
80c5c32d63SJB Tsai 		    IS_UNII_INVALID(3, 6525, 6875) ||
81c5c32d63SJB Tsai 		    IS_UNII_INVALID(4, 6875, 7125))
82c5c32d63SJB Tsai 			ch->flags |= IEEE80211_CHAN_DISABLED;
83c5c32d63SJB Tsai 	}
84c5c32d63SJB Tsai }
85c5c32d63SJB Tsai 
__mt7921_mcu_regd_update(struct mt792x_dev * dev,u8 * alpha2,enum environment_cap country_ie_env)861f83a837SCharlie-cy Wu int __mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
87e8809813SJB Tsai 			     enum environment_cap country_ie_env)
88c5c32d63SJB Tsai {
89c5c32d63SJB Tsai 	struct mt76_dev *mdev = &dev->mt76;
90c5c32d63SJB Tsai 	struct ieee80211_hw *hw = mdev->hw;
91c5c32d63SJB Tsai 	struct wiphy *wiphy = hw->wiphy;
921f83a837SCharlie-cy Wu 	int ret;
93c5c32d63SJB Tsai 
941f83a837SCharlie-cy Wu 	lockdep_assert_held(&dev->mt76.mutex);
95e8809813SJB Tsai 
96e8809813SJB Tsai 	if (!dev->regd_change)
971f83a837SCharlie-cy Wu 		return 0;
98e8809813SJB Tsai 
99e8809813SJB Tsai 	ret = mt7921_mcu_set_clc(dev, alpha2, country_ie_env);
100e8809813SJB Tsai 	if (ret < 0)
1011f83a837SCharlie-cy Wu 		return ret;
102e8809813SJB Tsai 
103c5c32d63SJB Tsai 	mt7921_regd_channel_update(wiphy, dev);
104e8809813SJB Tsai 
105e8809813SJB Tsai 	ret = mt76_connac_mcu_set_channel_domain(hw->priv);
106e8809813SJB Tsai 	if (ret < 0)
1071f83a837SCharlie-cy Wu 		return ret;
108e8809813SJB Tsai 
109e8809813SJB Tsai 	ret = mt7921_set_tx_sar_pwr(hw, NULL);
110e8809813SJB Tsai 
1111f83a837SCharlie-cy Wu 	return ret;
1121f83a837SCharlie-cy Wu }
1131f83a837SCharlie-cy Wu 
mt7921_mcu_regd_update(struct mt792x_dev * dev,u8 * alpha2,enum environment_cap country_ie_env)1141f83a837SCharlie-cy Wu int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
1151f83a837SCharlie-cy Wu 			   enum environment_cap country_ie_env)
1161f83a837SCharlie-cy Wu {
1171f83a837SCharlie-cy Wu 	int ret;
1181f83a837SCharlie-cy Wu 
1191f83a837SCharlie-cy Wu 	dev->regd_in_progress = true;
1201f83a837SCharlie-cy Wu 
1211f83a837SCharlie-cy Wu 	mt792x_mutex_acquire(dev);
1221f83a837SCharlie-cy Wu 	ret = __mt7921_mcu_regd_update(dev, alpha2, country_ie_env);
123e8809813SJB Tsai 	mt792x_mutex_release(dev);
1241f83a837SCharlie-cy Wu 
125e8809813SJB Tsai 	dev->regd_change = false;
126e8809813SJB Tsai 	dev->regd_in_progress = false;
127e8809813SJB Tsai 	wake_up(&dev->wait);
128e8809813SJB Tsai 
129e8809813SJB Tsai 	return ret;
130c5c32d63SJB Tsai }
131e8809813SJB Tsai EXPORT_SYMBOL_GPL(mt7921_mcu_regd_update);
132c5c32d63SJB Tsai 
mt7921_regd_notifier(struct wiphy * wiphy,struct regulatory_request * req)133c5c32d63SJB Tsai void mt7921_regd_notifier(struct wiphy *wiphy,
134ae05055dSJB Tsai 			  struct regulatory_request *req)
135c5c32d63SJB Tsai {
136c5c32d63SJB Tsai 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
137c5c32d63SJB Tsai 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
138c5c32d63SJB Tsai 	struct mt76_connac_pm *pm = &dev->pm;
139ae05055dSJB Tsai 	struct mt76_dev *mdev = &dev->mt76;
140c5c32d63SJB Tsai 
1419c082046SJB Tsai 	if (req->initiator == NL80211_REGDOM_SET_BY_USER &&
1429c082046SJB Tsai 	    !dev->regd_user)
1439c082046SJB Tsai 		dev->regd_user = true;
1449c082046SJB Tsai 
145ae05055dSJB Tsai 	/* do not need to update the same country twice */
146ae05055dSJB Tsai 	if (!memcmp(req->alpha2, mdev->alpha2, 2) &&
147ae05055dSJB Tsai 	    dev->country_ie_env == req->country_ie_env)
148ae05055dSJB Tsai 		return;
149c5c32d63SJB Tsai 
150ae05055dSJB Tsai 	memcpy(mdev->alpha2, req->alpha2, 2);
151ae05055dSJB Tsai 	mdev->region = req->dfs_region;
152ae05055dSJB Tsai 	dev->country_ie_env = req->country_ie_env;
153ae05055dSJB Tsai 
154ae05055dSJB Tsai 	if (req->initiator == NL80211_REGDOM_SET_BY_USER) {
155c5c32d63SJB Tsai 		if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0')
156c5c32d63SJB Tsai 			wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE;
157c5c32d63SJB Tsai 		else
158c5c32d63SJB Tsai 			wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
159c5c32d63SJB Tsai 	}
160c5c32d63SJB Tsai 
161ae05055dSJB Tsai 	dev->regd_change = true;
162ae05055dSJB Tsai 
163c5c32d63SJB Tsai 	if (pm->suspended)
164c5c32d63SJB Tsai 		return;
165c5c32d63SJB Tsai 
166*9b80bd9cSCharlie-cy Wu 	if (MT7921_REGD_SUPPORTED(&dev->phy)) {
167*9b80bd9cSCharlie-cy Wu 		mt7921_regd_update(&dev->phy, req->alpha2);
168*9b80bd9cSCharlie-cy Wu 
169*9b80bd9cSCharlie-cy Wu 		return;
170*9b80bd9cSCharlie-cy Wu 	}
171*9b80bd9cSCharlie-cy Wu 
172ae05055dSJB Tsai 	mt7921_mcu_regd_update(dev, req->alpha2,
173ae05055dSJB Tsai 			       req->country_ie_env);
174ae05055dSJB Tsai }
175ae05055dSJB Tsai 
176*9b80bd9cSCharlie-cy Wu static struct sk_buff *
mt7921_regd_query_regdb(struct mt792x_phy * phy,char * alpha2)177*9b80bd9cSCharlie-cy Wu mt7921_regd_query_regdb(struct mt792x_phy *phy, char *alpha2)
178*9b80bd9cSCharlie-cy Wu {
179*9b80bd9cSCharlie-cy Wu 	struct wiphy *wiphy = phy->mt76->hw->wiphy;
180*9b80bd9cSCharlie-cy Wu 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
181*9b80bd9cSCharlie-cy Wu 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
182*9b80bd9cSCharlie-cy Wu 	struct mt7921_clc *clc = phy->clc[MT792x_CLC_REGD];
183*9b80bd9cSCharlie-cy Wu 	struct mt7921_regd_query_req *req;
184*9b80bd9cSCharlie-cy Wu 	struct mt7921_regd_cc *regd_cc;
185*9b80bd9cSCharlie-cy Wu 	struct sk_buff *ret_skb = NULL;
186*9b80bd9cSCharlie-cy Wu 	u8 *pos, *last_pos;
187*9b80bd9cSCharlie-cy Wu 	int ret = 0;
188*9b80bd9cSCharlie-cy Wu 
189*9b80bd9cSCharlie-cy Wu 	if (!clc)
190*9b80bd9cSCharlie-cy Wu 		return NULL;
191*9b80bd9cSCharlie-cy Wu 
192*9b80bd9cSCharlie-cy Wu 	pos = clc->data;
193*9b80bd9cSCharlie-cy Wu 	last_pos = pos + le32_to_cpu(clc->len) - sizeof(struct mt7921_clc);
194*9b80bd9cSCharlie-cy Wu 	while (pos < last_pos) {
195*9b80bd9cSCharlie-cy Wu 		u32 req_len = 0;
196*9b80bd9cSCharlie-cy Wu 		u32 rules_len = 0;
197*9b80bd9cSCharlie-cy Wu 		u32 sign_len = 4;
198*9b80bd9cSCharlie-cy Wu 		u32 n_reg_rules;
199*9b80bd9cSCharlie-cy Wu 
200*9b80bd9cSCharlie-cy Wu 		if (pos + sizeof(*regd_cc) > last_pos)
201*9b80bd9cSCharlie-cy Wu 			break;
202*9b80bd9cSCharlie-cy Wu 
203*9b80bd9cSCharlie-cy Wu 		regd_cc = (struct mt7921_regd_cc *)pos;
204*9b80bd9cSCharlie-cy Wu 		n_reg_rules = le32_to_cpu(regd_cc->n_reg_rules);
205*9b80bd9cSCharlie-cy Wu 		if (n_reg_rules > NL80211_MAX_SUPP_REG_RULES)
206*9b80bd9cSCharlie-cy Wu 			break;
207*9b80bd9cSCharlie-cy Wu 
208*9b80bd9cSCharlie-cy Wu 		rules_len = sizeof(struct mt7921_regd_rule_header) +
209*9b80bd9cSCharlie-cy Wu 			    sizeof(struct mt7921_regd_rule) * n_reg_rules;
210*9b80bd9cSCharlie-cy Wu 
211*9b80bd9cSCharlie-cy Wu 		if (pos + sizeof(*regd_cc) + rules_len + sign_len > last_pos)
212*9b80bd9cSCharlie-cy Wu 			break;
213*9b80bd9cSCharlie-cy Wu 
214*9b80bd9cSCharlie-cy Wu 		pos += sizeof(*regd_cc) + rules_len + sign_len;
215*9b80bd9cSCharlie-cy Wu 		if (memcmp(regd_cc->alpha2, alpha2, 2))
216*9b80bd9cSCharlie-cy Wu 			continue;
217*9b80bd9cSCharlie-cy Wu 
218*9b80bd9cSCharlie-cy Wu 		req_len = sizeof(*req) + rules_len + sign_len;
219*9b80bd9cSCharlie-cy Wu 		req = kzalloc(req_len, GFP_KERNEL);
220*9b80bd9cSCharlie-cy Wu 
221*9b80bd9cSCharlie-cy Wu 		if (!req)
222*9b80bd9cSCharlie-cy Wu 			return NULL;
223*9b80bd9cSCharlie-cy Wu 
224*9b80bd9cSCharlie-cy Wu 		req->ver = regd_cc->ver;
225*9b80bd9cSCharlie-cy Wu 		req->sign_type = regd_cc->sign_type;
226*9b80bd9cSCharlie-cy Wu 		req->size = cpu_to_le32(rules_len + sign_len);
227*9b80bd9cSCharlie-cy Wu 		req->n_reg_rules = regd_cc->n_reg_rules;
228*9b80bd9cSCharlie-cy Wu 
229*9b80bd9cSCharlie-cy Wu 		memcpy(req->alpha2, regd_cc->alpha2, 2);
230*9b80bd9cSCharlie-cy Wu 		memcpy(req->data, regd_cc->data, rules_len + sign_len);
231*9b80bd9cSCharlie-cy Wu 
232*9b80bd9cSCharlie-cy Wu 		ret = mt76_mcu_send_and_get_msg(&dev->mt76,
233*9b80bd9cSCharlie-cy Wu 						MCU_CE_CMD(SET_REGD_CH),
234*9b80bd9cSCharlie-cy Wu 						req, req_len, true, &ret_skb);
235*9b80bd9cSCharlie-cy Wu 
236*9b80bd9cSCharlie-cy Wu 		kfree(req);
237*9b80bd9cSCharlie-cy Wu 
238*9b80bd9cSCharlie-cy Wu 		return ret < 0 ? NULL : ret_skb;
239*9b80bd9cSCharlie-cy Wu 	}
240*9b80bd9cSCharlie-cy Wu 
241*9b80bd9cSCharlie-cy Wu 	return NULL;
242*9b80bd9cSCharlie-cy Wu }
243*9b80bd9cSCharlie-cy Wu 
mt7921_regd_update(struct mt792x_phy * phy,char * alpha2)244*9b80bd9cSCharlie-cy Wu int mt7921_regd_update(struct mt792x_phy *phy, char *alpha2)
245*9b80bd9cSCharlie-cy Wu {
246*9b80bd9cSCharlie-cy Wu 	struct wiphy *wiphy = phy->mt76->hw->wiphy;
247*9b80bd9cSCharlie-cy Wu 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
248*9b80bd9cSCharlie-cy Wu 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
249*9b80bd9cSCharlie-cy Wu 	struct mt7921_regd_rule *mt7921_rule;
250*9b80bd9cSCharlie-cy Wu 	struct mt76_dev *mdev = &dev->mt76;
251*9b80bd9cSCharlie-cy Wu 	struct ieee80211_regdomain *regd;
252*9b80bd9cSCharlie-cy Wu 	struct ieee80211_reg_rule *rule;
253*9b80bd9cSCharlie-cy Wu 	struct mt7921_regd_rule_ev *ev;
254*9b80bd9cSCharlie-cy Wu 	int i, num_of_rules = 0;
255*9b80bd9cSCharlie-cy Wu 	struct sk_buff *skb;
256*9b80bd9cSCharlie-cy Wu 	int ret = 0;
257*9b80bd9cSCharlie-cy Wu 
258*9b80bd9cSCharlie-cy Wu 	if (dev->hw_full_reset)
259*9b80bd9cSCharlie-cy Wu 		return 0;
260*9b80bd9cSCharlie-cy Wu 
261*9b80bd9cSCharlie-cy Wu 	if (!MT7921_REGD_SUPPORTED(phy))
262*9b80bd9cSCharlie-cy Wu 		return -EOPNOTSUPP;
263*9b80bd9cSCharlie-cy Wu 
264*9b80bd9cSCharlie-cy Wu 	mt792x_mutex_acquire(dev);
265*9b80bd9cSCharlie-cy Wu 	skb = mt7921_regd_query_regdb(phy, alpha2);
266*9b80bd9cSCharlie-cy Wu 	mt792x_mutex_release(dev);
267*9b80bd9cSCharlie-cy Wu 
268*9b80bd9cSCharlie-cy Wu 	if (!skb) {
269*9b80bd9cSCharlie-cy Wu 		ret = -EINVAL;
270*9b80bd9cSCharlie-cy Wu 		goto err;
271*9b80bd9cSCharlie-cy Wu 	}
272*9b80bd9cSCharlie-cy Wu 
273*9b80bd9cSCharlie-cy Wu 	if (skb->len < sizeof(*ev) + 4) {
274*9b80bd9cSCharlie-cy Wu 		ret = -EINVAL;
275*9b80bd9cSCharlie-cy Wu 		goto err;
276*9b80bd9cSCharlie-cy Wu 	}
277*9b80bd9cSCharlie-cy Wu 
278*9b80bd9cSCharlie-cy Wu 	ev = (struct mt7921_regd_rule_ev *)(skb->data + 4);
279*9b80bd9cSCharlie-cy Wu 	num_of_rules = le32_to_cpu(ev->n_reg_rules);
280*9b80bd9cSCharlie-cy Wu 
281*9b80bd9cSCharlie-cy Wu 	if (!num_of_rules ||
282*9b80bd9cSCharlie-cy Wu 	    WARN_ON_ONCE(num_of_rules > NL80211_MAX_SUPP_REG_RULES)) {
283*9b80bd9cSCharlie-cy Wu 		ret = -EINVAL;
284*9b80bd9cSCharlie-cy Wu 		goto err;
285*9b80bd9cSCharlie-cy Wu 	}
286*9b80bd9cSCharlie-cy Wu 
287*9b80bd9cSCharlie-cy Wu 	if (skb->len < struct_size(ev, reg_rule, num_of_rules) + 4) {
288*9b80bd9cSCharlie-cy Wu 		ret = -EINVAL;
289*9b80bd9cSCharlie-cy Wu 		goto err;
290*9b80bd9cSCharlie-cy Wu 	}
291*9b80bd9cSCharlie-cy Wu 
292*9b80bd9cSCharlie-cy Wu 	regd = kzalloc(struct_size(regd, reg_rules, num_of_rules), GFP_KERNEL);
293*9b80bd9cSCharlie-cy Wu 	if (!regd) {
294*9b80bd9cSCharlie-cy Wu 		ret = -ENOMEM;
295*9b80bd9cSCharlie-cy Wu 		goto err;
296*9b80bd9cSCharlie-cy Wu 	}
297*9b80bd9cSCharlie-cy Wu 
298*9b80bd9cSCharlie-cy Wu 	for (i = 0; i < num_of_rules; i++) {
299*9b80bd9cSCharlie-cy Wu 		mt7921_rule = &ev->reg_rule[i];
300*9b80bd9cSCharlie-cy Wu 		rule = &regd->reg_rules[i];
301*9b80bd9cSCharlie-cy Wu 
302*9b80bd9cSCharlie-cy Wu 		rule->freq_range.start_freq_khz =
303*9b80bd9cSCharlie-cy Wu 			MHZ_TO_KHZ(le32_to_cpu(mt7921_rule->start_freq));
304*9b80bd9cSCharlie-cy Wu 		rule->freq_range.end_freq_khz =
305*9b80bd9cSCharlie-cy Wu 			MHZ_TO_KHZ(le32_to_cpu(mt7921_rule->end_freq));
306*9b80bd9cSCharlie-cy Wu 		rule->freq_range.max_bandwidth_khz =
307*9b80bd9cSCharlie-cy Wu 			MHZ_TO_KHZ(le32_to_cpu(mt7921_rule->max_bw));
308*9b80bd9cSCharlie-cy Wu 		/* not used by fw */
309*9b80bd9cSCharlie-cy Wu 		rule->power_rule.max_antenna_gain = DBI_TO_MBI(6);
310*9b80bd9cSCharlie-cy Wu 		rule->power_rule.max_eirp = DBM_TO_MBM(22);
311*9b80bd9cSCharlie-cy Wu 		rule->flags = le32_to_cpu(mt7921_rule->flags);
312*9b80bd9cSCharlie-cy Wu 	}
313*9b80bd9cSCharlie-cy Wu 
314*9b80bd9cSCharlie-cy Wu 	regd->n_reg_rules = num_of_rules;
315*9b80bd9cSCharlie-cy Wu 	regd->dfs_region = ev->dfs_region;
316*9b80bd9cSCharlie-cy Wu 
317*9b80bd9cSCharlie-cy Wu 	memcpy(regd->alpha2, alpha2, 2);
318*9b80bd9cSCharlie-cy Wu 	memcpy(mdev->alpha2, alpha2, 2);
319*9b80bd9cSCharlie-cy Wu 
320*9b80bd9cSCharlie-cy Wu 	dev->regd_change = true;
321*9b80bd9cSCharlie-cy Wu 	mt7921_mcu_regd_update(dev, alpha2, ENVIRON_ANY);
322*9b80bd9cSCharlie-cy Wu 
323*9b80bd9cSCharlie-cy Wu 	ret = regulatory_set_wiphy_regd(wiphy, regd);
324*9b80bd9cSCharlie-cy Wu 
325*9b80bd9cSCharlie-cy Wu 	kfree(regd);
326*9b80bd9cSCharlie-cy Wu err:
327*9b80bd9cSCharlie-cy Wu 	dev_kfree_skb(skb);
328*9b80bd9cSCharlie-cy Wu 
329*9b80bd9cSCharlie-cy Wu 	if (ret < 0)
330*9b80bd9cSCharlie-cy Wu 		return regulatory_set_wiphy_regd(wiphy, &mt7921_regd_ww);
331*9b80bd9cSCharlie-cy Wu 
332*9b80bd9cSCharlie-cy Wu 	return ret;
333*9b80bd9cSCharlie-cy Wu }
334*9b80bd9cSCharlie-cy Wu EXPORT_SYMBOL_GPL(mt7921_regd_update);
335*9b80bd9cSCharlie-cy Wu 
336ae05055dSJB Tsai static bool
mt7921_regd_is_valid_alpha2(const char * alpha2)337ae05055dSJB Tsai mt7921_regd_is_valid_alpha2(const char *alpha2)
338ae05055dSJB Tsai {
339ae05055dSJB Tsai 	if (!alpha2)
340ae05055dSJB Tsai 		return false;
341ae05055dSJB Tsai 
342ae05055dSJB Tsai 	if (alpha2[0] == '0' && alpha2[1] == '0')
343ae05055dSJB Tsai 		return true;
344ae05055dSJB Tsai 
345ae05055dSJB Tsai 	if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
346ae05055dSJB Tsai 		return true;
347ae05055dSJB Tsai 
348ae05055dSJB Tsai 	return false;
349ae05055dSJB Tsai }
350ae05055dSJB Tsai 
mt7921_regd_change(struct mt792x_phy * phy,char * alpha2)351ae05055dSJB Tsai int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2)
352ae05055dSJB Tsai {
353ae05055dSJB Tsai 	struct wiphy *wiphy = phy->mt76->hw->wiphy;
354ae05055dSJB Tsai 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
355ae05055dSJB Tsai 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
356ae05055dSJB Tsai 	struct mt76_dev *mdev = &dev->mt76;
357ae05055dSJB Tsai 
358ae05055dSJB Tsai 	if (dev->hw_full_reset)
359ae05055dSJB Tsai 		return 0;
360ae05055dSJB Tsai 
361ae05055dSJB Tsai 	if (!mt7921_regd_is_valid_alpha2(alpha2) ||
3629c082046SJB Tsai 	    !mt7921_regd_clc_supported(dev) ||
3639c082046SJB Tsai 	    dev->regd_user)
364ae05055dSJB Tsai 		return -EINVAL;
365ae05055dSJB Tsai 
366ae05055dSJB Tsai 	if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0')
367ae05055dSJB Tsai 		return 0;
368ae05055dSJB Tsai 
369ae05055dSJB Tsai 	/* do not need to update the same country twice */
370ae05055dSJB Tsai 	if (!memcmp(alpha2, mdev->alpha2, 2))
371ae05055dSJB Tsai 		return 0;
372ae05055dSJB Tsai 
373*9b80bd9cSCharlie-cy Wu 	if (MT7921_REGD_SUPPORTED(phy))
374*9b80bd9cSCharlie-cy Wu 		return mt7921_regd_update(phy, alpha2);
375*9b80bd9cSCharlie-cy Wu 	else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
376ae05055dSJB Tsai 		return regulatory_hint(wiphy, alpha2);
377ae05055dSJB Tsai 	else
378ae05055dSJB Tsai 		return mt7921_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR);
379ae05055dSJB Tsai }
380ae05055dSJB Tsai 
mt7921_regd_init(struct mt792x_phy * phy)381ae05055dSJB Tsai int mt7921_regd_init(struct mt792x_phy *phy)
382ae05055dSJB Tsai {
383ae05055dSJB Tsai 	struct wiphy *wiphy = phy->mt76->hw->wiphy;
384ae05055dSJB Tsai 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
385ae05055dSJB Tsai 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
386ae05055dSJB Tsai 	struct mt76_dev *mdev = &dev->mt76;
387ae05055dSJB Tsai 
388*9b80bd9cSCharlie-cy Wu 	if (MT7921_REGD_SUPPORTED(phy)) {
389*9b80bd9cSCharlie-cy Wu 		wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED |
390*9b80bd9cSCharlie-cy Wu 					   REGULATORY_DISABLE_BEACON_HINTS;
391*9b80bd9cSCharlie-cy Wu 		return mt7921_regd_update(phy, "00");
392*9b80bd9cSCharlie-cy Wu 	} else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
393ae05055dSJB Tsai 		wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE |
394ae05055dSJB Tsai 					   REGULATORY_DISABLE_BEACON_HINTS;
395ae05055dSJB Tsai 	else
396ae05055dSJB Tsai 		memzero_explicit(&mdev->alpha2, sizeof(mdev->alpha2));
397ae05055dSJB Tsai 
398ae05055dSJB Tsai 	return 0;
399c5c32d63SJB Tsai }
400