1*4882a593Smuzhiyun // SPDX-License-Identifier: ISC
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
4*4882a593Smuzhiyun * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
5*4882a593Smuzhiyun * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl>
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include "mt76x02.h"
9*4882a593Smuzhiyun
mt76x02_set_beacon_offsets(struct mt76x02_dev * dev)10*4882a593Smuzhiyun static void mt76x02_set_beacon_offsets(struct mt76x02_dev *dev)
11*4882a593Smuzhiyun {
12*4882a593Smuzhiyun u32 regs[4] = {};
13*4882a593Smuzhiyun u16 val;
14*4882a593Smuzhiyun int i;
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun for (i = 0; i < dev->beacon_ops->nslots; i++) {
17*4882a593Smuzhiyun val = i * dev->beacon_ops->slot_size;
18*4882a593Smuzhiyun regs[i / 4] |= (val / 64) << (8 * (i % 4));
19*4882a593Smuzhiyun }
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun for (i = 0; i < 4; i++)
22*4882a593Smuzhiyun mt76_wr(dev, MT_BCN_OFFSET(i), regs[i]);
23*4882a593Smuzhiyun }
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun static int
mt76x02_write_beacon(struct mt76x02_dev * dev,int offset,struct sk_buff * skb)26*4882a593Smuzhiyun mt76x02_write_beacon(struct mt76x02_dev *dev, int offset, struct sk_buff *skb)
27*4882a593Smuzhiyun {
28*4882a593Smuzhiyun int beacon_len = dev->beacon_ops->slot_size;
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun if (WARN_ON_ONCE(beacon_len < skb->len + sizeof(struct mt76x02_txwi)))
31*4882a593Smuzhiyun return -ENOSPC;
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun /* USB devices already reserve enough skb headroom for txwi's. This
34*4882a593Smuzhiyun * helps to save slow copies over USB.
35*4882a593Smuzhiyun */
36*4882a593Smuzhiyun if (mt76_is_usb(&dev->mt76)) {
37*4882a593Smuzhiyun struct mt76x02_txwi *txwi;
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun txwi = (struct mt76x02_txwi *)(skb->data - sizeof(*txwi));
40*4882a593Smuzhiyun mt76x02_mac_write_txwi(dev, txwi, skb, NULL, NULL, skb->len);
41*4882a593Smuzhiyun skb_push(skb, sizeof(*txwi));
42*4882a593Smuzhiyun } else {
43*4882a593Smuzhiyun struct mt76x02_txwi txwi;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun mt76x02_mac_write_txwi(dev, &txwi, skb, NULL, NULL, skb->len);
46*4882a593Smuzhiyun mt76_wr_copy(dev, offset, &txwi, sizeof(txwi));
47*4882a593Smuzhiyun offset += sizeof(txwi);
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun mt76_wr_copy(dev, offset, skb->data, skb->len);
51*4882a593Smuzhiyun return 0;
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun
mt76x02_mac_set_beacon(struct mt76x02_dev * dev,struct sk_buff * skb)54*4882a593Smuzhiyun void mt76x02_mac_set_beacon(struct mt76x02_dev *dev,
55*4882a593Smuzhiyun struct sk_buff *skb)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun int bcn_len = dev->beacon_ops->slot_size;
58*4882a593Smuzhiyun int bcn_addr = MT_BEACON_BASE + (bcn_len * dev->beacon_data_count);
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun if (!mt76x02_write_beacon(dev, bcn_addr, skb))
61*4882a593Smuzhiyun dev->beacon_data_count++;
62*4882a593Smuzhiyun dev_kfree_skb(skb);
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mt76x02_mac_set_beacon);
65*4882a593Smuzhiyun
mt76x02_mac_set_beacon_enable(struct mt76x02_dev * dev,struct ieee80211_vif * vif,bool enable)66*4882a593Smuzhiyun void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev,
67*4882a593Smuzhiyun struct ieee80211_vif *vif, bool enable)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
70*4882a593Smuzhiyun u8 old_mask = dev->mt76.beacon_mask;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun mt76x02_pre_tbtt_enable(dev, false);
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun if (!dev->mt76.beacon_mask)
75*4882a593Smuzhiyun dev->tbtt_count = 0;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun if (enable) {
78*4882a593Smuzhiyun dev->mt76.beacon_mask |= BIT(mvif->idx);
79*4882a593Smuzhiyun } else {
80*4882a593Smuzhiyun dev->mt76.beacon_mask &= ~BIT(mvif->idx);
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun if (!!old_mask == !!dev->mt76.beacon_mask)
84*4882a593Smuzhiyun goto out;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun if (dev->mt76.beacon_mask)
87*4882a593Smuzhiyun mt76_set(dev, MT_BEACON_TIME_CFG,
88*4882a593Smuzhiyun MT_BEACON_TIME_CFG_BEACON_TX |
89*4882a593Smuzhiyun MT_BEACON_TIME_CFG_TBTT_EN |
90*4882a593Smuzhiyun MT_BEACON_TIME_CFG_TIMER_EN);
91*4882a593Smuzhiyun else
92*4882a593Smuzhiyun mt76_clear(dev, MT_BEACON_TIME_CFG,
93*4882a593Smuzhiyun MT_BEACON_TIME_CFG_BEACON_TX |
94*4882a593Smuzhiyun MT_BEACON_TIME_CFG_TBTT_EN |
95*4882a593Smuzhiyun MT_BEACON_TIME_CFG_TIMER_EN);
96*4882a593Smuzhiyun mt76x02_beacon_enable(dev, !!dev->mt76.beacon_mask);
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun out:
99*4882a593Smuzhiyun mt76x02_pre_tbtt_enable(dev, true);
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun void
mt76x02_resync_beacon_timer(struct mt76x02_dev * dev)103*4882a593Smuzhiyun mt76x02_resync_beacon_timer(struct mt76x02_dev *dev)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun u32 timer_val = dev->mt76.beacon_int << 4;
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun dev->tbtt_count++;
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun /*
110*4882a593Smuzhiyun * Beacon timer drifts by 1us every tick, the timer is configured
111*4882a593Smuzhiyun * in 1/16 TU (64us) units.
112*4882a593Smuzhiyun */
113*4882a593Smuzhiyun if (dev->tbtt_count < 63)
114*4882a593Smuzhiyun return;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun /*
117*4882a593Smuzhiyun * The updated beacon interval takes effect after two TBTT, because
118*4882a593Smuzhiyun * at this point the original interval has already been loaded into
119*4882a593Smuzhiyun * the next TBTT_TIMER value
120*4882a593Smuzhiyun */
121*4882a593Smuzhiyun if (dev->tbtt_count == 63)
122*4882a593Smuzhiyun timer_val -= 1;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
125*4882a593Smuzhiyun MT_BEACON_TIME_CFG_INTVAL, timer_val);
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun if (dev->tbtt_count >= 64)
128*4882a593Smuzhiyun dev->tbtt_count = 0;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mt76x02_resync_beacon_timer);
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun void
mt76x02_update_beacon_iter(void * priv,u8 * mac,struct ieee80211_vif * vif)133*4882a593Smuzhiyun mt76x02_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun struct mt76x02_dev *dev = (struct mt76x02_dev *)priv;
136*4882a593Smuzhiyun struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
137*4882a593Smuzhiyun struct sk_buff *skb = NULL;
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun if (!(dev->mt76.beacon_mask & BIT(mvif->idx)))
140*4882a593Smuzhiyun return;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun skb = ieee80211_beacon_get(mt76_hw(dev), vif);
143*4882a593Smuzhiyun if (!skb)
144*4882a593Smuzhiyun return;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun mt76x02_mac_set_beacon(dev, skb);
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mt76x02_update_beacon_iter);
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun static void
mt76x02_add_buffered_bc(void * priv,u8 * mac,struct ieee80211_vif * vif)151*4882a593Smuzhiyun mt76x02_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun struct beacon_bc_data *data = priv;
154*4882a593Smuzhiyun struct mt76x02_dev *dev = data->dev;
155*4882a593Smuzhiyun struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
156*4882a593Smuzhiyun struct ieee80211_tx_info *info;
157*4882a593Smuzhiyun struct sk_buff *skb;
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun if (!(dev->mt76.beacon_mask & BIT(mvif->idx)))
160*4882a593Smuzhiyun return;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun skb = ieee80211_get_buffered_bc(mt76_hw(dev), vif);
163*4882a593Smuzhiyun if (!skb)
164*4882a593Smuzhiyun return;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun info = IEEE80211_SKB_CB(skb);
167*4882a593Smuzhiyun info->control.vif = vif;
168*4882a593Smuzhiyun info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
169*4882a593Smuzhiyun mt76_skb_set_moredata(skb, true);
170*4882a593Smuzhiyun __skb_queue_tail(&data->q, skb);
171*4882a593Smuzhiyun data->tail[mvif->idx] = skb;
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun void
mt76x02_enqueue_buffered_bc(struct mt76x02_dev * dev,struct beacon_bc_data * data,int max_nframes)175*4882a593Smuzhiyun mt76x02_enqueue_buffered_bc(struct mt76x02_dev *dev,
176*4882a593Smuzhiyun struct beacon_bc_data *data,
177*4882a593Smuzhiyun int max_nframes)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun int i, nframes;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun data->dev = dev;
182*4882a593Smuzhiyun __skb_queue_head_init(&data->q);
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun do {
185*4882a593Smuzhiyun nframes = skb_queue_len(&data->q);
186*4882a593Smuzhiyun ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
187*4882a593Smuzhiyun IEEE80211_IFACE_ITER_RESUME_ALL,
188*4882a593Smuzhiyun mt76x02_add_buffered_bc, data);
189*4882a593Smuzhiyun } while (nframes != skb_queue_len(&data->q) &&
190*4882a593Smuzhiyun skb_queue_len(&data->q) < max_nframes);
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun if (!skb_queue_len(&data->q))
193*4882a593Smuzhiyun return;
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(data->tail); i++) {
196*4882a593Smuzhiyun if (!data->tail[i])
197*4882a593Smuzhiyun continue;
198*4882a593Smuzhiyun mt76_skb_set_moredata(data->tail[i], false);
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mt76x02_enqueue_buffered_bc);
202*4882a593Smuzhiyun
mt76x02_init_beacon_config(struct mt76x02_dev * dev)203*4882a593Smuzhiyun void mt76x02_init_beacon_config(struct mt76x02_dev *dev)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun mt76_clear(dev, MT_BEACON_TIME_CFG, (MT_BEACON_TIME_CFG_TIMER_EN |
206*4882a593Smuzhiyun MT_BEACON_TIME_CFG_TBTT_EN |
207*4882a593Smuzhiyun MT_BEACON_TIME_CFG_BEACON_TX));
208*4882a593Smuzhiyun mt76_set(dev, MT_BEACON_TIME_CFG, MT_BEACON_TIME_CFG_SYNC_MODE);
209*4882a593Smuzhiyun mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xffff);
210*4882a593Smuzhiyun mt76x02_set_beacon_offsets(dev);
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mt76x02_init_beacon_config);
213*4882a593Smuzhiyun
214