1d4fd0404SClaudiu Manoil // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2d4fd0404SClaudiu Manoil /* Copyright 2017-2019 NXP */
3d4fd0404SClaudiu Manoil 
4d4fd0404SClaudiu Manoil #include "enetc.h"
5d4fd0404SClaudiu Manoil 
enetc_setup_cbdr(struct device * dev,struct enetc_hw * hw,int bd_count,struct enetc_cbdr * cbdr)65b4daa7fSVladimir Oltean int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count,
724be14e3SVladimir Oltean 		     struct enetc_cbdr *cbdr)
8176769d1SVladimir Oltean {
95b4daa7fSVladimir Oltean 	int size = bd_count * sizeof(struct enetc_cbd);
10176769d1SVladimir Oltean 
11176769d1SVladimir Oltean 	cbdr->bd_base = dma_alloc_coherent(dev, size, &cbdr->bd_dma_base,
12176769d1SVladimir Oltean 					   GFP_KERNEL);
13176769d1SVladimir Oltean 	if (!cbdr->bd_base)
14176769d1SVladimir Oltean 		return -ENOMEM;
15176769d1SVladimir Oltean 
16176769d1SVladimir Oltean 	/* h/w requires 128B alignment */
17176769d1SVladimir Oltean 	if (!IS_ALIGNED(cbdr->bd_dma_base, 128)) {
1801121ab7SVladimir Oltean 		dma_free_coherent(dev, size, cbdr->bd_base,
1901121ab7SVladimir Oltean 				  cbdr->bd_dma_base);
20176769d1SVladimir Oltean 		return -EINVAL;
21176769d1SVladimir Oltean 	}
22176769d1SVladimir Oltean 
23176769d1SVladimir Oltean 	cbdr->next_to_clean = 0;
24176769d1SVladimir Oltean 	cbdr->next_to_use = 0;
2501121ab7SVladimir Oltean 	cbdr->dma_dev = dev;
265b4daa7fSVladimir Oltean 	cbdr->bd_count = bd_count;
27176769d1SVladimir Oltean 
2827f9025dSVladimir Oltean 	cbdr->pir = hw->reg + ENETC_SICBDRPIR;
2927f9025dSVladimir Oltean 	cbdr->cir = hw->reg + ENETC_SICBDRCIR;
3027f9025dSVladimir Oltean 	cbdr->mr = hw->reg + ENETC_SICBDRMR;
3127f9025dSVladimir Oltean 
32176769d1SVladimir Oltean 	/* set CBDR cache attributes */
33176769d1SVladimir Oltean 	enetc_wr(hw, ENETC_SICAR2,
34176769d1SVladimir Oltean 		 ENETC_SICAR_RD_COHERENT | ENETC_SICAR_WR_COHERENT);
35176769d1SVladimir Oltean 
36176769d1SVladimir Oltean 	enetc_wr(hw, ENETC_SICBDRBAR0, lower_32_bits(cbdr->bd_dma_base));
37176769d1SVladimir Oltean 	enetc_wr(hw, ENETC_SICBDRBAR1, upper_32_bits(cbdr->bd_dma_base));
38176769d1SVladimir Oltean 	enetc_wr(hw, ENETC_SICBDRLENR, ENETC_RTBLENR_LEN(cbdr->bd_count));
39176769d1SVladimir Oltean 
4027f9025dSVladimir Oltean 	enetc_wr_reg(cbdr->pir, cbdr->next_to_clean);
4127f9025dSVladimir Oltean 	enetc_wr_reg(cbdr->cir, cbdr->next_to_use);
42176769d1SVladimir Oltean 	/* enable ring */
4327f9025dSVladimir Oltean 	enetc_wr_reg(cbdr->mr, BIT(31));
4424be14e3SVladimir Oltean 
4524be14e3SVladimir Oltean 	return 0;
4624be14e3SVladimir Oltean }
47*e3972399SVladimir Oltean EXPORT_SYMBOL_GPL(enetc_setup_cbdr);
4824be14e3SVladimir Oltean 
enetc_teardown_cbdr(struct enetc_cbdr * cbdr)490bfde022SVladimir Oltean void enetc_teardown_cbdr(struct enetc_cbdr *cbdr)
5024be14e3SVladimir Oltean {
5124be14e3SVladimir Oltean 	int size = cbdr->bd_count * sizeof(struct enetc_cbd);
5224be14e3SVladimir Oltean 
530bfde022SVladimir Oltean 	/* disable ring */
540bfde022SVladimir Oltean 	enetc_wr_reg(cbdr->mr, 0);
550bfde022SVladimir Oltean 
5624be14e3SVladimir Oltean 	dma_free_coherent(cbdr->dma_dev, size, cbdr->bd_base,
5724be14e3SVladimir Oltean 			  cbdr->bd_dma_base);
5824be14e3SVladimir Oltean 	cbdr->bd_base = NULL;
5924be14e3SVladimir Oltean 	cbdr->dma_dev = NULL;
60176769d1SVladimir Oltean }
61*e3972399SVladimir Oltean EXPORT_SYMBOL_GPL(enetc_teardown_cbdr);
62176769d1SVladimir Oltean 
enetc_clean_cbdr(struct enetc_cbdr * ring)6301121ab7SVladimir Oltean static void enetc_clean_cbdr(struct enetc_cbdr *ring)
64d4fd0404SClaudiu Manoil {
65d4fd0404SClaudiu Manoil 	struct enetc_cbd *dest_cbd;
66d4fd0404SClaudiu Manoil 	int i, status;
67d4fd0404SClaudiu Manoil 
68d4fd0404SClaudiu Manoil 	i = ring->next_to_clean;
69d4fd0404SClaudiu Manoil 
70d4fd0404SClaudiu Manoil 	while (enetc_rd_reg(ring->cir) != i) {
71d4fd0404SClaudiu Manoil 		dest_cbd = ENETC_CBD(*ring, i);
72d4fd0404SClaudiu Manoil 		status = dest_cbd->status_flags & ENETC_CBD_STATUS_MASK;
73d4fd0404SClaudiu Manoil 		if (status)
7401121ab7SVladimir Oltean 			dev_warn(ring->dma_dev, "CMD err %04x for cmd %04x\n",
75d4fd0404SClaudiu Manoil 				 status, dest_cbd->cmd);
76d4fd0404SClaudiu Manoil 
77d4fd0404SClaudiu Manoil 		memset(dest_cbd, 0, sizeof(*dest_cbd));
78d4fd0404SClaudiu Manoil 
79d4fd0404SClaudiu Manoil 		i = (i + 1) % ring->bd_count;
80d4fd0404SClaudiu Manoil 	}
81d4fd0404SClaudiu Manoil 
82d4fd0404SClaudiu Manoil 	ring->next_to_clean = i;
83d4fd0404SClaudiu Manoil }
84d4fd0404SClaudiu Manoil 
enetc_cbd_unused(struct enetc_cbdr * r)85d4fd0404SClaudiu Manoil static int enetc_cbd_unused(struct enetc_cbdr *r)
86d4fd0404SClaudiu Manoil {
87d4fd0404SClaudiu Manoil 	return (r->next_to_clean - r->next_to_use - 1 + r->bd_count) %
88d4fd0404SClaudiu Manoil 		r->bd_count;
89d4fd0404SClaudiu Manoil }
90d4fd0404SClaudiu Manoil 
enetc_send_cmd(struct enetc_si * si,struct enetc_cbd * cbd)9134c6adf1SPo Liu int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd)
92d4fd0404SClaudiu Manoil {
93d4fd0404SClaudiu Manoil 	struct enetc_cbdr *ring = &si->cbd_ring;
94d4fd0404SClaudiu Manoil 	int timeout = ENETC_CBDR_TIMEOUT;
95d4fd0404SClaudiu Manoil 	struct enetc_cbd *dest_cbd;
96d4fd0404SClaudiu Manoil 	int i;
97d4fd0404SClaudiu Manoil 
98d4fd0404SClaudiu Manoil 	if (unlikely(!ring->bd_base))
99d4fd0404SClaudiu Manoil 		return -EIO;
100d4fd0404SClaudiu Manoil 
101d4fd0404SClaudiu Manoil 	if (unlikely(!enetc_cbd_unused(ring)))
10201121ab7SVladimir Oltean 		enetc_clean_cbdr(ring);
103d4fd0404SClaudiu Manoil 
104d4fd0404SClaudiu Manoil 	i = ring->next_to_use;
105d4fd0404SClaudiu Manoil 	dest_cbd = ENETC_CBD(*ring, i);
106d4fd0404SClaudiu Manoil 
107d4fd0404SClaudiu Manoil 	/* copy command to the ring */
108d4fd0404SClaudiu Manoil 	*dest_cbd = *cbd;
109d4fd0404SClaudiu Manoil 	i = (i + 1) % ring->bd_count;
110d4fd0404SClaudiu Manoil 
111d4fd0404SClaudiu Manoil 	ring->next_to_use = i;
112d4fd0404SClaudiu Manoil 	/* let H/W know BD ring has been updated */
113d4fd0404SClaudiu Manoil 	enetc_wr_reg(ring->pir, i);
114d4fd0404SClaudiu Manoil 
115d4fd0404SClaudiu Manoil 	do {
116d4fd0404SClaudiu Manoil 		if (enetc_rd_reg(ring->cir) == i)
117d4fd0404SClaudiu Manoil 			break;
118d4fd0404SClaudiu Manoil 		udelay(10); /* cannot sleep, rtnl_lock() */
119d4fd0404SClaudiu Manoil 		timeout -= 10;
120d4fd0404SClaudiu Manoil 	} while (timeout);
121d4fd0404SClaudiu Manoil 
122d4fd0404SClaudiu Manoil 	if (!timeout)
123d4fd0404SClaudiu Manoil 		return -EBUSY;
124d4fd0404SClaudiu Manoil 
12534c6adf1SPo Liu 	/* CBD may writeback data, feedback up level */
12634c6adf1SPo Liu 	*cbd = *dest_cbd;
12734c6adf1SPo Liu 
12801121ab7SVladimir Oltean 	enetc_clean_cbdr(ring);
129d4fd0404SClaudiu Manoil 
130d4fd0404SClaudiu Manoil 	return 0;
131d4fd0404SClaudiu Manoil }
132*e3972399SVladimir Oltean EXPORT_SYMBOL_GPL(enetc_send_cmd);
133d4fd0404SClaudiu Manoil 
enetc_clear_mac_flt_entry(struct enetc_si * si,int index)134d4fd0404SClaudiu Manoil int enetc_clear_mac_flt_entry(struct enetc_si *si, int index)
135d4fd0404SClaudiu Manoil {
136d4fd0404SClaudiu Manoil 	struct enetc_cbd cbd;
137d4fd0404SClaudiu Manoil 
138d4fd0404SClaudiu Manoil 	memset(&cbd, 0, sizeof(cbd));
139d4fd0404SClaudiu Manoil 
140d4fd0404SClaudiu Manoil 	cbd.cls = 1;
141d4fd0404SClaudiu Manoil 	cbd.status_flags = ENETC_CBD_FLAGS_SF;
142d4fd0404SClaudiu Manoil 	cbd.index = cpu_to_le16(index);
143d4fd0404SClaudiu Manoil 
144d4fd0404SClaudiu Manoil 	return enetc_send_cmd(si, &cbd);
145d4fd0404SClaudiu Manoil }
146*e3972399SVladimir Oltean EXPORT_SYMBOL_GPL(enetc_clear_mac_flt_entry);
147d4fd0404SClaudiu Manoil 
enetc_set_mac_flt_entry(struct enetc_si * si,int index,char * mac_addr,int si_map)148d4fd0404SClaudiu Manoil int enetc_set_mac_flt_entry(struct enetc_si *si, int index,
149d4fd0404SClaudiu Manoil 			    char *mac_addr, int si_map)
150d4fd0404SClaudiu Manoil {
151d4fd0404SClaudiu Manoil 	struct enetc_cbd cbd;
152d4fd0404SClaudiu Manoil 	u32 upper;
153d4fd0404SClaudiu Manoil 	u16 lower;
154d4fd0404SClaudiu Manoil 
155d4fd0404SClaudiu Manoil 	memset(&cbd, 0, sizeof(cbd));
156d4fd0404SClaudiu Manoil 
157d4fd0404SClaudiu Manoil 	/* fill up the "set" descriptor */
158d4fd0404SClaudiu Manoil 	cbd.cls = 1;
159d4fd0404SClaudiu Manoil 	cbd.status_flags = ENETC_CBD_FLAGS_SF;
160d4fd0404SClaudiu Manoil 	cbd.index = cpu_to_le16(index);
161d4fd0404SClaudiu Manoil 	cbd.opt[3] = cpu_to_le32(si_map);
162d4fd0404SClaudiu Manoil 	/* enable entry */
163d4fd0404SClaudiu Manoil 	cbd.opt[0] = cpu_to_le32(BIT(31));
164d4fd0404SClaudiu Manoil 
165d4fd0404SClaudiu Manoil 	upper = *(const u32 *)mac_addr;
166d4fd0404SClaudiu Manoil 	lower = *(const u16 *)(mac_addr + 4);
167d4fd0404SClaudiu Manoil 	cbd.addr[0] = cpu_to_le32(upper);
168d4fd0404SClaudiu Manoil 	cbd.addr[1] = cpu_to_le32(lower);
169d4fd0404SClaudiu Manoil 
170d4fd0404SClaudiu Manoil 	return enetc_send_cmd(si, &cbd);
171d4fd0404SClaudiu Manoil }
172*e3972399SVladimir Oltean EXPORT_SYMBOL_GPL(enetc_set_mac_flt_entry);
173d382563fSClaudiu Manoil 
174d382563fSClaudiu Manoil /* Set entry in RFS table */
enetc_set_fs_entry(struct enetc_si * si,struct enetc_cmd_rfse * rfse,int index)175d382563fSClaudiu Manoil int enetc_set_fs_entry(struct enetc_si *si, struct enetc_cmd_rfse *rfse,
176d382563fSClaudiu Manoil 		       int index)
177d382563fSClaudiu Manoil {
17801121ab7SVladimir Oltean 	struct enetc_cbdr *ring = &si->cbd_ring;
179d382563fSClaudiu Manoil 	struct enetc_cbd cbd = {.cmd = 0};
180d382563fSClaudiu Manoil 	void *tmp, *tmp_align;
1810cc11cdbSPo Liu 	dma_addr_t dma;
182d382563fSClaudiu Manoil 	int err;
183d382563fSClaudiu Manoil 
184d382563fSClaudiu Manoil 	/* fill up the "set" descriptor */
185d382563fSClaudiu Manoil 	cbd.cmd = 0;
186d382563fSClaudiu Manoil 	cbd.cls = 4;
187d382563fSClaudiu Manoil 	cbd.index = cpu_to_le16(index);
188d382563fSClaudiu Manoil 	cbd.opt[3] = cpu_to_le32(0); /* SI */
189d382563fSClaudiu Manoil 
1900cc11cdbSPo Liu 	tmp = enetc_cbd_alloc_data_mem(si, &cbd, sizeof(*rfse),
1910cc11cdbSPo Liu 				       &dma, &tmp_align);
1920cc11cdbSPo Liu 	if (!tmp)
193d382563fSClaudiu Manoil 		return -ENOMEM;
194d382563fSClaudiu Manoil 
195d382563fSClaudiu Manoil 	memcpy(tmp_align, rfse, sizeof(*rfse));
196d382563fSClaudiu Manoil 
197d382563fSClaudiu Manoil 	err = enetc_send_cmd(si, &cbd);
198d382563fSClaudiu Manoil 	if (err)
19901121ab7SVladimir Oltean 		dev_err(ring->dma_dev, "FS entry add failed (%d)!", err);
200d382563fSClaudiu Manoil 
2010cc11cdbSPo Liu 	enetc_cbd_free_data_mem(si, sizeof(*rfse), tmp, &dma);
202d382563fSClaudiu Manoil 
203d382563fSClaudiu Manoil 	return err;
204d382563fSClaudiu Manoil }
205*e3972399SVladimir Oltean EXPORT_SYMBOL_GPL(enetc_set_fs_entry);
206d382563fSClaudiu Manoil 
enetc_cmd_rss_table(struct enetc_si * si,u32 * table,int count,bool read)207d382563fSClaudiu Manoil static int enetc_cmd_rss_table(struct enetc_si *si, u32 *table, int count,
208d382563fSClaudiu Manoil 			       bool read)
209d382563fSClaudiu Manoil {
21001121ab7SVladimir Oltean 	struct enetc_cbdr *ring = &si->cbd_ring;
211d382563fSClaudiu Manoil 	struct enetc_cbd cbd = {.cmd = 0};
212d382563fSClaudiu Manoil 	u8 *tmp, *tmp_align;
2130cc11cdbSPo Liu 	dma_addr_t dma;
214d382563fSClaudiu Manoil 	int err, i;
215d382563fSClaudiu Manoil 
2160cc11cdbSPo Liu 	if (count < ENETC_CBD_DATA_MEM_ALIGN)
217d382563fSClaudiu Manoil 		/* HW only takes in a full 64 entry table */
218d382563fSClaudiu Manoil 		return -EINVAL;
219d382563fSClaudiu Manoil 
2200cc11cdbSPo Liu 	tmp = enetc_cbd_alloc_data_mem(si, &cbd, count,
2210cc11cdbSPo Liu 				       &dma, (void *)&tmp_align);
2220cc11cdbSPo Liu 	if (!tmp)
223d382563fSClaudiu Manoil 		return -ENOMEM;
224d382563fSClaudiu Manoil 
225d382563fSClaudiu Manoil 	if (!read)
226d382563fSClaudiu Manoil 		for (i = 0; i < count; i++)
227d382563fSClaudiu Manoil 			tmp_align[i] = (u8)(table[i]);
228d382563fSClaudiu Manoil 
229d382563fSClaudiu Manoil 	/* fill up the descriptor */
230d382563fSClaudiu Manoil 	cbd.cmd = read ? 2 : 1;
231d382563fSClaudiu Manoil 	cbd.cls = 3;
232d382563fSClaudiu Manoil 
233d382563fSClaudiu Manoil 	err = enetc_send_cmd(si, &cbd);
234d382563fSClaudiu Manoil 	if (err)
23501121ab7SVladimir Oltean 		dev_err(ring->dma_dev, "RSS cmd failed (%d)!", err);
236d382563fSClaudiu Manoil 
237d382563fSClaudiu Manoil 	if (read)
238d382563fSClaudiu Manoil 		for (i = 0; i < count; i++)
239d382563fSClaudiu Manoil 			table[i] = tmp_align[i];
240d382563fSClaudiu Manoil 
2410cc11cdbSPo Liu 	enetc_cbd_free_data_mem(si, count, tmp, &dma);
242d382563fSClaudiu Manoil 
243d382563fSClaudiu Manoil 	return err;
244d382563fSClaudiu Manoil }
245d382563fSClaudiu Manoil 
246d382563fSClaudiu Manoil /* Get RSS table */
enetc_get_rss_table(struct enetc_si * si,u32 * table,int count)247d382563fSClaudiu Manoil int enetc_get_rss_table(struct enetc_si *si, u32 *table, int count)
248d382563fSClaudiu Manoil {
249d382563fSClaudiu Manoil 	return enetc_cmd_rss_table(si, table, count, true);
250d382563fSClaudiu Manoil }
251*e3972399SVladimir Oltean EXPORT_SYMBOL_GPL(enetc_get_rss_table);
252d382563fSClaudiu Manoil 
253d382563fSClaudiu Manoil /* Set RSS table */
enetc_set_rss_table(struct enetc_si * si,const u32 * table,int count)254d382563fSClaudiu Manoil int enetc_set_rss_table(struct enetc_si *si, const u32 *table, int count)
255d382563fSClaudiu Manoil {
256d382563fSClaudiu Manoil 	return enetc_cmd_rss_table(si, (u32 *)table, count, false);
257d382563fSClaudiu Manoil }
258*e3972399SVladimir Oltean EXPORT_SYMBOL_GPL(enetc_set_rss_table);
259