Commit b230812b authored by Sujuan Chen's avatar Sujuan Chen Committed by Paolo Abeni
Browse files

net: ethernet: mtk_wed: introduce partial AMSDU offload support for MT7988



Introduce partial AMSDU offload support for MT7988 SoC in order to merge
in hw packets belonging to the same AMSDU before passing them to the
WLAN nic.

Co-developed-by: default avatarLorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: default avatarLorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: default avatarSujuan Chen <sujuan.chen@mediatek.com>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 96ddb4d0
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -425,7 +425,8 @@ int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry,
}

int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry,
			   int wdma_idx, int txq, int bss, int wcid)
			   int wdma_idx, int txq, int bss, int wcid,
			   bool amsdu_en)
{
	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
	u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
@@ -437,6 +438,7 @@ int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry,
			 MTK_FOE_IB2_WDMA_WINFO_V2;
		l2->w3info = FIELD_PREP(MTK_FOE_WINFO_WCID_V3, wcid) |
			     FIELD_PREP(MTK_FOE_WINFO_BSS_V3, bss);
		l2->amsdu = FIELD_PREP(MTK_FOE_WINFO_AMSDU_EN, amsdu_en);
		break;
	case 2:
		*ib2 &= ~MTK_FOE_IB2_PORT_MG_V2;
+10 −9
Original line number Diff line number Diff line
@@ -88,13 +88,13 @@ enum {
#define MTK_FOE_WINFO_BSS_V3		GENMASK(23, 16)
#define MTK_FOE_WINFO_WCID_V3		GENMASK(15, 0)

#define MTK_FOE_WINFO_PAO_USR_INFO	GENMASK(15, 0)
#define MTK_FOE_WINFO_PAO_TID		GENMASK(19, 16)
#define MTK_FOE_WINFO_PAO_IS_FIXEDRATE	BIT(20)
#define MTK_FOE_WINFO_PAO_IS_PRIOR	BIT(21)
#define MTK_FOE_WINFO_PAO_IS_SP		BIT(22)
#define MTK_FOE_WINFO_PAO_HF		BIT(23)
#define MTK_FOE_WINFO_PAO_AMSDU_EN	BIT(24)
#define MTK_FOE_WINFO_AMSDU_USR_INFO	GENMASK(15, 0)
#define MTK_FOE_WINFO_AMSDU_TID		GENMASK(19, 16)
#define MTK_FOE_WINFO_AMSDU_IS_FIXEDRATE	BIT(20)
#define MTK_FOE_WINFO_AMSDU_IS_PRIOR	BIT(21)
#define MTK_FOE_WINFO_AMSDU_IS_SP	BIT(22)
#define MTK_FOE_WINFO_AMSDU_HF		BIT(23)
#define MTK_FOE_WINFO_AMSDU_EN		BIT(24)

enum {
	MTK_FOE_STATE_INVALID,
@@ -123,7 +123,7 @@ struct mtk_foe_mac_info {

	/* netsys_v3 */
	u32 w3info;
	u32 wpao;
	u32 amsdu;
};

/* software-only entry type */
@@ -392,7 +392,8 @@ int mtk_foe_entry_set_vlan(struct mtk_eth *eth, struct mtk_foe_entry *entry,
int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry,
			    int sid);
int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry,
			   int wdma_idx, int txq, int bss, int wcid);
			   int wdma_idx, int txq, int bss, int wcid,
			   bool amsdu_en);
int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry,
			    unsigned int queue);
