Commit eedb427e authored by Rex Lu's avatar Rex Lu Committed by Felix Fietkau
Browse files

wifi: mt76: mt7996: Enable HW RRO for MT7992 chipset



This is a preliminary patch to enable WED support for MT7992 Kite
chipset supported by MT7996 driver.

Co-developed-by: default avatarSujuan Chen <sujuan.chen@mediatek.com>
Signed-off-by: default avatarSujuan Chen <sujuan.chen@mediatek.com>
Co-developed-by: default avatarBenjamin Lin <benjamin-jw.lin@mediatek.com>
Signed-off-by: default avatarBenjamin Lin <benjamin-jw.lin@mediatek.com>
Signed-off-by: default avatarRex Lu <rex.lu@mediatek.com>
Co-developed-by: default avatarLorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: default avatarLorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20250909-mt7996-rro-rework-v5-6-7d66f6eb7795@kernel.org


Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
parent 3bc2f02f
Loading
Loading
Loading
Loading
+118 −50
Original line number Diff line number Diff line
@@ -605,19 +605,21 @@ void mt7996_mac_init(struct mt7996_dev *dev)
	}

	/* rro module init */
	if (is_mt7996(&dev->mt76))
		mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 2);
	else
	if (dev->hif2)
		mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE,
				   dev->hif2 ? 7 : 0);
				   is_mt7996(&dev->mt76) ? 2 : 7);
	else
		mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 0);

	if (dev->has_rro) {
		u16 timeout;

		timeout = mt76_rr(dev, MT_HW_REV) == MT_HW_REV1 ? 512 : 128;
		mt7996_mcu_set_rro(dev, UNI_RRO_SET_FLUSH_TIMEOUT, timeout);
		mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 1);
		mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 0);
		mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE,
				   is_mt7996(&dev->mt76) ? 1 : 2);
		mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH,
				   !is_mt7996(&dev->mt76));
	} else {
		mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 3);
		mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 1);
@@ -754,11 +756,95 @@ void mt7996_wfsys_reset(struct mt7996_dev *dev)
	msleep(20);
}

#ifdef CONFIG_NET_MEDIATEK_SOC_WED
static void mt7996_rro_hw_init(struct mt7996_dev *dev)
{
	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
	u32 reg = MT_RRO_ADDR_ELEM_SEG_ADDR0;
	int i;

	if (!dev->has_rro)
		return;

	if (is_mt7992(&dev->mt76)) {
		/* Set emul 3.0 function */
		mt76_wr(dev, MT_RRO_3_0_EMU_CONF,
			MT_RRO_3_0_EMU_CONF_EN_MASK);
		mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE0,
			dev->wed_rro.addr_elem[0].phy_addr);
	} else {
		/* TODO: remove line after WM has set */
		mt76_clear(dev, WF_RRO_AXI_MST_CFG,
			   WF_RRO_AXI_MST_CFG_DIDX_OK);
		/* setup BA bitmap cache address */
		mt76_wr(dev, MT_RRO_BA_BITMAP_BASE0,
			dev->wed_rro.ba_bitmap[0].phy_addr);
		mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0);
		mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0,
			dev->wed_rro.ba_bitmap[1].phy_addr);
		mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0);

		/* Setup Address element address */
		for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) {
			mt76_wr(dev, reg,
				dev->wed_rro.addr_elem[i].phy_addr >> 4);
			reg += 4;
		}

		/* Setup Address element address - separate address
		 * segment mode
		 */
		mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1,
			MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE);
	}
	wed->wlan.ind_cmd.win_size = ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6;
	if (is_mt7996(&dev->mt76))
		wed->wlan.ind_cmd.particular_sid = MT7996_RRO_MAX_SESSION;
	else
		wed->wlan.ind_cmd.particular_sid = 1;
	wed->wlan.ind_cmd.particular_se_phys = dev->wed_rro.session.phy_addr;
	wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN;
	wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;

	mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
	mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
		 MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);

	/* particular session configure */
	/* use max session idx + 1 as particular session id */
	mt76_wr(dev, MT_RRO_PARTICULAR_CFG0, dev->wed_rro.session.phy_addr);

	if (is_mt7992(&dev->mt76)) {
		reg = MT_RRO_MSDU_PG_SEG_ADDR0;

		mt76_set(dev, MT_RRO_3_1_GLOBAL_CONFIG,
			 MT_RRO_3_1_GLOBAL_CONFIG_INTERLEAVE_EN);

		/* setup Msdu page address */
		for (i = 0; i < ARRAY_SIZE(dev->wed_rro.msdu_pg); i++) {
			mt76_wr(dev, reg,
				dev->wed_rro.msdu_pg[i].phy_addr >> 4);
			reg += 4;
		}
		mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
			MT_RRO_PARTICULAR_CONFG_EN |
			FIELD_PREP(MT_RRO_PARTICULAR_SID, 1));
	} else {
		mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
			MT_RRO_PARTICULAR_CONFG_EN |
			FIELD_PREP(MT_RRO_PARTICULAR_SID,
				   MT7996_RRO_MAX_SESSION));
	}
	/* interrupt enable */
	mt76_wr(dev, MT_RRO_HOST_INT_ENA,
		MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
}
#endif

