1// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2/* Copyright 2017-2019 NXP */
3
4#include "enetc.h"
5
6int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count,
7		     struct enetc_cbdr *cbdr)
8{
9	int size = bd_count * sizeof(struct enetc_cbd);
10
11	cbdr->bd_base = dma_alloc_coherent(dev, size, &cbdr->bd_dma_base,
12					   GFP_KERNEL);
13	if (!cbdr->bd_base)
14		return -ENOMEM;
15
16	/* h/w requires 128B alignment */
17	if (!IS_ALIGNED(cbdr->bd_dma_base, 128)) {
18		dma_free_coherent(dev, size, cbdr->bd_base,
19				  cbdr->bd_dma_base);
20		return -EINVAL;
21	}
22
23	cbdr->next_to_clean = 0;
24	cbdr->next_to_use = 0;
25	cbdr->dma_dev = dev;
26	cbdr->bd_count = bd_count;
27
28	cbdr->pir = hw->reg + ENETC_SICBDRPIR;
29	cbdr->cir = hw->reg + ENETC_SICBDRCIR;
30	cbdr->mr = hw->reg + ENETC_SICBDRMR;
31
32	/* set CBDR cache attributes */
33	enetc_wr(hw, ENETC_SICAR2,
34		 ENETC_SICAR_RD_COHERENT | ENETC_SICAR_WR_COHERENT);
35
36	enetc_wr(hw, ENETC_SICBDRBAR0, lower_32_bits(cbdr->bd_dma_base));
37	enetc_wr(hw, ENETC_SICBDRBAR1, upper_32_bits(cbdr->bd_dma_base));
38	enetc_wr(hw, ENETC_SICBDRLENR, ENETC_RTBLENR_LEN(cbdr->bd_count));
39
40	enetc_wr_reg(cbdr->pir, cbdr->next_to_clean);
41	enetc_wr_reg(cbdr->cir, cbdr->next_to_use);
42	/* enable ring */
43	enetc_wr_reg(cbdr->mr, BIT(31));
44
45	return 0;
46}
47EXPORT_SYMBOL_GPL(enetc_setup_cbdr);
48
49void enetc_teardown_cbdr(struct enetc_cbdr *cbdr)
50{
51	int size = cbdr->bd_count * sizeof(struct enetc_cbd);
52
53	/* disable ring */
54	enetc_wr_reg(cbdr->mr, 0);
55
56	dma_free_coherent(cbdr->dma_dev, size, cbdr->bd_base,
57			  cbdr->bd_dma_base);
58	cbdr->bd_base = NULL;
59	cbdr->dma_dev = NULL;
60}
61EXPORT_SYMBOL_GPL(enetc_teardown_cbdr);
62
63static void enetc_clean_cbdr(struct enetc_cbdr *ring)
64{
65	struct enetc_cbd *dest_cbd;
66	int i, status;
67
68	i = ring->next_to_clean;
69
70	while (enetc_rd_reg(ring->cir) != i) {
71		dest_cbd = ENETC_CBD(*ring, i);
72		status = dest_cbd->status_flags & ENETC_CBD_STATUS_MASK;
73		if (status)
74			dev_warn(ring->dma_dev, "CMD err %04x for cmd %04x\n",
75				 status, dest_cbd->cmd);
76
77		memset(dest_cbd, 0, sizeof(*dest_cbd));
78
79		i = (i + 1) % ring->bd_count;
80	}
81
82	ring->next_to_clean = i;
83}
84
85static int enetc_cbd_unused(struct enetc_cbdr *r)
86{
87	return (r->next_to_clean - r->next_to_use - 1 + r->bd_count) %
88		r->bd_count;
89}
90
91int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd)
92{
93	struct enetc_cbdr *ring = &si->cbd_ring;
94	int timeout = ENETC_CBDR_TIMEOUT;
95	struct enetc_cbd *dest_cbd;
96	int i;
97
98	if (unlikely(!ring->bd_base))
99		return -EIO;
100
101	if (unlikely(!enetc_cbd_unused(ring)))
102		enetc_clean_cbdr(ring);
103
104	i = ring->next_to_use;
105	dest_cbd = ENETC_CBD(*ring, i);
106
107	/* copy command to the ring */
108	*dest_cbd = *cbd;
109	i = (i + 1) % ring->bd_count;
110
111	ring->next_to_use = i;
112	/* let H/W know BD ring has been updated */
113	enetc_wr_reg(ring->pir, i);
114
115	do {
116		if (enetc_rd_reg(ring->cir) == i)
117			break;
118		udelay(10); /* cannot sleep, rtnl_lock() */
119		timeout -= 10;
120	} while (timeout);
121
122	if (!timeout)
123		return -EBUSY;
124
125	/* CBD may writeback data, feedback up level */
126	*cbd = *dest_cbd;
127
128	enetc_clean_cbdr(ring);
129
130	return 0;
131}
132EXPORT_SYMBOL_GPL(enetc_send_cmd);
133
134int enetc_clear_mac_flt_entry(struct enetc_si *si, int index)
135{
136	struct enetc_cbd cbd;
137
138	memset(&cbd, 0, sizeof(cbd));
139
140	cbd.cls = 1;
141	cbd.status_flags = ENETC_CBD_FLAGS_SF;
142	cbd.index = cpu_to_le16(index);
143
144	return enetc_send_cmd(si, &cbd);
145}
146EXPORT_SYMBOL_GPL(enetc_clear_mac_flt_entry);
147
148int enetc_set_mac_flt_entry(struct enetc_si *si, int index,
149			    char *mac_addr, int si_map)
150{
151	struct enetc_cbd cbd;
152	u32 upper;
153	u16 lower;
154
155	memset(&cbd, 0, sizeof(cbd));
156
157	/* fill up the "set" descriptor */
158	cbd.cls = 1;
159	cbd.status_flags = ENETC_CBD_FLAGS_SF;
160	cbd.index = cpu_to_le16(index);
161	cbd.opt[3] = cpu_to_le32(si_map);
162	/* enable entry */
163	cbd.opt[0] = cpu_to_le32(BIT(31));
164
165	upper = *(const u32 *)mac_addr;
166	lower = *(const u16 *)(mac_addr + 4);
167	cbd.addr[0] = cpu_to_le32(upper);
168	cbd.addr[1] = cpu_to_le32(lower);
169
170	return enetc_send_cmd(si, &cbd);
171}
172EXPORT_SYMBOL_GPL(enetc_set_mac_flt_entry);
173
174/* Set entry in RFS table */
175int enetc_set_fs_entry(struct enetc_si *si, struct enetc_cmd_rfse *rfse,
176		       int index)
177{
178	struct enetc_cbdr *ring = &si->cbd_ring;
179	struct enetc_cbd cbd = {.cmd = 0};
180	void *tmp, *tmp_align;
181	dma_addr_t dma;
182	int err;
183
184	/* fill up the "set" descriptor */
185	cbd.cmd = 0;
186	cbd.cls = 4;
187	cbd.index = cpu_to_le16(index);
188	cbd.opt[3] = cpu_to_le32(0); /* SI */
189
190	tmp = enetc_cbd_alloc_data_mem(si, &cbd, sizeof(*rfse),
191				       &dma, &tmp_align);
192	if (!tmp)
193		return -ENOMEM;
194
195	memcpy(tmp_align, rfse, sizeof(*rfse));
196
197	err = enetc_send_cmd(si, &cbd);
198	if (err)
199		dev_err(ring->dma_dev, "FS entry add failed (%d)!", err);
200
201	enetc_cbd_free_data_mem(si, sizeof(*rfse), tmp, &dma);
202
203	return err;
204}
205EXPORT_SYMBOL_GPL(enetc_set_fs_entry);
206
207static int enetc_cmd_rss_table(struct enetc_si *si, u32 *table, int count,
208			       bool read)
209{
210	struct enetc_cbdr *ring = &si->cbd_ring;
211	struct enetc_cbd cbd = {.cmd = 0};
212	u8 *tmp, *tmp_align;
213	dma_addr_t dma;
214	int err, i;
215
216	if (count < ENETC_CBD_DATA_MEM_ALIGN)
217		/* HW only takes in a full 64 entry table */
218		return -EINVAL;
219
220	tmp = enetc_cbd_alloc_data_mem(si, &cbd, count,
221				       &dma, (void *)&tmp_align);
222	if (!tmp)
223		return -ENOMEM;
224
225	if (!read)
226		for (i = 0; i < count; i++)
227			tmp_align[i] = (u8)(table[i]);
228
229	/* fill up the descriptor */
230	cbd.cmd = read ? 2 : 1;
231	cbd.cls = 3;
232
233	err = enetc_send_cmd(si, &cbd);
234	if (err)
235		dev_err(ring->dma_dev, "RSS cmd failed (%d)!", err);
236
237	if (read)
238		for (i = 0; i < count; i++)
239			table[i] = tmp_align[i];
240
241	enetc_cbd_free_data_mem(si, count, tmp, &dma);
242
243	return err;
244}
245
246/* Get RSS table */
247int enetc_get_rss_table(struct enetc_si *si, u32 *table, int count)
248{
249	return enetc_cmd_rss_table(si, table, count, true);
250}
251EXPORT_SYMBOL_GPL(enetc_get_rss_table);
252
253/* Set RSS table */
254int enetc_set_rss_table(struct enetc_si *si, const u32 *table, int count)
255{
256	return enetc_cmd_rss_table(si, (u32 *)table, count, false);
257}
258EXPORT_SYMBOL_GPL(enetc_set_rss_table);
259