int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+2 −1
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_i
	info->queue = path->mtk_wdma.queue;
	info->bss = path->mtk_wdma.bss;
	info->wcid = path->mtk_wdma.wcid;
	info->amsdu = path->mtk_wdma.amsdu;

	return 0;
}
@@ -192,7 +193,7 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,

	if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) {
		mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue,
				       info.bss, info.wcid);
				       info.bss, info.wcid, info.amsdu);
		if (mtk_is_netsys_v2_or_greater(eth)) {
			switch (info.wdma_idx) {
			case 0:
+137 −17
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@
#define MTK_WED_RX_PAGE_BUF_PER_PAGE	(PAGE_SIZE / 128)
#define MTK_WED_RX_RING_SIZE		1536
#define MTK_WED_RX_PG_BM_CNT		8192
#define MTK_WED_AMSDU_BUF_SIZE		(PAGE_SIZE << 4)
#define MTK_WED_AMSDU_NPAGES		32

#define MTK_WED_TX_RING_SIZE		2048
#define MTK_WED_WDMA_RING_SIZE		1024
@@ -173,6 +175,23 @@ mtk_wdma_rx_reset(struct mtk_wed_device *dev)
	return ret;
}

static u32
mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
{
	return !!(wed_r32(dev, reg) & mask);
}

static int
mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
{
	int sleep = 15000;
	int timeout = 100 * sleep;
	u32 val;

	return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep,
				 timeout, false, dev, reg, mask);
}

static void
mtk_wdma_tx_reset(struct mtk_wed_device *dev)
{
@@ -335,6 +354,118 @@ mtk_wed_assign(struct mtk_wed_device *dev)
	return hw;
}

static int
mtk_wed_amsdu_buffer_alloc(struct mtk_wed_device *dev)
{
	struct mtk_wed_hw *hw = dev->hw;
	struct mtk_wed_amsdu *wed_amsdu;
	int i;

	if (!mtk_wed_is_v3_or_greater(hw))
		return 0;

	wed_amsdu = devm_kcalloc(hw->dev, MTK_WED_AMSDU_NPAGES,
				 sizeof(*wed_amsdu), GFP_KERNEL);
	if (!wed_amsdu)
		return -ENOMEM;

	for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) {
		void *ptr;

		/* each segment is 64K */
		ptr = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN |
					       __GFP_ZERO | __GFP_COMP |
					       GFP_DMA32,
					       get_order(MTK_WED_AMSDU_BUF_SIZE));
		if (!ptr)
			goto error;

		wed_amsdu[i].txd = ptr;
		wed_amsdu[i].txd_phy = dma_map_single(hw->dev, ptr,
						      MTK_WED_AMSDU_BUF_SIZE,
						      DMA_TO_DEVICE);
		if (dma_mapping_error(hw->dev, wed_amsdu[i].txd_phy))
			goto error;
	}
	dev->hw->wed_amsdu = wed_amsdu;

	return 0;

error:
	for (i--; i >= 0; i--)
		dma_unmap_single(hw->dev, wed_amsdu[i].txd_phy,
				 MTK_WED_AMSDU_BUF_SIZE, DMA_TO_DEVICE);
	return -ENOMEM;
}

static void
mtk_wed_amsdu_free_buffer(struct mtk_wed_device *dev)
{
	struct mtk_wed_amsdu *wed_amsdu = dev->hw->wed_amsdu;
	int i;

	if (!wed_amsdu)
		return;

	for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) {
		dma_unmap_single(dev->hw->dev, wed_amsdu[i].txd_phy,
				 MTK_WED_AMSDU_BUF_SIZE, DMA_TO_DEVICE);
		free_pages((unsigned long)wed_amsdu[i].txd,
			   get_order(MTK_WED_AMSDU_BUF_SIZE));
	}
}