static int mt7996_wed_rro_init(struct mt7996_dev *dev)
{
#ifdef CONFIG_NET_MEDIATEK_SOC_WED
	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
	u32 reg = MT_RRO_ADDR_ELEM_SEG_ADDR0;
	struct mt7996_wed_rro_addr *addr;
	void *ptr;
	int i;
@@ -804,6 +890,20 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
			dev->wed_rro.addr_elem[i].phy_addr;
	}

	for (i = 0; i < ARRAY_SIZE(dev->wed_rro.msdu_pg); i++) {
		ptr = dmam_alloc_coherent(dev->mt76.dma_dev,
					  MT7996_RRO_MSDU_PG_SIZE_PER_CR,
					  &dev->wed_rro.msdu_pg[i].phy_addr,
					  GFP_KERNEL);
		if (!ptr)
			return -ENOMEM;

		dev->wed_rro.msdu_pg[i].ptr = ptr;

		memset(dev->wed_rro.msdu_pg[i].ptr, 0,
		       MT7996_RRO_MSDU_PG_SIZE_PER_CR);
	}

	ptr = dmam_alloc_coherent(dev->mt76.dma_dev,
				  MT7996_RRO_WINDOW_MAX_LEN * sizeof(*addr),
				  &dev->wed_rro.session.phy_addr,
@@ -818,50 +918,8 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
		addr++;
	}

	/* rro hw init */
	/* TODO: remove line after WM has set */
	mt76_clear(dev, WF_RRO_AXI_MST_CFG, WF_RRO_AXI_MST_CFG_DIDX_OK);

	/* setup BA bitmap cache address */
	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE0,
		dev->wed_rro.ba_bitmap[0].phy_addr);
	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0);
	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0,
		dev->wed_rro.ba_bitmap[1].phy_addr);
	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0);

	/* setup Address element address */
	for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) {
		mt76_wr(dev, reg, dev->wed_rro.addr_elem[i].phy_addr >> 4);
		reg += 4;
	}
	mt7996_rro_hw_init(dev);

	/* setup Address element address - separate address segment mode */
	mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1,
		MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE);

	wed->wlan.ind_cmd.win_size = ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6;
	wed->wlan.ind_cmd.particular_sid = MT7996_RRO_MAX_SESSION;
	wed->wlan.ind_cmd.particular_se_phys = dev->wed_rro.session.phy_addr;
	wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN;
	wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;

	mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
	mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
		 MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);

	/* particular session configure */
	/* use max session idx + 1 as particular session id */
	mt76_wr(dev, MT_RRO_PARTICULAR_CFG0, dev->wed_rro.session.phy_addr);
	mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
		MT_RRO_PARTICULAR_CONFG_EN |
		FIELD_PREP(MT_RRO_PARTICULAR_SID, MT7996_RRO_MAX_SESSION));

	/* interrupt enable */
	mt76_wr(dev, MT_RRO_HOST_INT_ENA,
		MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);

	/* rro ind cmd queue init */
	return mt7996_dma_rro_init(dev);
