18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
28c2ecf20Sopenharmony_ci/* Copyright 2017-2019 NXP */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include "enetc.h"
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_cistatic void enetc_clean_cbdr(struct enetc_si *si)
78c2ecf20Sopenharmony_ci{
88c2ecf20Sopenharmony_ci	struct enetc_cbdr *ring = &si->cbd_ring;
98c2ecf20Sopenharmony_ci	struct enetc_cbd *dest_cbd;
108c2ecf20Sopenharmony_ci	int i, status;
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci	i = ring->next_to_clean;
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci	while (enetc_rd_reg(ring->cir) != i) {
158c2ecf20Sopenharmony_ci		dest_cbd = ENETC_CBD(*ring, i);
168c2ecf20Sopenharmony_ci		status = dest_cbd->status_flags & ENETC_CBD_STATUS_MASK;
178c2ecf20Sopenharmony_ci		if (status)
188c2ecf20Sopenharmony_ci			dev_warn(&si->pdev->dev, "CMD err %04x for cmd %04x\n",
198c2ecf20Sopenharmony_ci				 status, dest_cbd->cmd);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci		memset(dest_cbd, 0, sizeof(*dest_cbd));
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci		i = (i + 1) % ring->bd_count;
248c2ecf20Sopenharmony_ci	}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	ring->next_to_clean = i;
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int enetc_cbd_unused(struct enetc_cbdr *r)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	return (r->next_to_clean - r->next_to_use - 1 + r->bd_count) %
328c2ecf20Sopenharmony_ci		r->bd_count;
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ciint enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct enetc_cbdr *ring = &si->cbd_ring;
388c2ecf20Sopenharmony_ci	int timeout = ENETC_CBDR_TIMEOUT;
398c2ecf20Sopenharmony_ci	struct enetc_cbd *dest_cbd;
408c2ecf20Sopenharmony_ci	int i;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (unlikely(!ring->bd_base))
438c2ecf20Sopenharmony_ci		return -EIO;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (unlikely(!enetc_cbd_unused(ring)))
468c2ecf20Sopenharmony_ci		enetc_clean_cbdr(si);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	i = ring->next_to_use;
498c2ecf20Sopenharmony_ci	dest_cbd = ENETC_CBD(*ring, i);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/* copy command to the ring */
528c2ecf20Sopenharmony_ci	*dest_cbd = *cbd;
538c2ecf20Sopenharmony_ci	i = (i + 1) % ring->bd_count;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	ring->next_to_use = i;
568c2ecf20Sopenharmony_ci	/* let H/W know BD ring has been updated */
578c2ecf20Sopenharmony_ci	enetc_wr_reg(ring->pir, i);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	do {
608c2ecf20Sopenharmony_ci		if (enetc_rd_reg(ring->cir) == i)
618c2ecf20Sopenharmony_ci			break;
628c2ecf20Sopenharmony_ci		udelay(10); /* cannot sleep, rtnl_lock() */
638c2ecf20Sopenharmony_ci		timeout -= 10;
648c2ecf20Sopenharmony_ci	} while (timeout);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (!timeout)
678c2ecf20Sopenharmony_ci		return -EBUSY;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* CBD may writeback data, feedback up level */
708c2ecf20Sopenharmony_ci	*cbd = *dest_cbd;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	enetc_clean_cbdr(si);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ciint enetc_clear_mac_flt_entry(struct enetc_si *si, int index)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct enetc_cbd cbd;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	memset(&cbd, 0, sizeof(cbd));
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	cbd.cls = 1;
848c2ecf20Sopenharmony_ci	cbd.status_flags = ENETC_CBD_FLAGS_SF;
858c2ecf20Sopenharmony_ci	cbd.index = cpu_to_le16(index);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return enetc_send_cmd(si, &cbd);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ciint enetc_set_mac_flt_entry(struct enetc_si *si, int index,
918c2ecf20Sopenharmony_ci			    char *mac_addr, int si_map)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct enetc_cbd cbd;
948c2ecf20Sopenharmony_ci	u32 upper;
958c2ecf20Sopenharmony_ci	u16 lower;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	memset(&cbd, 0, sizeof(cbd));
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* fill up the "set" descriptor */
1008c2ecf20Sopenharmony_ci	cbd.cls = 1;
1018c2ecf20Sopenharmony_ci	cbd.status_flags = ENETC_CBD_FLAGS_SF;
1028c2ecf20Sopenharmony_ci	cbd.index = cpu_to_le16(index);
1038c2ecf20Sopenharmony_ci	cbd.opt[3] = cpu_to_le32(si_map);
1048c2ecf20Sopenharmony_ci	/* enable entry */
1058c2ecf20Sopenharmony_ci	cbd.opt[0] = cpu_to_le32(BIT(31));
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	upper = *(const u32 *)mac_addr;
1088c2ecf20Sopenharmony_ci	lower = *(const u16 *)(mac_addr + 4);
1098c2ecf20Sopenharmony_ci	cbd.addr[0] = cpu_to_le32(upper);
1108c2ecf20Sopenharmony_ci	cbd.addr[1] = cpu_to_le32(lower);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return enetc_send_cmd(si, &cbd);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci#define RFSE_ALIGN	64
1168c2ecf20Sopenharmony_ci/* Set entry in RFS table */
1178c2ecf20Sopenharmony_ciint enetc_set_fs_entry(struct enetc_si *si, struct enetc_cmd_rfse *rfse,
1188c2ecf20Sopenharmony_ci		       int index)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct enetc_cbd cbd = {.cmd = 0};
1218c2ecf20Sopenharmony_ci	dma_addr_t dma, dma_align;
1228c2ecf20Sopenharmony_ci	void *tmp, *tmp_align;
1238c2ecf20Sopenharmony_ci	int err;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* fill up the "set" descriptor */
1268c2ecf20Sopenharmony_ci	cbd.cmd = 0;
1278c2ecf20Sopenharmony_ci	cbd.cls = 4;
1288c2ecf20Sopenharmony_ci	cbd.index = cpu_to_le16(index);
1298c2ecf20Sopenharmony_ci	cbd.length = cpu_to_le16(sizeof(*rfse));
1308c2ecf20Sopenharmony_ci	cbd.opt[3] = cpu_to_le32(0); /* SI */
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	tmp = dma_alloc_coherent(&si->pdev->dev, sizeof(*rfse) + RFSE_ALIGN,
1338c2ecf20Sopenharmony_ci				 &dma, GFP_KERNEL);
1348c2ecf20Sopenharmony_ci	if (!tmp) {
1358c2ecf20Sopenharmony_ci		dev_err(&si->pdev->dev, "DMA mapping of RFS entry failed!\n");
1368c2ecf20Sopenharmony_ci		return -ENOMEM;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	dma_align = ALIGN(dma, RFSE_ALIGN);
1408c2ecf20Sopenharmony_ci	tmp_align = PTR_ALIGN(tmp, RFSE_ALIGN);
1418c2ecf20Sopenharmony_ci	memcpy(tmp_align, rfse, sizeof(*rfse));
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	cbd.addr[0] = cpu_to_le32(lower_32_bits(dma_align));
1448c2ecf20Sopenharmony_ci	cbd.addr[1] = cpu_to_le32(upper_32_bits(dma_align));
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	err = enetc_send_cmd(si, &cbd);
1478c2ecf20Sopenharmony_ci	if (err)
1488c2ecf20Sopenharmony_ci		dev_err(&si->pdev->dev, "FS entry add failed (%d)!", err);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	dma_free_coherent(&si->pdev->dev, sizeof(*rfse) + RFSE_ALIGN,
1518c2ecf20Sopenharmony_ci			  tmp, dma);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return err;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci#define RSSE_ALIGN	64
1578c2ecf20Sopenharmony_cistatic int enetc_cmd_rss_table(struct enetc_si *si, u32 *table, int count,
1588c2ecf20Sopenharmony_ci			       bool read)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	struct enetc_cbd cbd = {.cmd = 0};
1618c2ecf20Sopenharmony_ci	dma_addr_t dma, dma_align;
1628c2ecf20Sopenharmony_ci	u8 *tmp, *tmp_align;
1638c2ecf20Sopenharmony_ci	int err, i;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if (count < RSSE_ALIGN)
1668c2ecf20Sopenharmony_ci		/* HW only takes in a full 64 entry table */
1678c2ecf20Sopenharmony_ci		return -EINVAL;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	tmp = dma_alloc_coherent(&si->pdev->dev, count + RSSE_ALIGN,
1708c2ecf20Sopenharmony_ci				 &dma, GFP_KERNEL);
1718c2ecf20Sopenharmony_ci	if (!tmp) {
1728c2ecf20Sopenharmony_ci		dev_err(&si->pdev->dev, "DMA mapping of RSS table failed!\n");
1738c2ecf20Sopenharmony_ci		return -ENOMEM;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci	dma_align = ALIGN(dma, RSSE_ALIGN);
1768c2ecf20Sopenharmony_ci	tmp_align = PTR_ALIGN(tmp, RSSE_ALIGN);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (!read)
1798c2ecf20Sopenharmony_ci		for (i = 0; i < count; i++)
1808c2ecf20Sopenharmony_ci			tmp_align[i] = (u8)(table[i]);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/* fill up the descriptor */
1838c2ecf20Sopenharmony_ci	cbd.cmd = read ? 2 : 1;
1848c2ecf20Sopenharmony_ci	cbd.cls = 3;
1858c2ecf20Sopenharmony_ci	cbd.length = cpu_to_le16(count);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	cbd.addr[0] = cpu_to_le32(lower_32_bits(dma_align));
1888c2ecf20Sopenharmony_ci	cbd.addr[1] = cpu_to_le32(upper_32_bits(dma_align));
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	err = enetc_send_cmd(si, &cbd);
1918c2ecf20Sopenharmony_ci	if (err)
1928c2ecf20Sopenharmony_ci		dev_err(&si->pdev->dev, "RSS cmd failed (%d)!", err);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (read)
1958c2ecf20Sopenharmony_ci		for (i = 0; i < count; i++)
1968c2ecf20Sopenharmony_ci			table[i] = tmp_align[i];
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	dma_free_coherent(&si->pdev->dev, count + RSSE_ALIGN, tmp, dma);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return err;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/* Get RSS table */
2048c2ecf20Sopenharmony_ciint enetc_get_rss_table(struct enetc_si *si, u32 *table, int count)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	return enetc_cmd_rss_table(si, table, count, true);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/* Set RSS table */
2108c2ecf20Sopenharmony_ciint enetc_set_rss_table(struct enetc_si *si, const u32 *table, int count)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	return enetc_cmd_rss_table(si, (u32 *)table, count, false);
2138c2ecf20Sopenharmony_ci}
214