1*b1bebaabSBjoern A. Zeeb // SPDX-License-Identifier: BSD-3-Clause-Clear
2*b1bebaabSBjoern A. Zeeb /* Copyright (C) 2025 MediaTek Inc. */
3*b1bebaabSBjoern A. Zeeb
4*b1bebaabSBjoern A. Zeeb #include "mt7925.h"
5*b1bebaabSBjoern A. Zeeb #include "regd.h"
6*b1bebaabSBjoern A. Zeeb #include "mcu.h"
7*b1bebaabSBjoern A. Zeeb
8*b1bebaabSBjoern A. Zeeb static bool mt7925_disable_clc;
9*b1bebaabSBjoern A. Zeeb module_param_named(disable_clc, mt7925_disable_clc, bool, 0644);
10*b1bebaabSBjoern A. Zeeb MODULE_PARM_DESC(disable_clc, "disable CLC support");
11*b1bebaabSBjoern A. Zeeb
mt7925_regd_clc_supported(struct mt792x_dev * dev)12*b1bebaabSBjoern A. Zeeb bool mt7925_regd_clc_supported(struct mt792x_dev *dev)
13*b1bebaabSBjoern A. Zeeb {
14*b1bebaabSBjoern A. Zeeb if (mt7925_disable_clc ||
15*b1bebaabSBjoern A. Zeeb mt76_is_usb(&dev->mt76))
16*b1bebaabSBjoern A. Zeeb return false;
17*b1bebaabSBjoern A. Zeeb
18*b1bebaabSBjoern A. Zeeb return true;
19*b1bebaabSBjoern A. Zeeb }
20*b1bebaabSBjoern A. Zeeb
mt7925_regd_be_ctrl(struct mt792x_dev * dev,u8 * alpha2)21*b1bebaabSBjoern A. Zeeb void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2)
22*b1bebaabSBjoern A. Zeeb {
23*b1bebaabSBjoern A. Zeeb struct mt792x_phy *phy = &dev->phy;
24*b1bebaabSBjoern A. Zeeb struct mt7925_clc_rule_v2 *rule;
25*b1bebaabSBjoern A. Zeeb struct mt7925_clc *clc;
26*b1bebaabSBjoern A. Zeeb bool old = dev->has_eht, new = true;
27*b1bebaabSBjoern A. Zeeb u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, alpha2);
28*b1bebaabSBjoern A. Zeeb u8 *pos;
29*b1bebaabSBjoern A. Zeeb
30*b1bebaabSBjoern A. Zeeb if (mtcl_conf != MT792X_ACPI_MTCL_INVALID &&
31*b1bebaabSBjoern A. Zeeb (((mtcl_conf >> 4) & 0x3) == 0)) {
32*b1bebaabSBjoern A. Zeeb new = false;
33*b1bebaabSBjoern A. Zeeb goto out;
34*b1bebaabSBjoern A. Zeeb }
35*b1bebaabSBjoern A. Zeeb
36*b1bebaabSBjoern A. Zeeb if (!phy->clc[MT792x_CLC_BE_CTRL])
37*b1bebaabSBjoern A. Zeeb goto out;
38*b1bebaabSBjoern A. Zeeb
39*b1bebaabSBjoern A. Zeeb clc = (struct mt7925_clc *)phy->clc[MT792x_CLC_BE_CTRL];
40*b1bebaabSBjoern A. Zeeb pos = clc->data;
41*b1bebaabSBjoern A. Zeeb
42*b1bebaabSBjoern A. Zeeb while (1) {
43*b1bebaabSBjoern A. Zeeb rule = (struct mt7925_clc_rule_v2 *)pos;
44*b1bebaabSBjoern A. Zeeb
45*b1bebaabSBjoern A. Zeeb if (rule->alpha2[0] == alpha2[0] &&
46*b1bebaabSBjoern A. Zeeb rule->alpha2[1] == alpha2[1]) {
47*b1bebaabSBjoern A. Zeeb new = false;
48*b1bebaabSBjoern A. Zeeb break;
49*b1bebaabSBjoern A. Zeeb }
50*b1bebaabSBjoern A. Zeeb
51*b1bebaabSBjoern A. Zeeb /* Check the last one */
52*b1bebaabSBjoern A. Zeeb if (rule->flag & BIT(0))
53*b1bebaabSBjoern A. Zeeb break;
54*b1bebaabSBjoern A. Zeeb
55*b1bebaabSBjoern A. Zeeb pos += sizeof(*rule);
56*b1bebaabSBjoern A. Zeeb }
57*b1bebaabSBjoern A. Zeeb
58*b1bebaabSBjoern A. Zeeb out:
59*b1bebaabSBjoern A. Zeeb if (old == new)
60*b1bebaabSBjoern A. Zeeb return;
61*b1bebaabSBjoern A. Zeeb
62*b1bebaabSBjoern A. Zeeb dev->has_eht = new;
63*b1bebaabSBjoern A. Zeeb mt7925_set_stream_he_eht_caps(phy);
64*b1bebaabSBjoern A. Zeeb }
65*b1bebaabSBjoern A. Zeeb
66*b1bebaabSBjoern A. Zeeb static void
mt7925_regd_channel_update(struct wiphy * wiphy,struct mt792x_dev * dev)67*b1bebaabSBjoern A. Zeeb mt7925_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
68*b1bebaabSBjoern A. Zeeb {
69*b1bebaabSBjoern A. Zeeb #define IS_UNII_INVALID(idx, sfreq, efreq, cfreq) \
70*b1bebaabSBjoern A. Zeeb (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq))
71*b1bebaabSBjoern A. Zeeb #define MT7925_UNII_59G_IS_VALID 0x1
72*b1bebaabSBjoern A. Zeeb #define MT7925_UNII_6G_IS_VALID 0x1e
73*b1bebaabSBjoern A. Zeeb struct ieee80211_supported_band *sband;
74*b1bebaabSBjoern A. Zeeb struct mt76_dev *mdev = &dev->mt76;
75*b1bebaabSBjoern A. Zeeb struct ieee80211_channel *ch;
76*b1bebaabSBjoern A. Zeeb u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, mdev->alpha2);
77*b1bebaabSBjoern A. Zeeb int i;
78*b1bebaabSBjoern A. Zeeb
79*b1bebaabSBjoern A. Zeeb if (mtcl_conf != MT792X_ACPI_MTCL_INVALID) {
80*b1bebaabSBjoern A. Zeeb if ((mtcl_conf & 0x3) == 0)
81*b1bebaabSBjoern A. Zeeb dev->phy.clc_chan_conf &= ~MT7925_UNII_59G_IS_VALID;
82*b1bebaabSBjoern A. Zeeb if (((mtcl_conf >> 2) & 0x3) == 0)
83*b1bebaabSBjoern A. Zeeb dev->phy.clc_chan_conf &= ~MT7925_UNII_6G_IS_VALID;
84*b1bebaabSBjoern A. Zeeb }
85*b1bebaabSBjoern A. Zeeb
86*b1bebaabSBjoern A. Zeeb sband = wiphy->bands[NL80211_BAND_2GHZ];
87*b1bebaabSBjoern A. Zeeb if (!sband)
88*b1bebaabSBjoern A. Zeeb return;
89*b1bebaabSBjoern A. Zeeb
90*b1bebaabSBjoern A. Zeeb for (i = 0; i < sband->n_channels; i++) {
91*b1bebaabSBjoern A. Zeeb ch = &sband->channels[i];
92*b1bebaabSBjoern A. Zeeb
93*b1bebaabSBjoern A. Zeeb if (!dev->has_eht)
94*b1bebaabSBjoern A. Zeeb ch->flags |= IEEE80211_CHAN_NO_EHT;
95*b1bebaabSBjoern A. Zeeb }
96*b1bebaabSBjoern A. Zeeb
97*b1bebaabSBjoern A. Zeeb sband = wiphy->bands[NL80211_BAND_5GHZ];
98*b1bebaabSBjoern A. Zeeb if (!sband)
99*b1bebaabSBjoern A. Zeeb return;
100*b1bebaabSBjoern A. Zeeb
101*b1bebaabSBjoern A. Zeeb for (i = 0; i < sband->n_channels; i++) {
102*b1bebaabSBjoern A. Zeeb ch = &sband->channels[i];
103*b1bebaabSBjoern A. Zeeb
104*b1bebaabSBjoern A. Zeeb /* UNII-4 */
105*b1bebaabSBjoern A. Zeeb if (IS_UNII_INVALID(0, 5845, 5925, ch->center_freq))
106*b1bebaabSBjoern A. Zeeb ch->flags |= IEEE80211_CHAN_DISABLED;
107*b1bebaabSBjoern A. Zeeb
108*b1bebaabSBjoern A. Zeeb if (!dev->has_eht)
109*b1bebaabSBjoern A. Zeeb ch->flags |= IEEE80211_CHAN_NO_EHT;
110*b1bebaabSBjoern A. Zeeb }
111*b1bebaabSBjoern A. Zeeb
112*b1bebaabSBjoern A. Zeeb sband = wiphy->bands[NL80211_BAND_6GHZ];
113*b1bebaabSBjoern A. Zeeb if (!sband)
114*b1bebaabSBjoern A. Zeeb return;
115*b1bebaabSBjoern A. Zeeb
116*b1bebaabSBjoern A. Zeeb for (i = 0; i < sband->n_channels; i++) {
117*b1bebaabSBjoern A. Zeeb ch = &sband->channels[i];
118*b1bebaabSBjoern A. Zeeb
119*b1bebaabSBjoern A. Zeeb /* UNII-5/6/7/8 */
120*b1bebaabSBjoern A. Zeeb if (IS_UNII_INVALID(1, 5925, 6425, ch->center_freq) ||
121*b1bebaabSBjoern A. Zeeb IS_UNII_INVALID(2, 6425, 6525, ch->center_freq) ||
122*b1bebaabSBjoern A. Zeeb IS_UNII_INVALID(3, 6525, 6875, ch->center_freq) ||
123*b1bebaabSBjoern A. Zeeb IS_UNII_INVALID(4, 6875, 7125, ch->center_freq))
124*b1bebaabSBjoern A. Zeeb ch->flags |= IEEE80211_CHAN_DISABLED;
125*b1bebaabSBjoern A. Zeeb
126*b1bebaabSBjoern A. Zeeb if (!dev->has_eht)
127*b1bebaabSBjoern A. Zeeb ch->flags |= IEEE80211_CHAN_NO_EHT;
128*b1bebaabSBjoern A. Zeeb }
129*b1bebaabSBjoern A. Zeeb }
130*b1bebaabSBjoern A. Zeeb
mt7925_mcu_regd_update(struct mt792x_dev * dev,u8 * alpha2,enum environment_cap country_ie_env)131*b1bebaabSBjoern A. Zeeb int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
132*b1bebaabSBjoern A. Zeeb enum environment_cap country_ie_env)
133*b1bebaabSBjoern A. Zeeb {
134*b1bebaabSBjoern A. Zeeb struct ieee80211_hw *hw = mt76_hw(dev);
135*b1bebaabSBjoern A. Zeeb struct wiphy *wiphy = hw->wiphy;
136*b1bebaabSBjoern A. Zeeb int ret = 0;
137*b1bebaabSBjoern A. Zeeb
138*b1bebaabSBjoern A. Zeeb dev->regd_in_progress = true;
139*b1bebaabSBjoern A. Zeeb
140*b1bebaabSBjoern A. Zeeb mt792x_mutex_acquire(dev);
141*b1bebaabSBjoern A. Zeeb if (!dev->regd_change)
142*b1bebaabSBjoern A. Zeeb goto err;
143*b1bebaabSBjoern A. Zeeb
144*b1bebaabSBjoern A. Zeeb ret = mt7925_mcu_set_clc(dev, alpha2, country_ie_env);
145*b1bebaabSBjoern A. Zeeb if (ret < 0)
146*b1bebaabSBjoern A. Zeeb goto err;
147*b1bebaabSBjoern A. Zeeb
148*b1bebaabSBjoern A. Zeeb mt7925_regd_be_ctrl(dev, alpha2);
149*b1bebaabSBjoern A. Zeeb mt7925_regd_channel_update(wiphy, dev);
150*b1bebaabSBjoern A. Zeeb
151*b1bebaabSBjoern A. Zeeb ret = mt7925_mcu_set_channel_domain(hw->priv);
152*b1bebaabSBjoern A. Zeeb if (ret < 0)
153*b1bebaabSBjoern A. Zeeb goto err;
154*b1bebaabSBjoern A. Zeeb
155*b1bebaabSBjoern A. Zeeb ret = mt7925_set_tx_sar_pwr(hw, NULL);
156*b1bebaabSBjoern A. Zeeb if (ret < 0)
157*b1bebaabSBjoern A. Zeeb goto err;
158*b1bebaabSBjoern A. Zeeb
159*b1bebaabSBjoern A. Zeeb err:
160*b1bebaabSBjoern A. Zeeb mt792x_mutex_release(dev);
161*b1bebaabSBjoern A. Zeeb dev->regd_change = false;
162*b1bebaabSBjoern A. Zeeb dev->regd_in_progress = false;
163*b1bebaabSBjoern A. Zeeb wake_up(&dev->wait);
164*b1bebaabSBjoern A. Zeeb
165*b1bebaabSBjoern A. Zeeb return ret;
166*b1bebaabSBjoern A. Zeeb }
167*b1bebaabSBjoern A. Zeeb EXPORT_SYMBOL_GPL(mt7925_mcu_regd_update);
168*b1bebaabSBjoern A. Zeeb
mt7925_regd_notifier(struct wiphy * wiphy,struct regulatory_request * req)169*b1bebaabSBjoern A. Zeeb void mt7925_regd_notifier(struct wiphy *wiphy, struct regulatory_request *req)
170*b1bebaabSBjoern A. Zeeb {
171*b1bebaabSBjoern A. Zeeb struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
172*b1bebaabSBjoern A. Zeeb struct mt792x_dev *dev = mt792x_hw_dev(hw);
173*b1bebaabSBjoern A. Zeeb struct mt76_connac_pm *pm = &dev->pm;
174*b1bebaabSBjoern A. Zeeb struct mt76_dev *mdev = &dev->mt76;
175*b1bebaabSBjoern A. Zeeb
176*b1bebaabSBjoern A. Zeeb if (req->initiator == NL80211_REGDOM_SET_BY_USER &&
177*b1bebaabSBjoern A. Zeeb !dev->regd_user)
178*b1bebaabSBjoern A. Zeeb dev->regd_user = true;
179*b1bebaabSBjoern A. Zeeb
180*b1bebaabSBjoern A. Zeeb /* allow world regdom at the first boot only */
181*b1bebaabSBjoern A. Zeeb if (!memcmp(req->alpha2, "00", 2) &&
182*b1bebaabSBjoern A. Zeeb mdev->alpha2[0] && mdev->alpha2[1])
183*b1bebaabSBjoern A. Zeeb return;
184*b1bebaabSBjoern A. Zeeb
185*b1bebaabSBjoern A. Zeeb /* do not need to update the same country twice */
186*b1bebaabSBjoern A. Zeeb if (!memcmp(req->alpha2, mdev->alpha2, 2) &&
187*b1bebaabSBjoern A. Zeeb dev->country_ie_env == req->country_ie_env)
188*b1bebaabSBjoern A. Zeeb return;
189*b1bebaabSBjoern A. Zeeb
190*b1bebaabSBjoern A. Zeeb memcpy(mdev->alpha2, req->alpha2, 2);
191*b1bebaabSBjoern A. Zeeb mdev->region = req->dfs_region;
192*b1bebaabSBjoern A. Zeeb dev->country_ie_env = req->country_ie_env;
193*b1bebaabSBjoern A. Zeeb
194*b1bebaabSBjoern A. Zeeb dev->regd_change = true;
195*b1bebaabSBjoern A. Zeeb
196*b1bebaabSBjoern A. Zeeb if (pm->suspended)
197*b1bebaabSBjoern A. Zeeb /* postpone the mcu update to resume */
198*b1bebaabSBjoern A. Zeeb return;
199*b1bebaabSBjoern A. Zeeb
200*b1bebaabSBjoern A. Zeeb mt7925_mcu_regd_update(dev, req->alpha2,
201*b1bebaabSBjoern A. Zeeb req->country_ie_env);
202*b1bebaabSBjoern A. Zeeb return;
203*b1bebaabSBjoern A. Zeeb }
204*b1bebaabSBjoern A. Zeeb
205*b1bebaabSBjoern A. Zeeb static bool
mt7925_regd_is_valid_alpha2(const char * alpha2)206*b1bebaabSBjoern A. Zeeb mt7925_regd_is_valid_alpha2(const char *alpha2)
207*b1bebaabSBjoern A. Zeeb {
208*b1bebaabSBjoern A. Zeeb if (!alpha2)
209*b1bebaabSBjoern A. Zeeb return false;
210*b1bebaabSBjoern A. Zeeb
211*b1bebaabSBjoern A. Zeeb if (alpha2[0] == '0' && alpha2[1] == '0')
212*b1bebaabSBjoern A. Zeeb return true;
213*b1bebaabSBjoern A. Zeeb
214*b1bebaabSBjoern A. Zeeb if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
215*b1bebaabSBjoern A. Zeeb return true;
216*b1bebaabSBjoern A. Zeeb
217*b1bebaabSBjoern A. Zeeb return false;
218*b1bebaabSBjoern A. Zeeb }
219*b1bebaabSBjoern A. Zeeb
mt7925_regd_change(struct mt792x_phy * phy,char * alpha2)220*b1bebaabSBjoern A. Zeeb int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2)
221*b1bebaabSBjoern A. Zeeb {
222*b1bebaabSBjoern A. Zeeb struct wiphy *wiphy = phy->mt76->hw->wiphy;
223*b1bebaabSBjoern A. Zeeb struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
224*b1bebaabSBjoern A. Zeeb struct mt792x_dev *dev = mt792x_hw_dev(hw);
225*b1bebaabSBjoern A. Zeeb struct mt76_dev *mdev = &dev->mt76;
226*b1bebaabSBjoern A. Zeeb
227*b1bebaabSBjoern A. Zeeb if (dev->hw_full_reset)
228*b1bebaabSBjoern A. Zeeb return 0;
229*b1bebaabSBjoern A. Zeeb
230*b1bebaabSBjoern A. Zeeb if (!mt7925_regd_is_valid_alpha2(alpha2) ||
231*b1bebaabSBjoern A. Zeeb !mt7925_regd_clc_supported(dev) ||
232*b1bebaabSBjoern A. Zeeb dev->regd_user)
233*b1bebaabSBjoern A. Zeeb return -EINVAL;
234*b1bebaabSBjoern A. Zeeb
235*b1bebaabSBjoern A. Zeeb if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0')
236*b1bebaabSBjoern A. Zeeb return 0;
237*b1bebaabSBjoern A. Zeeb
238*b1bebaabSBjoern A. Zeeb /* do not need to update the same country twice */
239*b1bebaabSBjoern A. Zeeb if (!memcmp(alpha2, mdev->alpha2, 2))
240*b1bebaabSBjoern A. Zeeb return 0;
241*b1bebaabSBjoern A. Zeeb
242*b1bebaabSBjoern A. Zeeb if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) {
243*b1bebaabSBjoern A. Zeeb return regulatory_hint(wiphy, alpha2);
244*b1bebaabSBjoern A. Zeeb } else {
245*b1bebaabSBjoern A. Zeeb return mt7925_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR);
246*b1bebaabSBjoern A. Zeeb }
247*b1bebaabSBjoern A. Zeeb }
248*b1bebaabSBjoern A. Zeeb EXPORT_SYMBOL_GPL(mt7925_regd_change);
249*b1bebaabSBjoern A. Zeeb
mt7925_regd_init(struct mt792x_phy * phy)250*b1bebaabSBjoern A. Zeeb int mt7925_regd_init(struct mt792x_phy *phy)
251*b1bebaabSBjoern A. Zeeb {
252*b1bebaabSBjoern A. Zeeb struct wiphy *wiphy = phy->mt76->hw->wiphy;
253*b1bebaabSBjoern A. Zeeb struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
254*b1bebaabSBjoern A. Zeeb struct mt792x_dev *dev = mt792x_hw_dev(hw);
255*b1bebaabSBjoern A. Zeeb struct mt76_dev *mdev = &dev->mt76;
256*b1bebaabSBjoern A. Zeeb
257*b1bebaabSBjoern A. Zeeb if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) {
258*b1bebaabSBjoern A. Zeeb wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE |
259*b1bebaabSBjoern A. Zeeb REGULATORY_DISABLE_BEACON_HINTS;
260*b1bebaabSBjoern A. Zeeb } else {
261*b1bebaabSBjoern A. Zeeb memzero_explicit(&mdev->alpha2, sizeof(mdev->alpha2));
262*b1bebaabSBjoern A. Zeeb }
263*b1bebaabSBjoern A. Zeeb
264*b1bebaabSBjoern A. Zeeb return 0;
265*b1bebaabSBjoern A. Zeeb }
266