#else
	return 0;
@@ -900,6 +958,16 @@ static void mt7996_wed_rro_free(struct mt7996_dev *dev)
				   dev->wed_rro.addr_elem[i].phy_addr);
	}

	for (i = 0; i < ARRAY_SIZE(dev->wed_rro.msdu_pg); i++) {
		if (!dev->wed_rro.msdu_pg[i].ptr)
			continue;

		dmam_free_coherent(dev->mt76.dma_dev,
				   MT7996_RRO_MSDU_PG_SIZE_PER_CR,
				   dev->wed_rro.msdu_pg[i].ptr,
				   dev->wed_rro.msdu_pg[i].phy_addr);
	}

	if (!dev->wed_rro.session.ptr)
		return;

+9 −0
Original line number Diff line number Diff line
@@ -112,6 +112,7 @@
#define MT7996_CRIT_TEMP		110
#define MT7996_MAX_TEMP			120

#define MT7996_RRO_MSDU_PG_HASH_SIZE	127
#define MT7996_RRO_MAX_SESSION		1024
#define MT7996_RRO_WINDOW_MAX_LEN	1024
#define MT7996_RRO_ADDR_ELEM_LEN	128
@@ -128,6 +129,10 @@
#define MT7996_RX_MSDU_PAGE_SIZE	(128 + \
					 SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))

/* RRO 3.1 */
#define MT7996_RRO_MSDU_PG_CR_CNT	8
#define MT7996_RRO_MSDU_PG_SIZE_PER_CR	0x10000

struct mt7996_vif;
struct mt7996_sta;
struct mt7996_dfs_pulse;
@@ -400,6 +405,10 @@ struct mt7996_dev {
			void *ptr;
			dma_addr_t phy_addr;
		} session;
		struct {
			void *ptr;
			dma_addr_t phy_addr;
		} msdu_pg[MT7996_RRO_MSDU_PG_CR_CNT];

		struct work_struct work;
		struct list_head poll_list;
+10 −0
Original line number Diff line number Diff line
@@ -88,6 +88,8 @@ enum offs_rev {
#define MT_RRO_BA_BITMAP_BASE1			MT_RRO_TOP(0xC)
#define WF_RRO_AXI_MST_CFG			MT_RRO_TOP(0xB8)
#define WF_RRO_AXI_MST_CFG_DIDX_OK		BIT(12)

#define MT_RRO_ADDR_ARRAY_BASE0			MT_RRO_TOP(0x30)
#define MT_RRO_ADDR_ARRAY_BASE1			MT_RRO_TOP(0x34)
#define MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE	BIT(31)

@@ -108,6 +110,14 @@ enum offs_rev {

#define MT_RRO_ADDR_ELEM_SEG_ADDR0		MT_RRO_TOP(0x400)

#define MT_RRO_3_0_EMU_CONF			MT_RRO_TOP(0x600)
#define MT_RRO_3_0_EMU_CONF_EN_MASK		BIT(11)

#define MT_RRO_3_1_GLOBAL_CONFIG		MT_RRO_TOP(0x604)
#define MT_RRO_3_1_GLOBAL_CONFIG_INTERLEAVE_EN	BIT(0)

#define MT_RRO_MSDU_PG_SEG_ADDR0		MT_RRO_TOP(0x620)

#define MT_RRO_ACK_SN_CTRL			MT_RRO_TOP(0x50)
#define MT_RRO_ACK_SN_CTRL_SN_MASK		GENMASK(27, 16)
#define MT_RRO_ACK_SN_CTRL_SESSION_MASK		GENMASK(11, 0)