18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
48c2ecf20Sopenharmony_ci * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "mt76x02.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_cistatic void mt76x02_set_beacon_offsets(struct mt76x02_dev *dev)
118c2ecf20Sopenharmony_ci{
128c2ecf20Sopenharmony_ci	u32 regs[4] = {};
138c2ecf20Sopenharmony_ci	u16 val;
148c2ecf20Sopenharmony_ci	int i;
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci	for (i = 0; i < dev->beacon_ops->nslots; i++) {
178c2ecf20Sopenharmony_ci		val = i * dev->beacon_ops->slot_size;
188c2ecf20Sopenharmony_ci		regs[i / 4] |= (val / 64) << (8 * (i % 4));
198c2ecf20Sopenharmony_ci	}
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++)
228c2ecf20Sopenharmony_ci		mt76_wr(dev, MT_BCN_OFFSET(i), regs[i]);
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int
268c2ecf20Sopenharmony_cimt76x02_write_beacon(struct mt76x02_dev *dev, int offset, struct sk_buff *skb)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	int beacon_len = dev->beacon_ops->slot_size;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(beacon_len < skb->len + sizeof(struct mt76x02_txwi)))
318c2ecf20Sopenharmony_ci		return -ENOSPC;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	/* USB devices already reserve enough skb headroom for txwi's. This
348c2ecf20Sopenharmony_ci	 * helps to save slow copies over USB.
358c2ecf20Sopenharmony_ci	 */
368c2ecf20Sopenharmony_ci	if (mt76_is_usb(&dev->mt76)) {
378c2ecf20Sopenharmony_ci		struct mt76x02_txwi *txwi;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci		txwi = (struct mt76x02_txwi *)(skb->data - sizeof(*txwi));
408c2ecf20Sopenharmony_ci		mt76x02_mac_write_txwi(dev, txwi, skb, NULL, NULL, skb->len);
418c2ecf20Sopenharmony_ci		skb_push(skb, sizeof(*txwi));
428c2ecf20Sopenharmony_ci	} else {
438c2ecf20Sopenharmony_ci		struct mt76x02_txwi txwi;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci		mt76x02_mac_write_txwi(dev, &txwi, skb, NULL, NULL, skb->len);
468c2ecf20Sopenharmony_ci		mt76_wr_copy(dev, offset, &txwi, sizeof(txwi));
478c2ecf20Sopenharmony_ci		offset += sizeof(txwi);
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	mt76_wr_copy(dev, offset, skb->data, skb->len);
518c2ecf20Sopenharmony_ci	return 0;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_civoid mt76x02_mac_set_beacon(struct mt76x02_dev *dev,
558c2ecf20Sopenharmony_ci			    struct sk_buff *skb)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	int bcn_len = dev->beacon_ops->slot_size;
588c2ecf20Sopenharmony_ci	int bcn_addr = MT_BEACON_BASE + (bcn_len * dev->beacon_data_count);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (!mt76x02_write_beacon(dev, bcn_addr, skb))
618c2ecf20Sopenharmony_ci		dev->beacon_data_count++;
628c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_mac_set_beacon);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_civoid mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev,
678c2ecf20Sopenharmony_ci				   struct ieee80211_vif *vif, bool enable)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
708c2ecf20Sopenharmony_ci	u8 old_mask = dev->mt76.beacon_mask;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	mt76x02_pre_tbtt_enable(dev, false);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (!dev->mt76.beacon_mask)
758c2ecf20Sopenharmony_ci		dev->tbtt_count = 0;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (enable) {
788c2ecf20Sopenharmony_ci		dev->mt76.beacon_mask |= BIT(mvif->idx);
798c2ecf20Sopenharmony_ci	} else {
808c2ecf20Sopenharmony_ci		dev->mt76.beacon_mask &= ~BIT(mvif->idx);
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (!!old_mask == !!dev->mt76.beacon_mask)
848c2ecf20Sopenharmony_ci		goto out;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (dev->mt76.beacon_mask)
878c2ecf20Sopenharmony_ci		mt76_set(dev, MT_BEACON_TIME_CFG,
888c2ecf20Sopenharmony_ci			 MT_BEACON_TIME_CFG_BEACON_TX |
898c2ecf20Sopenharmony_ci			 MT_BEACON_TIME_CFG_TBTT_EN |
908c2ecf20Sopenharmony_ci			 MT_BEACON_TIME_CFG_TIMER_EN);
918c2ecf20Sopenharmony_ci	else
928c2ecf20Sopenharmony_ci		mt76_clear(dev, MT_BEACON_TIME_CFG,
938c2ecf20Sopenharmony_ci			   MT_BEACON_TIME_CFG_BEACON_TX |
948c2ecf20Sopenharmony_ci			   MT_BEACON_TIME_CFG_TBTT_EN |
958c2ecf20Sopenharmony_ci			   MT_BEACON_TIME_CFG_TIMER_EN);
968c2ecf20Sopenharmony_ci	mt76x02_beacon_enable(dev, !!dev->mt76.beacon_mask);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ciout:
998c2ecf20Sopenharmony_ci	mt76x02_pre_tbtt_enable(dev, true);
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_civoid
1038c2ecf20Sopenharmony_cimt76x02_resync_beacon_timer(struct mt76x02_dev *dev)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	u32 timer_val = dev->mt76.beacon_int << 4;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	dev->tbtt_count++;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/*
1108c2ecf20Sopenharmony_ci	 * Beacon timer drifts by 1us every tick, the timer is configured
1118c2ecf20Sopenharmony_ci	 * in 1/16 TU (64us) units.
1128c2ecf20Sopenharmony_ci	 */
1138c2ecf20Sopenharmony_ci	if (dev->tbtt_count < 63)
1148c2ecf20Sopenharmony_ci		return;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/*
1178c2ecf20Sopenharmony_ci	 * The updated beacon interval takes effect after two TBTT, because
1188c2ecf20Sopenharmony_ci	 * at this point the original interval has already been loaded into
1198c2ecf20Sopenharmony_ci	 * the next TBTT_TIMER value
1208c2ecf20Sopenharmony_ci	 */
1218c2ecf20Sopenharmony_ci	if (dev->tbtt_count == 63)
1228c2ecf20Sopenharmony_ci		timer_val -= 1;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
1258c2ecf20Sopenharmony_ci		       MT_BEACON_TIME_CFG_INTVAL, timer_val);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (dev->tbtt_count >= 64)
1288c2ecf20Sopenharmony_ci		dev->tbtt_count = 0;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_resync_beacon_timer);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_civoid
1338c2ecf20Sopenharmony_cimt76x02_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct mt76x02_dev *dev = (struct mt76x02_dev *)priv;
1368c2ecf20Sopenharmony_ci	struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
1378c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (!(dev->mt76.beacon_mask & BIT(mvif->idx)))
1408c2ecf20Sopenharmony_ci		return;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	skb = ieee80211_beacon_get(mt76_hw(dev), vif);
1438c2ecf20Sopenharmony_ci	if (!skb)
1448c2ecf20Sopenharmony_ci		return;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	mt76x02_mac_set_beacon(dev, skb);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_update_beacon_iter);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic void
1518c2ecf20Sopenharmony_cimt76x02_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct beacon_bc_data *data = priv;
1548c2ecf20Sopenharmony_ci	struct mt76x02_dev *dev = data->dev;
1558c2ecf20Sopenharmony_ci	struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
1568c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *info;
1578c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (!(dev->mt76.beacon_mask & BIT(mvif->idx)))
1608c2ecf20Sopenharmony_ci		return;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	skb = ieee80211_get_buffered_bc(mt76_hw(dev), vif);
1638c2ecf20Sopenharmony_ci	if (!skb)
1648c2ecf20Sopenharmony_ci		return;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	info = IEEE80211_SKB_CB(skb);
1678c2ecf20Sopenharmony_ci	info->control.vif = vif;
1688c2ecf20Sopenharmony_ci	info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
1698c2ecf20Sopenharmony_ci	mt76_skb_set_moredata(skb, true);
1708c2ecf20Sopenharmony_ci	__skb_queue_tail(&data->q, skb);
1718c2ecf20Sopenharmony_ci	data->tail[mvif->idx] = skb;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_civoid
1758c2ecf20Sopenharmony_cimt76x02_enqueue_buffered_bc(struct mt76x02_dev *dev,
1768c2ecf20Sopenharmony_ci			    struct beacon_bc_data *data,
1778c2ecf20Sopenharmony_ci			    int max_nframes)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	int i, nframes;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	data->dev = dev;
1828c2ecf20Sopenharmony_ci	__skb_queue_head_init(&data->q);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	do {
1858c2ecf20Sopenharmony_ci		nframes = skb_queue_len(&data->q);
1868c2ecf20Sopenharmony_ci		ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
1878c2ecf20Sopenharmony_ci			IEEE80211_IFACE_ITER_RESUME_ALL,
1888c2ecf20Sopenharmony_ci			mt76x02_add_buffered_bc, data);
1898c2ecf20Sopenharmony_ci	} while (nframes != skb_queue_len(&data->q) &&
1908c2ecf20Sopenharmony_ci		 skb_queue_len(&data->q) < max_nframes);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (!skb_queue_len(&data->q))
1938c2ecf20Sopenharmony_ci		return;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(data->tail); i++) {
1968c2ecf20Sopenharmony_ci		if (!data->tail[i])
1978c2ecf20Sopenharmony_ci			continue;
1988c2ecf20Sopenharmony_ci		mt76_skb_set_moredata(data->tail[i], false);
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_enqueue_buffered_bc);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_civoid mt76x02_init_beacon_config(struct mt76x02_dev *dev)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	mt76_clear(dev, MT_BEACON_TIME_CFG, (MT_BEACON_TIME_CFG_TIMER_EN |
2068c2ecf20Sopenharmony_ci					     MT_BEACON_TIME_CFG_TBTT_EN |
2078c2ecf20Sopenharmony_ci					     MT_BEACON_TIME_CFG_BEACON_TX));
2088c2ecf20Sopenharmony_ci	mt76_set(dev, MT_BEACON_TIME_CFG, MT_BEACON_TIME_CFG_SYNC_MODE);
2098c2ecf20Sopenharmony_ci	mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xffff);
2108c2ecf20Sopenharmony_ci	mt76x02_set_beacon_offsets(dev);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_init_beacon_config);
2138c2ecf20Sopenharmony_ci
214