static int
mtk_wed_amsdu_init(struct mtk_wed_device *dev)
{
	struct mtk_wed_amsdu *wed_amsdu = dev->hw->wed_amsdu;
	int i, ret;

	if (!wed_amsdu)
		return 0;

	for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++)
		wed_w32(dev, MTK_WED_AMSDU_HIFTXD_BASE_L(i),
			wed_amsdu[i].txd_phy);

	/* init all sta parameter */
	wed_w32(dev, MTK_WED_AMSDU_STA_INFO_INIT, MTK_WED_AMSDU_STA_RMVL |
		MTK_WED_AMSDU_STA_WTBL_HDRT_MODE |
		FIELD_PREP(MTK_WED_AMSDU_STA_MAX_AMSDU_LEN,
			   dev->wlan.amsdu_max_len >> 8) |
		FIELD_PREP(MTK_WED_AMSDU_STA_MAX_AMSDU_NUM,
			   dev->wlan.amsdu_max_subframes));

	wed_w32(dev, MTK_WED_AMSDU_STA_INFO, MTK_WED_AMSDU_STA_INFO_DO_INIT);

	ret = mtk_wed_poll_busy(dev, MTK_WED_AMSDU_STA_INFO,
				MTK_WED_AMSDU_STA_INFO_DO_INIT);
	if (ret) {
		dev_err(dev->hw->dev, "amsdu initialization failed\n");
		return ret;
	}

	/* init partial amsdu offload txd src */
	wed_set(dev, MTK_WED_AMSDU_HIFTXD_CFG,
		FIELD_PREP(MTK_WED_AMSDU_HIFTXD_SRC, dev->hw->index));

	/* init qmem */
	wed_set(dev, MTK_WED_AMSDU_PSE, MTK_WED_AMSDU_PSE_RESET);
	ret = mtk_wed_poll_busy(dev, MTK_WED_MON_AMSDU_QMEM_STS1, BIT(29));
	if (ret) {
		pr_info("%s: amsdu qmem initialization failed\n", __func__);
		return ret;
	}

	/* eagle E1 PCIE1 tx ring 22 flow control issue */
	if (dev->wlan.id == 0x7991)
		wed_clr(dev, MTK_WED_AMSDU_FIFO, MTK_WED_AMSDU_IS_PRIOR0_RING);

	wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN);

	return 0;
}

static int
mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev)
{
@@ -709,6 +840,7 @@ __mtk_wed_detach(struct mtk_wed_device *dev)

	mtk_wdma_rx_reset(dev);
	mtk_wed_reset(dev, MTK_WED_RESET_WED);
	mtk_wed_amsdu_free_buffer(dev);
	mtk_wed_free_tx_buffer(dev);
	mtk_wed_free_tx_rings(dev);

@@ -1129,23 +1261,6 @@ mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size, bool tx)
	}
}

static u32
mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
{
	return !!(wed_r32(dev, reg) & mask);
}

static int
mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
{
	int sleep = 15000;
	int timeout = 100 * sleep;
	u32 val;

	return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep,
				 timeout, false, dev, reg, mask);
}

static int
mtk_wed_rx_reset(struct mtk_wed_device *dev)
{
@@ -1692,6 +1807,7 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
	}

	mtk_wed_set_512_support(dev, dev->wlan.wcid_512);
	mtk_wed_amsdu_init(dev);

	mtk_wed_dma_enable(dev);
	dev->running = true;
@@ -1748,6 +1864,10 @@ mtk_wed_attach(struct mtk_wed_device *dev)
	if (ret)
		goto out;

	ret = mtk_wed_amsdu_buffer_alloc(dev);
	if (ret)
		goto out;

	if (mtk_wed_get_rx_capa(dev)) {
		ret = mtk_wed_rro_alloc(dev);
		if (ret)
+7 −0
Original line number Diff line number Diff line
@@ -25,6 +25,11 @@ struct mtk_wed_soc_data {
	u32 wdma_desc_size;
};

struct mtk_wed_amsdu {
	void *txd;
	dma_addr_t txd_phy;
};

struct mtk_wed_hw {
	const struct mtk_wed_soc_data *soc;
	struct device_node *node;
@@ -38,6 +43,7 @@ struct mtk_wed_hw {
	struct dentry *debugfs_dir;
	struct mtk_wed_device *wed_dev;
	struct mtk_wed_wo *wed_wo;
	struct mtk_wed_amsdu *wed_amsdu;
	u32 pcie_base;
	u32 debugfs_reg;
	u32 num_flows;
@@ -52,6 +58,7 @@ struct mtk_wdma_info {
	u8 queue;
	u16 wcid;
	u8 bss;
	u8 amsdu;
};

#ifdef CONFIG_NET_MEDIATEK_SOC_WED
Loading