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 = ®d->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