18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci#include "mt7603.h"
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_cistruct beacon_bc_data {
68c2ecf20Sopenharmony_ci	struct mt7603_dev *dev;
78c2ecf20Sopenharmony_ci	struct sk_buff_head q;
88c2ecf20Sopenharmony_ci	struct sk_buff *tail[MT7603_MAX_INTERFACES];
98c2ecf20Sopenharmony_ci	int count[MT7603_MAX_INTERFACES];
108c2ecf20Sopenharmony_ci};
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic void
138c2ecf20Sopenharmony_cimt7603_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	struct mt7603_dev *dev = (struct mt7603_dev *)priv;
168c2ecf20Sopenharmony_ci	struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv;
178c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	if (!(dev->mt76.beacon_mask & BIT(mvif->idx)))
208c2ecf20Sopenharmony_ci		return;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	skb = ieee80211_beacon_get(mt76_hw(dev), vif);
238c2ecf20Sopenharmony_ci	if (!skb)
248c2ecf20Sopenharmony_ci		return;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	mt76_tx_queue_skb(dev, MT_TXQ_BEACON, skb, &mvif->sta.wcid, NULL);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	spin_lock_bh(&dev->ps_lock);
298c2ecf20Sopenharmony_ci	mt76_wr(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY |
308c2ecf20Sopenharmony_ci		FIELD_PREP(MT_DMA_FQCR0_TARGET_WCID, mvif->sta.wcid.idx) |
318c2ecf20Sopenharmony_ci		FIELD_PREP(MT_DMA_FQCR0_TARGET_QID,
328c2ecf20Sopenharmony_ci			   dev->mt76.q_tx[MT_TXQ_CAB]->hw_idx) |
338c2ecf20Sopenharmony_ci		FIELD_PREP(MT_DMA_FQCR0_DEST_PORT_ID, 3) |
348c2ecf20Sopenharmony_ci		FIELD_PREP(MT_DMA_FQCR0_DEST_QUEUE_ID, 8));
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (!mt76_poll(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY, 0, 5000))
378c2ecf20Sopenharmony_ci		dev->beacon_check = MT7603_WATCHDOG_TIMEOUT;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	spin_unlock_bh(&dev->ps_lock);
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic void
438c2ecf20Sopenharmony_cimt7603_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct beacon_bc_data *data = priv;
468c2ecf20Sopenharmony_ci	struct mt7603_dev *dev = data->dev;
478c2ecf20Sopenharmony_ci	struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv;
488c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *info;
498c2ecf20Sopenharmony_ci	struct sk_buff *skb;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (!(dev->mt76.beacon_mask & BIT(mvif->idx)))
528c2ecf20Sopenharmony_ci		return;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	skb = ieee80211_get_buffered_bc(mt76_hw(dev), vif);
558c2ecf20Sopenharmony_ci	if (!skb)
568c2ecf20Sopenharmony_ci		return;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	info = IEEE80211_SKB_CB(skb);
598c2ecf20Sopenharmony_ci	info->control.vif = vif;
608c2ecf20Sopenharmony_ci	info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
618c2ecf20Sopenharmony_ci	mt76_skb_set_moredata(skb, true);
628c2ecf20Sopenharmony_ci	__skb_queue_tail(&data->q, skb);
638c2ecf20Sopenharmony_ci	data->tail[mvif->idx] = skb;
648c2ecf20Sopenharmony_ci	data->count[mvif->idx]++;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_civoid mt7603_pre_tbtt_tasklet(unsigned long arg)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct mt7603_dev *dev = (struct mt7603_dev *)arg;
708c2ecf20Sopenharmony_ci	struct mt76_queue *q;
718c2ecf20Sopenharmony_ci	struct beacon_bc_data data = {};
728c2ecf20Sopenharmony_ci	struct sk_buff *skb;
738c2ecf20Sopenharmony_ci	int i, nframes;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (mt76_hw(dev)->conf.flags & IEEE80211_CONF_OFFCHANNEL)
768c2ecf20Sopenharmony_ci		return;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	data.dev = dev;
798c2ecf20Sopenharmony_ci	__skb_queue_head_init(&data.q);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	q = dev->mt76.q_tx[MT_TXQ_BEACON];
828c2ecf20Sopenharmony_ci	spin_lock_bh(&q->lock);
838c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
848c2ecf20Sopenharmony_ci		IEEE80211_IFACE_ITER_RESUME_ALL,
858c2ecf20Sopenharmony_ci		mt7603_update_beacon_iter, dev);
868c2ecf20Sopenharmony_ci	mt76_queue_kick(dev, q);
878c2ecf20Sopenharmony_ci	spin_unlock_bh(&q->lock);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* Flush all previous CAB queue packets */
908c2ecf20Sopenharmony_ci	mt76_wr(dev, MT_WF_ARB_CAB_FLUSH, GENMASK(30, 16) | BIT(0));
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	mt76_queue_tx_cleanup(dev, MT_TXQ_CAB, false);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	mt76_csa_check(&dev->mt76);
958c2ecf20Sopenharmony_ci	if (dev->mt76.csa_complete)
968c2ecf20Sopenharmony_ci		goto out;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	q = dev->mt76.q_tx[MT_TXQ_CAB];
998c2ecf20Sopenharmony_ci	do {
1008c2ecf20Sopenharmony_ci		nframes = skb_queue_len(&data.q);
1018c2ecf20Sopenharmony_ci		ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
1028c2ecf20Sopenharmony_ci			IEEE80211_IFACE_ITER_RESUME_ALL,
1038c2ecf20Sopenharmony_ci			mt7603_add_buffered_bc, &data);
1048c2ecf20Sopenharmony_ci	} while (nframes != skb_queue_len(&data.q) &&
1058c2ecf20Sopenharmony_ci		 skb_queue_len(&data.q) < 8);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (skb_queue_empty(&data.q))
1088c2ecf20Sopenharmony_ci		goto out;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(data.tail); i++) {
1118c2ecf20Sopenharmony_ci		if (!data.tail[i])
1128c2ecf20Sopenharmony_ci			continue;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		mt76_skb_set_moredata(data.tail[i], false);
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	spin_lock_bh(&q->lock);
1188c2ecf20Sopenharmony_ci	while ((skb = __skb_dequeue(&data.q)) != NULL) {
1198c2ecf20Sopenharmony_ci		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
1208c2ecf20Sopenharmony_ci		struct ieee80211_vif *vif = info->control.vif;
1218c2ecf20Sopenharmony_ci		struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		mt76_tx_queue_skb(dev, MT_TXQ_CAB, skb, &mvif->sta.wcid, NULL);
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci	mt76_queue_kick(dev, q);
1268c2ecf20Sopenharmony_ci	spin_unlock_bh(&q->lock);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(data.count); i++)
1298c2ecf20Sopenharmony_ci		mt76_wr(dev, MT_WF_ARB_CAB_COUNT_B0_REG(i),
1308c2ecf20Sopenharmony_ci			data.count[i] << MT_WF_ARB_CAB_COUNT_B0_SHIFT(i));
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	mt76_wr(dev, MT_WF_ARB_CAB_START,
1338c2ecf20Sopenharmony_ci		MT_WF_ARB_CAB_START_BSSn(0) |
1348c2ecf20Sopenharmony_ci		(MT_WF_ARB_CAB_START_BSS0n(1) *
1358c2ecf20Sopenharmony_ci		 ((1 << (MT7603_MAX_INTERFACES - 1)) - 1)));
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ciout:
1388c2ecf20Sopenharmony_ci	mt76_queue_tx_cleanup(dev, MT_TXQ_BEACON, false);
1398c2ecf20Sopenharmony_ci	if (dev->mt76.q_tx[MT_TXQ_BEACON]->queued >
1408c2ecf20Sopenharmony_ci	    hweight8(dev->mt76.beacon_mask))
1418c2ecf20Sopenharmony_ci		dev->beacon_check++;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_civoid mt7603_beacon_set_timer(struct mt7603_dev *dev, int idx, int intval)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	u32 pre_tbtt = MT7603_PRE_TBTT_TIME / 64;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (idx >= 0) {
1498c2ecf20Sopenharmony_ci		if (intval)
1508c2ecf20Sopenharmony_ci			dev->mt76.beacon_mask |= BIT(idx);
1518c2ecf20Sopenharmony_ci		else
1528c2ecf20Sopenharmony_ci			dev->mt76.beacon_mask &= ~BIT(idx);
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (!dev->mt76.beacon_mask || (!intval && idx < 0)) {
1568c2ecf20Sopenharmony_ci		mt7603_irq_disable(dev, MT_INT_MAC_IRQ3);
1578c2ecf20Sopenharmony_ci		mt76_clear(dev, MT_ARB_SCR, MT_ARB_SCR_BCNQ_OPMODE_MASK);
1588c2ecf20Sopenharmony_ci		mt76_wr(dev, MT_HW_INT_MASK(3), 0);
1598c2ecf20Sopenharmony_ci		return;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	dev->mt76.beacon_int = intval;
1638c2ecf20Sopenharmony_ci	mt76_wr(dev, MT_TBTT,
1648c2ecf20Sopenharmony_ci		FIELD_PREP(MT_TBTT_PERIOD, intval) | MT_TBTT_CAL_ENABLE);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	mt76_wr(dev, MT_TBTT_TIMER_CFG, 0x99); /* start timer */
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	mt76_rmw_field(dev, MT_ARB_SCR, MT_ARB_SCR_BCNQ_OPMODE_MASK,
1698c2ecf20Sopenharmony_ci		       MT_BCNQ_OPMODE_AP);
1708c2ecf20Sopenharmony_ci	mt76_clear(dev, MT_ARB_SCR, MT_ARB_SCR_TBTT_BCN_PRIO);
1718c2ecf20Sopenharmony_ci	mt76_set(dev, MT_ARB_SCR, MT_ARB_SCR_TBTT_BCAST_PRIO);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	mt76_wr(dev, MT_PRE_TBTT, pre_tbtt);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	mt76_set(dev, MT_HW_INT_MASK(3),
1768c2ecf20Sopenharmony_ci		 MT_HW_INT3_PRE_TBTT0 | MT_HW_INT3_TBTT0);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	mt76_set(dev, MT_WF_ARB_BCN_START,
1798c2ecf20Sopenharmony_ci		 MT_WF_ARB_BCN_START_BSSn(0) |
1808c2ecf20Sopenharmony_ci		 ((dev->mt76.beacon_mask >> 1) *
1818c2ecf20Sopenharmony_ci		  MT_WF_ARB_BCN_START_BSS0n(1)));
1828c2ecf20Sopenharmony_ci	mt7603_irq_enable(dev, MT_INT_MAC_IRQ3);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (dev->mt76.beacon_mask & ~BIT(0))
1858c2ecf20Sopenharmony_ci		mt76_set(dev, MT_LPON_SBTOR(0), MT_LPON_SBTOR_SUB_BSS_EN);
1868c2ecf20Sopenharmony_ci	else
1878c2ecf20Sopenharmony_ci		mt76_clear(dev, MT_LPON_SBTOR(0), MT_LPON_SBTOR_SUB_BSS_EN);
1888c2ecf20Sopenharmony_ci}
189