1 | // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) |
2 | /* Copyright 2017-2019 NXP */ |
3 | |
4 | #include "enetc.h" |
5 | |
6 | int 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, dma_handle: &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, cpu_addr: cbdr->bd_base, |
19 | dma_handle: 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 | } |
47 | EXPORT_SYMBOL_GPL(enetc_setup_cbdr); |
48 | |
49 | void 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(dev: cbdr->dma_dev, size, cpu_addr: cbdr->bd_base, |
57 | dma_handle: cbdr->bd_dma_base); |
58 | cbdr->bd_base = NULL; |
59 | cbdr->dma_dev = NULL; |
60 | } |
61 | EXPORT_SYMBOL_GPL(enetc_teardown_cbdr); |
62 | |
63 | static 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 | |
85 | static 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 | |
91 | int 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 | } |
132 | EXPORT_SYMBOL_GPL(enetc_send_cmd); |
133 | |
134 | int 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 | } |
146 | EXPORT_SYMBOL_GPL(enetc_clear_mac_flt_entry); |
147 | |
148 | int 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 | } |
172 | EXPORT_SYMBOL_GPL(enetc_set_mac_flt_entry); |
173 | |
174 | /* Set entry in RFS table */ |
175 | int 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: &cbd, size: sizeof(*rfse), |
191 | dma: &dma, data_align: &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, size: sizeof(*rfse), data: tmp, dma: &dma); |
202 | |
203 | return err; |
204 | } |
205 | EXPORT_SYMBOL_GPL(enetc_set_fs_entry); |
206 | |
207 | static int (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: &cbd, size: count, |
221 | dma: &dma, data_align: (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, size: count, data: tmp, dma: &dma); |
242 | |
243 | return err; |
244 | } |
245 | |
246 | /* Get RSS table */ |
247 | int (struct enetc_si *si, u32 *table, int count) |
248 | { |
249 | return enetc_cmd_rss_table(si, table, count, read: true); |
250 | } |
251 | EXPORT_SYMBOL_GPL(enetc_get_rss_table); |
252 | |
253 | /* Set RSS table */ |
254 | int (struct enetc_si *si, const u32 *table, int count) |
255 | { |
256 | return enetc_cmd_rss_table(si, table: (u32 *)table, count, read: false); |
257 | } |
258 | EXPORT_SYMBOL_GPL(enetc_set_rss_table); |
259 | |