xref: /openbmc/linux/drivers/net/wireless/mediatek/mt76/mt76x0/main.c (revision 05cf4fe738242183f1237f1b3a28b4479348c0a1)
1 /*
2  * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
3  * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>
4  * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2
8  * as published by the Free Software Foundation
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  */
15 
16 #include <linux/etherdevice.h>
17 #include "mt76x0.h"
18 
19 static int
20 mt76x0_set_channel(struct mt76x02_dev *dev, struct cfg80211_chan_def *chandef)
21 {
22 	int ret;
23 
24 	cancel_delayed_work_sync(&dev->cal_work);
25 
26 	mt76_set_channel(&dev->mt76);
27 	ret = mt76x0_phy_set_channel(dev, chandef);
28 	mt76_txq_schedule_all(&dev->mt76);
29 
30 	return ret;
31 }
32 
33 int mt76x0_config(struct ieee80211_hw *hw, u32 changed)
34 {
35 	struct mt76x02_dev *dev = hw->priv;
36 	int ret = 0;
37 
38 	mutex_lock(&dev->mt76.mutex);
39 
40 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
41 		ieee80211_stop_queues(hw);
42 		ret = mt76x0_set_channel(dev, &hw->conf.chandef);
43 		ieee80211_wake_queues(hw);
44 	}
45 
46 	if (changed & IEEE80211_CONF_CHANGE_POWER) {
47 		dev->mt76.txpower_conf = hw->conf.power_level * 2;
48 
49 		if (test_bit(MT76_STATE_RUNNING, &dev->mt76.state))
50 			mt76x0_phy_set_txpower(dev);
51 	}
52 
53 	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
54 		if (!(hw->conf.flags & IEEE80211_CONF_MONITOR))
55 			dev->mt76.rxfilter |= MT_RX_FILTR_CFG_PROMISC;
56 		else
57 			dev->mt76.rxfilter &= ~MT_RX_FILTR_CFG_PROMISC;
58 
59 		mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter);
60 	}
61 
62 	mutex_unlock(&dev->mt76.mutex);
63 
64 	return ret;
65 }
66 EXPORT_SYMBOL_GPL(mt76x0_config);
67 
68 static void
69 mt76x0_addr_wr(struct mt76x02_dev *dev, const u32 offset, const u8 *addr)
70 {
71 	mt76_wr(dev, offset, get_unaligned_le32(addr));
72 	mt76_wr(dev, offset + 4, addr[4] | addr[5] << 8);
73 }
74 
75 void mt76x0_bss_info_changed(struct ieee80211_hw *hw,
76 			     struct ieee80211_vif *vif,
77 			     struct ieee80211_bss_conf *info, u32 changed)
78 {
79 	struct mt76x02_dev *dev = hw->priv;
80 
81 	mutex_lock(&dev->mt76.mutex);
82 
83 	if (changed & BSS_CHANGED_BSSID) {
84 		mt76x0_addr_wr(dev, MT_MAC_BSSID_DW0, info->bssid);
85 
86 		/* Note: this is a hack because beacon_int is not changed
87 		 *	 on leave nor is any more appropriate event generated.
88 		 *	 rt2x00 doesn't seem to be bothered though.
89 		 */
90 		if (is_zero_ether_addr(info->bssid))
91 			mt76x0_mac_config_tsf(dev, false, 0);
92 	}
93 
94 	if (changed & BSS_CHANGED_BASIC_RATES) {
95 		mt76_wr(dev, MT_LEGACY_BASIC_RATE, info->basic_rates);
96 		mt76_wr(dev, MT_VHT_HT_FBK_CFG0, 0x65432100);
97 		mt76_wr(dev, MT_VHT_HT_FBK_CFG1, 0xedcba980);
98 		mt76_wr(dev, MT_LG_FBK_CFG0, 0xedcba988);
99 		mt76_wr(dev, MT_LG_FBK_CFG1, 0x00002100);
100 	}
101 
102 	if (changed & BSS_CHANGED_BEACON_INT)
103 		mt76x0_mac_config_tsf(dev, true, info->beacon_int);
104 
105 	if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT)
106 		mt76x0_mac_set_protection(dev, info->use_cts_prot,
107 					   info->ht_operation_mode);
108 
109 	if (changed & BSS_CHANGED_ERP_PREAMBLE)
110 		mt76x0_mac_set_short_preamble(dev, info->use_short_preamble);
111 
112 	if (changed & BSS_CHANGED_ERP_SLOT) {
113 		int slottime = info->use_short_slot ? 9 : 20;
114 
115 		mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG,
116 			       MT_BKOFF_SLOT_CFG_SLOTTIME, slottime);
117 	}
118 
119 	if (changed & BSS_CHANGED_ASSOC)
120 		mt76x0_phy_recalibrate_after_assoc(dev);
121 
122 	mutex_unlock(&dev->mt76.mutex);
123 }
124 EXPORT_SYMBOL_GPL(mt76x0_bss_info_changed);
125 
126 void mt76x0_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
127 		    const u8 *mac_addr)
128 {
129 	struct mt76x02_dev *dev = hw->priv;
130 
131 	set_bit(MT76_SCANNING, &dev->mt76.state);
132 }
133 EXPORT_SYMBOL_GPL(mt76x0_sw_scan);
134 
135 void mt76x0_sw_scan_complete(struct ieee80211_hw *hw,
136 			     struct ieee80211_vif *vif)
137 {
138 	struct mt76x02_dev *dev = hw->priv;
139 
140 	clear_bit(MT76_SCANNING, &dev->mt76.state);
141 }
142 EXPORT_SYMBOL_GPL(mt76x0_sw_scan_complete);
143 
144 int mt76x0_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
145 {
146 	struct mt76x02_dev *dev = hw->priv;
147 
148 	mt76_rmw_field(dev, MT_TX_RTS_CFG, MT_TX_RTS_CFG_THRESH, value);
149 
150 	return 0;
151 }
152 EXPORT_SYMBOL_GPL(mt76x0_set_rts_threshold);
153