1 // SPDX-License-Identifier: GPL-2.0
2 /* Texas Instruments PRUETH Switch Driver
3 *
4 * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com
5 */
6 #include <linux/etherdevice.h>
7 #include <linux/kernel.h>
8 #include <linux/remoteproc.h>
9 #include <net/switchdev.h>
10 #include "icssm_prueth.h"
11 #include "icssm_prueth_switch.h"
12 #include "icssm_prueth_fdb_tbl.h"
13
14 #define FDB_IDX_TBL_ENTRY(n) (&prueth->fdb_tbl->index_a->index_tbl_entry[n])
15
16 #define FDB_MAC_TBL_ENTRY(n) (&prueth->fdb_tbl->mac_tbl_a->mac_tbl_entry[n])
17
18 #define FLAG_IS_STATIC BIT(0)
19 #define FLAG_ACTIVE BIT(1)
20
21 #define FDB_LEARN 1
22 #define FDB_PURGE 2
23
24 struct icssm_prueth_sw_fdb_work {
25 netdevice_tracker ndev_tracker;
26 struct work_struct work;
27 struct prueth_emac *emac;
28 u8 addr[ETH_ALEN];
29 int event;
30 };
31
32 const struct prueth_queue_info sw_queue_infos[][NUM_QUEUES] = {
33 [PRUETH_PORT_QUEUE_HOST] = {
34 [PRUETH_QUEUE1] = {
35 P0_Q1_BUFFER_OFFSET,
36 P0_QUEUE_DESC_OFFSET,
37 P0_Q1_BD_OFFSET,
38 P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE),
39 },
40 [PRUETH_QUEUE2] = {
41 P0_Q2_BUFFER_OFFSET,
42 P0_QUEUE_DESC_OFFSET + 8,
43 P0_Q2_BD_OFFSET,
44 P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE),
45 },
46 [PRUETH_QUEUE3] = {
47 P0_Q3_BUFFER_OFFSET,
48 P0_QUEUE_DESC_OFFSET + 16,
49 P0_Q3_BD_OFFSET,
50 P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE),
51 },
52 [PRUETH_QUEUE4] = {
53 P0_Q4_BUFFER_OFFSET,
54 P0_QUEUE_DESC_OFFSET + 24,
55 P0_Q4_BD_OFFSET,
56 P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE),
57 },
58 },
59 [PRUETH_PORT_QUEUE_MII0] = {
60 [PRUETH_QUEUE1] = {
61 P1_Q1_BUFFER_OFFSET,
62 P1_Q1_BUFFER_OFFSET +
63 ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE),
64 P1_Q1_BD_OFFSET,
65 P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE),
66 },
67 [PRUETH_QUEUE2] = {
68 P1_Q2_BUFFER_OFFSET,
69 P1_Q2_BUFFER_OFFSET +
70 ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE),
71 P1_Q2_BD_OFFSET,
72 P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE),
73 },
74 [PRUETH_QUEUE3] = {
75 P1_Q3_BUFFER_OFFSET,
76 P1_Q3_BUFFER_OFFSET +
77 ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE),
78 P1_Q3_BD_OFFSET,
79 P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE),
80 },
81 [PRUETH_QUEUE4] = {
82 P1_Q4_BUFFER_OFFSET,
83 P1_Q4_BUFFER_OFFSET +
84 ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE),
85 P1_Q4_BD_OFFSET,
86 P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE),
87 },
88 },
89 [PRUETH_PORT_QUEUE_MII1] = {
90 [PRUETH_QUEUE1] = {
91 P2_Q1_BUFFER_OFFSET,
92 P2_Q1_BUFFER_OFFSET +
93 ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE),
94 P2_Q1_BD_OFFSET,
95 P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE),
96 },
97 [PRUETH_QUEUE2] = {
98 P2_Q2_BUFFER_OFFSET,
99 P2_Q2_BUFFER_OFFSET +
100 ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE),
101 P2_Q2_BD_OFFSET,
102 P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE),
103 },
104 [PRUETH_QUEUE3] = {
105 P2_Q3_BUFFER_OFFSET,
106 P2_Q3_BUFFER_OFFSET +
107 ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE),
108 P2_Q3_BD_OFFSET,
109 P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE),
110 },
111 [PRUETH_QUEUE4] = {
112 P2_Q4_BUFFER_OFFSET,
113 P2_Q4_BUFFER_OFFSET +
114 ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE),
115 P2_Q4_BD_OFFSET,
116 P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE),
117 },
118 },
119 };
120
121 static const struct prueth_queue_info rx_queue_infos[][NUM_QUEUES] = {
122 [PRUETH_PORT_QUEUE_HOST] = {
123 [PRUETH_QUEUE1] = {
124 P0_Q1_BUFFER_OFFSET,
125 HOST_QUEUE_DESC_OFFSET,
126 P0_Q1_BD_OFFSET,
127 P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE),
128 },
129 [PRUETH_QUEUE2] = {
130 P0_Q2_BUFFER_OFFSET,
131 HOST_QUEUE_DESC_OFFSET + 8,
132 P0_Q2_BD_OFFSET,
133 P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE),
134 },
135 [PRUETH_QUEUE3] = {
136 P0_Q3_BUFFER_OFFSET,
137 HOST_QUEUE_DESC_OFFSET + 16,
138 P0_Q3_BD_OFFSET,
139 P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE),
140 },
141 [PRUETH_QUEUE4] = {
142 P0_Q4_BUFFER_OFFSET,
143 HOST_QUEUE_DESC_OFFSET + 24,
144 P0_Q4_BD_OFFSET,
145 P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE),
146 },
147 },
148 [PRUETH_PORT_QUEUE_MII0] = {
149 [PRUETH_QUEUE1] = {
150 P1_Q1_BUFFER_OFFSET,
151 P1_QUEUE_DESC_OFFSET,
152 P1_Q1_BD_OFFSET,
153 P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE),
154 },
155 [PRUETH_QUEUE2] = {
156 P1_Q2_BUFFER_OFFSET,
157 P1_QUEUE_DESC_OFFSET + 8,
158 P1_Q2_BD_OFFSET,
159 P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE),
160 },
161 [PRUETH_QUEUE3] = {
162 P1_Q3_BUFFER_OFFSET,
163 P1_QUEUE_DESC_OFFSET + 16,
164 P1_Q3_BD_OFFSET,
165 P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE),
166 },
167 [PRUETH_QUEUE4] = {
168 P1_Q4_BUFFER_OFFSET,
169 P1_QUEUE_DESC_OFFSET + 24,
170 P1_Q4_BD_OFFSET,
171 P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE),
172 },
173 },
174 [PRUETH_PORT_QUEUE_MII1] = {
175 [PRUETH_QUEUE1] = {
176 P2_Q1_BUFFER_OFFSET,
177 P2_QUEUE_DESC_OFFSET,
178 P2_Q1_BD_OFFSET,
179 P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE),
180 },
181 [PRUETH_QUEUE2] = {
182 P2_Q2_BUFFER_OFFSET,
183 P2_QUEUE_DESC_OFFSET + 8,
184 P2_Q2_BD_OFFSET,
185 P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE),
186 },
187 [PRUETH_QUEUE3] = {
188 P2_Q3_BUFFER_OFFSET,
189 P2_QUEUE_DESC_OFFSET + 16,
190 P2_Q3_BD_OFFSET,
191 P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE),
192 },
193 [PRUETH_QUEUE4] = {
194 P2_Q4_BUFFER_OFFSET,
195 P2_QUEUE_DESC_OFFSET + 24,
196 P2_Q4_BD_OFFSET,
197 P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE),
198 },
199 },
200 };
201
icssm_prueth_sw_free_fdb_table(struct prueth * prueth)202 void icssm_prueth_sw_free_fdb_table(struct prueth *prueth)
203 {
204 if (prueth->emac_configured)
205 return;
206
207 kfree(prueth->fdb_tbl);
208 prueth->fdb_tbl = NULL;
209 }
210
icssm_prueth_sw_fdb_tbl_init(struct prueth * prueth)211 void icssm_prueth_sw_fdb_tbl_init(struct prueth *prueth)
212 {
213 struct fdb_tbl *t = prueth->fdb_tbl;
214 void __iomem *sram_base;
215 u8 val;
216
217 sram_base = prueth->mem[PRUETH_MEM_SHARED_RAM].va;
218
219 t->index_a = sram_base + V2_1_FDB_TBL_OFFSET;
220 t->mac_tbl_a = sram_base + FDB_MAC_TBL_OFFSET;
221 t->port1_stp_cfg = sram_base + FDB_PORT1_STP_CFG_OFFSET;
222 t->port2_stp_cfg = sram_base + FDB_PORT2_STP_CFG_OFFSET;
223 t->flood_enable_flags = sram_base + FDB_FLOOD_ENABLE_FLAGS_OFFSET;
224 t->locks = sram_base + FDB_LOCKS_OFFSET;
225
226 val = readb(t->flood_enable_flags);
227 /* host_flood_enable = 1 */
228 val |= BIT(0);
229 /* port1_flood_enable = 1 */
230 val |= BIT(1);
231 /* port2_flood_enable = 1 */
232 val |= BIT(2);
233 writeb(val, t->flood_enable_flags);
234
235 writeb(0, &t->locks->host_lock);
236 t->total_entries = 0;
237 }
238
icssm_pru_lock_done(struct fdb_tbl * fdb_tbl)239 static u8 icssm_pru_lock_done(struct fdb_tbl *fdb_tbl)
240 {
241 return readb(&fdb_tbl->locks->pru_locks);
242 }
243
icssm_prueth_sw_fdb_spin_lock(struct fdb_tbl * fdb_tbl)244 static int icssm_prueth_sw_fdb_spin_lock(struct fdb_tbl *fdb_tbl)
245 {
246 u8 done;
247 int ret;
248
249 /* Take the host lock */
250 writeb(1, &fdb_tbl->locks->host_lock);
251
252 /* Wait for the PRUs to release their locks */
253 ret = read_poll_timeout(icssm_pru_lock_done, done, done == 0,
254 1, 10, false, fdb_tbl);
255 if (ret == -ETIMEDOUT)
256 writeb(0, &fdb_tbl->locks->host_lock);
257
258 return ret;
259 }
260
icssm_prueth_sw_fdb_spin_unlock(struct fdb_tbl * fdb_tbl)261 static void icssm_prueth_sw_fdb_spin_unlock(struct fdb_tbl *fdb_tbl)
262 {
263 writeb(0, &fdb_tbl->locks->host_lock);
264 }
265
icssm_prueth_sw_fdb_hash(const u8 * mac)266 static u8 icssm_prueth_sw_fdb_hash(const u8 *mac)
267 {
268 return (mac[0] ^ mac[1] ^ mac[2] ^ mac[3] ^ mac[4] ^ mac[5]);
269 }
270
271 static int
icssm_prueth_sw_fdb_search(struct fdb_mac_tbl_array __iomem * mac_tbl,struct fdb_index_tbl_entry __iomem * bucket_info,const u8 * mac)272 icssm_prueth_sw_fdb_search(struct fdb_mac_tbl_array __iomem *mac_tbl,
273 struct fdb_index_tbl_entry __iomem *bucket_info,
274 const u8 *mac)
275 {
276 unsigned int bucket_entries, mac_tbl_idx;
277 u8 tmp_mac[ETH_ALEN];
278 int i;
279
280 mac_tbl_idx = readw(&bucket_info->bucket_idx);
281 bucket_entries = readw(&bucket_info->bucket_entries);
282 for (i = 0; i < bucket_entries; i++, mac_tbl_idx++) {
283 memcpy_fromio(tmp_mac, mac_tbl->mac_tbl_entry[mac_tbl_idx].mac,
284 ETH_ALEN);
285 if (ether_addr_equal(mac, tmp_mac))
286 return mac_tbl_idx;
287 }
288
289 return -ENODATA;
290 }
291
icssm_prueth_sw_fdb_find_open_slot(struct fdb_tbl * fdb_tbl)292 static int icssm_prueth_sw_fdb_find_open_slot(struct fdb_tbl *fdb_tbl)
293 {
294 unsigned int i;
295 u8 flags;
296
297 for (i = 0; i < FDB_MAC_TBL_MAX_ENTRIES; i++) {
298 flags = readb(&fdb_tbl->mac_tbl_a->mac_tbl_entry[i].flags);
299 if (!(flags & FLAG_ACTIVE))
300 break;
301 }
302
303 return i;
304 }
305
306 static int
icssm_prueth_sw_find_fdb_insert(struct fdb_tbl * fdb,struct prueth * prueth,struct fdb_index_tbl_entry __iomem * bkt_info,const u8 * mac,const u8 port)307 icssm_prueth_sw_find_fdb_insert(struct fdb_tbl *fdb, struct prueth *prueth,
308 struct fdb_index_tbl_entry __iomem *bkt_info,
309 const u8 *mac, const u8 port)
310 {
311 struct fdb_mac_tbl_array __iomem *mac_tbl = fdb->mac_tbl_a;
312 unsigned int bucket_entries, mac_tbl_idx;
313 struct fdb_mac_tbl_entry __iomem *e;
314 u8 mac_from_hw[ETH_ALEN];
315 s8 cmp;
316 int i;
317
318 mac_tbl_idx = readw(&bkt_info->bucket_idx);
319 bucket_entries = readw(&bkt_info->bucket_entries);
320
321 for (i = 0; i < bucket_entries; i++, mac_tbl_idx++) {
322 e = &mac_tbl->mac_tbl_entry[mac_tbl_idx];
323 memcpy_fromio(mac_from_hw, e->mac, ETH_ALEN);
324 cmp = memcmp(mac, mac_from_hw, ETH_ALEN);
325 if (cmp < 0) {
326 return mac_tbl_idx;
327 } else if (cmp == 0) {
328 if (readb(&e->port) != port) {
329 /* MAC is already in FDB, only port is
330 * different. So just update the port.
331 * Note: total_entries and bucket_entries
332 * remain the same.
333 */
334 writeb(port, &e->port);
335 }
336
337 /* MAC and port are the same, touch the fdb */
338 writew(0, &e->age);
339 return -EEXIST;
340 }
341 }
342
343 return mac_tbl_idx;
344 }
345
346 static int
icssm_prueth_sw_fdb_empty_slot_left(struct fdb_mac_tbl_array __iomem * mac_tbl,unsigned int mac_tbl_idx)347 icssm_prueth_sw_fdb_empty_slot_left(struct fdb_mac_tbl_array __iomem *mac_tbl,
348 unsigned int mac_tbl_idx)
349 {
350 u8 flags;
351 int i;
352
353 for (i = mac_tbl_idx - 1; i > -1; i--) {
354 flags = readb(&mac_tbl->mac_tbl_entry[i].flags);
355 if (!(flags & FLAG_ACTIVE))
356 break;
357 }
358
359 return i;
360 }
361
362 static int
icssm_prueth_sw_fdb_empty_slot_right(struct fdb_mac_tbl_array __iomem * mac_tbl,unsigned int mac_tbl_idx)363 icssm_prueth_sw_fdb_empty_slot_right(struct fdb_mac_tbl_array __iomem *mac_tbl,
364 unsigned int mac_tbl_idx)
365 {
366 u8 flags;
367 int i;
368
369 for (i = mac_tbl_idx; i < FDB_MAC_TBL_MAX_ENTRIES; i++) {
370 flags = readb(&mac_tbl->mac_tbl_entry[i].flags);
371 if (!(flags & FLAG_ACTIVE))
372 return i;
373 }
374
375 return -1;
376 }
377
icssm_prueth_sw_fdb_move_range_left(struct prueth * prueth,u16 left,u16 right)378 static void icssm_prueth_sw_fdb_move_range_left(struct prueth *prueth,
379 u16 left, u16 right)
380 {
381 struct fdb_mac_tbl_entry entry;
382 u32 sz = 0;
383 u16 i;
384
385 sz = sizeof(struct fdb_mac_tbl_entry);
386 for (i = left; i < right; i++) {
387 memcpy_fromio(&entry, FDB_MAC_TBL_ENTRY(i + 1), sz);
388 memcpy_toio(FDB_MAC_TBL_ENTRY(i), &entry, sz);
389 }
390 }
391
icssm_prueth_sw_fdb_move_range_right(struct prueth * prueth,u16 left,u16 right)392 static void icssm_prueth_sw_fdb_move_range_right(struct prueth *prueth,
393 u16 left, u16 right)
394 {
395 struct fdb_mac_tbl_entry entry;
396 u32 sz = 0;
397 u16 i;
398
399 sz = sizeof(struct fdb_mac_tbl_entry);
400 for (i = right; i > left; i--) {
401 memcpy_fromio(&entry, FDB_MAC_TBL_ENTRY(i - 1), sz);
402 memcpy_toio(FDB_MAC_TBL_ENTRY(i), &entry, sz);
403 }
404 }
405
icssm_prueth_sw_fdb_update_index_tbl(struct prueth * prueth,u16 left,u16 right)406 static void icssm_prueth_sw_fdb_update_index_tbl(struct prueth *prueth,
407 u16 left, u16 right)
408 {
409 unsigned int hash, hash_prev;
410 u8 mac[ETH_ALEN];
411 unsigned int i;
412
413 /* To ensure we don't improperly update the
414 * bucket index, initialize with an invalid
415 * hash in case we are in leftmost slot
416 */
417 hash_prev = 0xff;
418
419 if (left > 0) {
420 memcpy_fromio(mac, FDB_MAC_TBL_ENTRY(left - 1)->mac, ETH_ALEN);
421 hash_prev = icssm_prueth_sw_fdb_hash(mac);
422 }
423
424 /* For each moved element, update the bucket index */
425 for (i = left; i <= right; i++) {
426 memcpy_fromio(mac, FDB_MAC_TBL_ENTRY(i)->mac, ETH_ALEN);
427 hash = icssm_prueth_sw_fdb_hash(mac);
428
429 /* Only need to update buckets once */
430 if (hash != hash_prev)
431 writew(i, &FDB_IDX_TBL_ENTRY(hash)->bucket_idx);
432
433 hash_prev = hash;
434 }
435 }
436
437 static struct fdb_mac_tbl_entry __iomem *
icssm_prueth_sw_find_free_mac(struct prueth * prueth,struct fdb_index_tbl_entry __iomem * bucket_info,u8 suggested_mac_tbl_idx,bool * update_indexes,const u8 * mac)438 icssm_prueth_sw_find_free_mac(struct prueth *prueth, struct fdb_index_tbl_entry
439 __iomem *bucket_info, u8 suggested_mac_tbl_idx,
440 bool *update_indexes, const u8 *mac)
441 {
442 s16 empty_slot_idx = 0, left = 0, right = 0;
443 unsigned int mti = suggested_mac_tbl_idx;
444 struct fdb_mac_tbl_array __iomem *mt;
445 struct fdb_tbl *fdb;
446 u8 flags;
447
448 fdb = prueth->fdb_tbl;
449 mt = fdb->mac_tbl_a;
450
451 flags = readb(&FDB_MAC_TBL_ENTRY(mti)->flags);
452 if (!(flags & FLAG_ACTIVE)) {
453 /* Claim the entry */
454 flags |= FLAG_ACTIVE;
455 writeb(flags, &FDB_MAC_TBL_ENTRY(mti)->flags);
456
457 return FDB_MAC_TBL_ENTRY(mti);
458 }
459
460 if (fdb->total_entries == FDB_MAC_TBL_MAX_ENTRIES)
461 return NULL;
462
463 empty_slot_idx = icssm_prueth_sw_fdb_empty_slot_left(mt, mti);
464 if (empty_slot_idx == -1) {
465 /* Nothing available on the left. But table isn't full
466 * so there must be space to the right,
467 */
468 empty_slot_idx = icssm_prueth_sw_fdb_empty_slot_right(mt, mti);
469
470 /* Shift right */
471 left = mti;
472 right = empty_slot_idx;
473 icssm_prueth_sw_fdb_move_range_right(prueth, left, right);
474
475 /* Claim the entry */
476 flags = readb(&FDB_MAC_TBL_ENTRY(mti)->flags);
477 flags |= FLAG_ACTIVE;
478 writeb(flags, &FDB_MAC_TBL_ENTRY(mti)->flags);
479
480 memcpy_toio(FDB_MAC_TBL_ENTRY(mti)->mac, mac, ETH_ALEN);
481
482 /* There is a chance we moved something in a
483 * different bucket, update index table
484 */
485 icssm_prueth_sw_fdb_update_index_tbl(prueth, left, right);
486
487 return FDB_MAC_TBL_ENTRY(mti);
488 }
489
490 if (empty_slot_idx == mti - 1) {
491 /* There is space immediately left of the open slot,
492 * which means the inserted MAC address
493 * must be the lowest-valued MAC address in bucket.
494 * Update bucket pointer accordingly.
495 */
496 writew(empty_slot_idx, &bucket_info->bucket_idx);
497
498 /* Claim the entry */
499 flags = readb(&FDB_MAC_TBL_ENTRY(empty_slot_idx)->flags);
500 flags |= FLAG_ACTIVE;
501 writeb(flags, &FDB_MAC_TBL_ENTRY(empty_slot_idx)->flags);
502
503 return FDB_MAC_TBL_ENTRY(empty_slot_idx);
504 }
505
506 /* There is empty space to the left, shift MAC table entries left */
507 left = empty_slot_idx;
508 right = mti - 1;
509 icssm_prueth_sw_fdb_move_range_left(prueth, left, right);
510
511 /* Claim the entry */
512 flags = readb(&FDB_MAC_TBL_ENTRY(mti - 1)->flags);
513 flags |= FLAG_ACTIVE;
514 writeb(flags, &FDB_MAC_TBL_ENTRY(mti - 1)->flags);
515
516 memcpy_toio(FDB_MAC_TBL_ENTRY(mti - 1)->mac, mac, ETH_ALEN);
517
518 /* There is a chance we moved something in a
519 * different bucket, update index table
520 */
521 icssm_prueth_sw_fdb_update_index_tbl(prueth, left, right);
522
523 return FDB_MAC_TBL_ENTRY(mti - 1);
524 }
525
icssm_prueth_sw_insert_fdb_entry(struct prueth_emac * emac,const u8 * mac,u8 is_static)526 static int icssm_prueth_sw_insert_fdb_entry(struct prueth_emac *emac,
527 const u8 *mac, u8 is_static)
528 {
529 struct fdb_index_tbl_entry __iomem *bucket_info;
530 struct fdb_mac_tbl_entry __iomem *mac_info;
531 struct prueth *prueth = emac->prueth;
532 unsigned int hash_val, mac_tbl_idx;
533 struct prueth_emac *other_emac;
534 enum prueth_port other_port_id;
535 int total_fdb_entries;
536 struct fdb_tbl *fdb;
537 u8 flags;
538 s16 ret;
539 int err;
540 u16 val;
541
542 fdb = prueth->fdb_tbl;
543 other_port_id = (emac->port_id == PRUETH_PORT_MII0) ?
544 PRUETH_PORT_MII1 : PRUETH_PORT_MII0;
545
546 other_emac = prueth->emac[other_port_id - 1];
547 if (!other_emac)
548 return -EINVAL;
549
550 err = icssm_prueth_sw_fdb_spin_lock(fdb);
551 if (err) {
552 dev_err(prueth->dev, "PRU lock timeout %d\n", err);
553 return err;
554 }
555
556 if (fdb->total_entries == FDB_MAC_TBL_MAX_ENTRIES) {
557 icssm_prueth_sw_fdb_spin_unlock(fdb);
558 return -ENOMEM;
559 }
560
561 if (ether_addr_equal(mac, emac->mac_addr) ||
562 (ether_addr_equal(mac, other_emac->mac_addr))) {
563 icssm_prueth_sw_fdb_spin_unlock(fdb);
564 /* Don't insert fdb of own mac addr */
565 return -EINVAL;
566 }
567
568 /* Get the bucket that the mac belongs to */
569 hash_val = icssm_prueth_sw_fdb_hash(mac);
570 bucket_info = FDB_IDX_TBL_ENTRY(hash_val);
571
572 if (!readw(&bucket_info->bucket_entries)) {
573 mac_tbl_idx = icssm_prueth_sw_fdb_find_open_slot(fdb);
574 writew(mac_tbl_idx, &bucket_info->bucket_idx);
575 }
576
577 ret = icssm_prueth_sw_find_fdb_insert(fdb, prueth, bucket_info, mac,
578 emac->port_id - 1);
579 if (ret < 0) {
580 icssm_prueth_sw_fdb_spin_unlock(fdb);
581 /* mac is already in fdb table */
582 return 0;
583 }
584
585 mac_tbl_idx = ret;
586
587 mac_info = icssm_prueth_sw_find_free_mac(prueth, bucket_info,
588 mac_tbl_idx, NULL,
589 mac);
590 if (!mac_info) {
591 /* Should not happen */
592 dev_warn(prueth->dev, "OUT of FDB MEM\n");
593 icssm_prueth_sw_fdb_spin_unlock(fdb);
594 return -ENOMEM;
595 }
596
597 memcpy_toio(mac_info->mac, mac, ETH_ALEN);
598 writew(0, &mac_info->age);
599 writeb(emac->port_id - 1, &mac_info->port);
600
601 flags = readb(&mac_info->flags);
602 if (is_static)
603 flags |= FLAG_IS_STATIC;
604 else
605 flags &= ~FLAG_IS_STATIC;
606
607 /* bit 1 - active */
608 flags |= FLAG_ACTIVE;
609 writeb(flags, &mac_info->flags);
610
611 val = readw(&bucket_info->bucket_entries);
612 val++;
613 writew(val, &bucket_info->bucket_entries);
614
615 fdb->total_entries++;
616
617 total_fdb_entries = fdb->total_entries;
618
619 icssm_prueth_sw_fdb_spin_unlock(fdb);
620
621 dev_dbg(prueth->dev, "added fdb: %pM port=%d total_entries=%u\n",
622 mac, emac->port_id, total_fdb_entries);
623
624 return 0;
625 }
626
icssm_prueth_sw_delete_fdb_entry(struct prueth_emac * emac,const u8 * mac,u8 is_static)627 static int icssm_prueth_sw_delete_fdb_entry(struct prueth_emac *emac,
628 const u8 *mac, u8 is_static)
629 {
630 struct fdb_index_tbl_entry __iomem *bucket_info;
631 struct fdb_mac_tbl_entry __iomem *mac_info;
632 struct fdb_mac_tbl_array __iomem *mt;
633 unsigned int hash_val, mac_tbl_idx;
634 unsigned int idx, entries;
635 struct prueth *prueth;
636 int total_fdb_entries;
637 s16 ret, left, right;
638 struct fdb_tbl *fdb;
639 u8 flags;
640 int err;
641 u16 val;
642
643 prueth = emac->prueth;
644 fdb = prueth->fdb_tbl;
645 mt = fdb->mac_tbl_a;
646
647 err = icssm_prueth_sw_fdb_spin_lock(fdb);
648 if (err) {
649 dev_err(prueth->dev, "PRU lock timeout %d\n", err);
650 return err;
651 }
652
653 if (fdb->total_entries == 0) {
654 icssm_prueth_sw_fdb_spin_unlock(fdb);
655 return 0;
656 }
657
658 /* Get the bucket that the mac belongs to */
659 hash_val = icssm_prueth_sw_fdb_hash(mac);
660 bucket_info = FDB_IDX_TBL_ENTRY(hash_val);
661
662 ret = icssm_prueth_sw_fdb_search(mt, bucket_info, mac);
663 if (ret < 0) {
664 icssm_prueth_sw_fdb_spin_unlock(fdb);
665 return ret;
666 }
667
668 mac_tbl_idx = ret;
669 mac_info = FDB_MAC_TBL_ENTRY(mac_tbl_idx);
670
671 /* Shift all elements in bucket to the left. No need to
672 * update index table since only shifting within bucket.
673 */
674 left = mac_tbl_idx;
675 idx = readw(&bucket_info->bucket_idx);
676 entries = readw(&bucket_info->bucket_entries);
677 right = idx + entries - 1;
678 icssm_prueth_sw_fdb_move_range_left(prueth, left, right);
679
680 /* Remove end of bucket from table */
681 mac_info = FDB_MAC_TBL_ENTRY(right);
682 flags = readb(&mac_info->flags);
683 /* active = 0 */
684 flags &= ~FLAG_ACTIVE;
685 writeb(flags, &mac_info->flags);
686 val = readw(&bucket_info->bucket_entries);
687 val--;
688 writew(val, &bucket_info->bucket_entries);
689 fdb->total_entries--;
690
691 total_fdb_entries = fdb->total_entries;
692
693 icssm_prueth_sw_fdb_spin_unlock(fdb);
694
695 dev_dbg(prueth->dev, "del fdb: %pM total_entries=%u\n",
696 mac, total_fdb_entries);
697
698 return 0;
699 }
700
icssm_prueth_sw_do_purge_fdb(struct prueth_emac * emac)701 int icssm_prueth_sw_do_purge_fdb(struct prueth_emac *emac)
702 {
703 struct fdb_index_tbl_entry __iomem *bucket_info;
704 struct prueth *prueth = emac->prueth;
705 u8 flags, mac[ETH_ALEN];
706 unsigned int hash_val;
707 struct fdb_tbl *fdb;
708 int ret, i;
709 u16 val;
710
711 fdb = prueth->fdb_tbl;
712
713 ret = icssm_prueth_sw_fdb_spin_lock(fdb);
714 if (ret) {
715 dev_err(prueth->dev, "PRU lock timeout %d\n", ret);
716 return ret;
717 }
718
719 if (fdb->total_entries == 0) {
720 icssm_prueth_sw_fdb_spin_unlock(fdb);
721 return 0;
722 }
723
724 for (i = 0; i < FDB_MAC_TBL_MAX_ENTRIES; i++) {
725 flags = readb(&fdb->mac_tbl_a->mac_tbl_entry[i].flags);
726 if ((flags & FLAG_ACTIVE) && !(flags & FLAG_IS_STATIC)) {
727 /* Get the bucket that the mac belongs to */
728 memcpy_fromio(mac, FDB_MAC_TBL_ENTRY(i)->mac,
729 ETH_ALEN);
730 hash_val = icssm_prueth_sw_fdb_hash(mac);
731 bucket_info = FDB_IDX_TBL_ENTRY(hash_val);
732 flags &= ~FLAG_ACTIVE;
733 writeb(flags,
734 &fdb->mac_tbl_a->mac_tbl_entry[i].flags);
735 val = readw(&bucket_info->bucket_entries);
736 val--;
737 writew(val, &bucket_info->bucket_entries);
738 fdb->total_entries--;
739 }
740 }
741
742 icssm_prueth_sw_fdb_spin_unlock(fdb);
743 return 0;
744 }
745
icssm_prueth_sw_init_fdb_table(struct prueth * prueth)746 int icssm_prueth_sw_init_fdb_table(struct prueth *prueth)
747 {
748 if (prueth->emac_configured)
749 return 0;
750
751 prueth->fdb_tbl = kmalloc_obj(*prueth->fdb_tbl);
752 if (!prueth->fdb_tbl)
753 return -ENOMEM;
754
755 icssm_prueth_sw_fdb_tbl_init(prueth);
756
757 return 0;
758 }
759
760 /**
761 * icssm_prueth_sw_fdb_add - insert fdb entry
762 *
763 * @emac: EMAC data structure
764 * @fdb: fdb info
765 *
766 */
icssm_prueth_sw_fdb_add(struct prueth_emac * emac,struct switchdev_notifier_fdb_info * fdb)767 void icssm_prueth_sw_fdb_add(struct prueth_emac *emac,
768 struct switchdev_notifier_fdb_info *fdb)
769 {
770 icssm_prueth_sw_insert_fdb_entry(emac, fdb->addr, 1);
771 }
772
773 /**
774 * icssm_prueth_sw_fdb_del - delete fdb entry
775 *
776 * @emac: EMAC data structure
777 * @fdb: fdb info
778 *
779 */
icssm_prueth_sw_fdb_del(struct prueth_emac * emac,struct switchdev_notifier_fdb_info * fdb)780 void icssm_prueth_sw_fdb_del(struct prueth_emac *emac,
781 struct switchdev_notifier_fdb_info *fdb)
782 {
783 icssm_prueth_sw_delete_fdb_entry(emac, fdb->addr, 1);
784 }
785
icssm_prueth_sw_fdb_work(struct work_struct * work)786 static void icssm_prueth_sw_fdb_work(struct work_struct *work)
787 {
788 struct icssm_prueth_sw_fdb_work *fdb_work =
789 container_of(work, struct icssm_prueth_sw_fdb_work, work);
790 struct prueth_emac *emac = fdb_work->emac;
791
792 rtnl_lock();
793
794 /* Interface is not up */
795 if (!emac->prueth->fdb_tbl)
796 goto free;
797
798 switch (fdb_work->event) {
799 case FDB_LEARN:
800 icssm_prueth_sw_insert_fdb_entry(emac, fdb_work->addr, 0);
801 break;
802 case FDB_PURGE:
803 icssm_prueth_sw_do_purge_fdb(emac);
804 break;
805 default:
806 break;
807 }
808
809 free:
810 rtnl_unlock();
811 netdev_put(emac->ndev, &fdb_work->ndev_tracker);
812 kfree(fdb_work);
813 }
814
icssm_prueth_sw_learn_fdb(struct prueth_emac * emac,u8 * src_mac)815 int icssm_prueth_sw_learn_fdb(struct prueth_emac *emac, u8 *src_mac)
816 {
817 struct icssm_prueth_sw_fdb_work *fdb_work;
818
819 fdb_work = kzalloc_obj(*fdb_work, GFP_ATOMIC);
820 if (WARN_ON(!fdb_work))
821 return -ENOMEM;
822
823 INIT_WORK(&fdb_work->work, icssm_prueth_sw_fdb_work);
824
825 fdb_work->event = FDB_LEARN;
826 fdb_work->emac = emac;
827 ether_addr_copy(fdb_work->addr, src_mac);
828
829 netdev_hold(emac->ndev, &fdb_work->ndev_tracker, GFP_ATOMIC);
830 queue_work(system_long_wq, &fdb_work->work);
831 return 0;
832 }
833
icssm_prueth_sw_purge_fdb(struct prueth_emac * emac)834 int icssm_prueth_sw_purge_fdb(struct prueth_emac *emac)
835 {
836 struct icssm_prueth_sw_fdb_work *fdb_work;
837
838 fdb_work = kzalloc_obj(*fdb_work, GFP_ATOMIC);
839 if (WARN_ON(!fdb_work))
840 return -ENOMEM;
841
842 INIT_WORK(&fdb_work->work, icssm_prueth_sw_fdb_work);
843
844 fdb_work->event = FDB_PURGE;
845 fdb_work->emac = emac;
846
847 netdev_hold(emac->ndev, &fdb_work->ndev_tracker, GFP_ATOMIC);
848 queue_work(system_long_wq, &fdb_work->work);
849 return 0;
850 }
851
icssm_prueth_sw_hostconfig(struct prueth * prueth)852 void icssm_prueth_sw_hostconfig(struct prueth *prueth)
853 {
854 void __iomem *dram1_base = prueth->mem[PRUETH_MEM_DRAM1].va;
855 void __iomem *dram;
856
857 /* queue information table */
858 dram = dram1_base + P0_Q1_RX_CONTEXT_OFFSET;
859 memcpy_toio(dram, sw_queue_infos[PRUETH_PORT_QUEUE_HOST],
860 sizeof(sw_queue_infos[PRUETH_PORT_QUEUE_HOST]));
861
862 /* buffer descriptor offset table*/
863 dram = dram1_base + QUEUE_DESCRIPTOR_OFFSET_ADDR;
864 writew(P0_Q1_BD_OFFSET, dram);
865 writew(P0_Q2_BD_OFFSET, dram + 2);
866 writew(P0_Q3_BD_OFFSET, dram + 4);
867 writew(P0_Q4_BD_OFFSET, dram + 6);
868
869 /* buffer offset table */
870 dram = dram1_base + QUEUE_OFFSET_ADDR;
871 writew(P0_Q1_BUFFER_OFFSET, dram);
872 writew(P0_Q2_BUFFER_OFFSET, dram + 2);
873 writew(P0_Q3_BUFFER_OFFSET, dram + 4);
874 writew(P0_Q4_BUFFER_OFFSET, dram + 6);
875
876 /* queue size lookup table */
877 dram = dram1_base + QUEUE_SIZE_ADDR;
878 writew(HOST_QUEUE_1_SIZE, dram);
879 writew(HOST_QUEUE_1_SIZE, dram + 2);
880 writew(HOST_QUEUE_1_SIZE, dram + 4);
881 writew(HOST_QUEUE_1_SIZE, dram + 6);
882
883 /* queue table */
884 dram = dram1_base + P0_QUEUE_DESC_OFFSET;
885 memcpy_toio(dram, queue_descs[PRUETH_PORT_QUEUE_HOST],
886 sizeof(queue_descs[PRUETH_PORT_QUEUE_HOST]));
887 }
888
icssm_prueth_sw_port_config(struct prueth * prueth,enum prueth_port port_id)889 static int icssm_prueth_sw_port_config(struct prueth *prueth,
890 enum prueth_port port_id)
891 {
892 unsigned int tx_context_ofs_addr, rx_context_ofs, queue_desc_ofs;
893 void __iomem *dram, *dram_base, *dram_mac;
894 struct prueth_emac *emac;
895 void __iomem *dram1_base;
896
897 dram1_base = prueth->mem[PRUETH_MEM_DRAM1].va;
898 emac = prueth->emac[port_id - 1];
899 switch (port_id) {
900 case PRUETH_PORT_MII0:
901 tx_context_ofs_addr = TX_CONTEXT_P1_Q1_OFFSET_ADDR;
902 rx_context_ofs = P1_Q1_RX_CONTEXT_OFFSET;
903 queue_desc_ofs = P1_QUEUE_DESC_OFFSET;
904
905 /* for switch PORT MII0 mac addr is in DRAM0. */
906 dram_mac = prueth->mem[PRUETH_MEM_DRAM0].va;
907 break;
908 case PRUETH_PORT_MII1:
909 tx_context_ofs_addr = TX_CONTEXT_P2_Q1_OFFSET_ADDR;
910 rx_context_ofs = P2_Q1_RX_CONTEXT_OFFSET;
911 queue_desc_ofs = P2_QUEUE_DESC_OFFSET;
912
913 /* for switch PORT MII1 mac addr is in DRAM1. */
914 dram_mac = prueth->mem[PRUETH_MEM_DRAM1].va;
915 break;
916 default:
917 netdev_err(emac->ndev, "invalid port\n");
918 return -EINVAL;
919 }
920
921 /* setup mac address */
922 memcpy_toio(dram_mac + PORT_MAC_ADDR, emac->mac_addr, 6);
923
924 /* Remaining switch port configs are in DRAM1 */
925 dram_base = prueth->mem[PRUETH_MEM_DRAM1].va;
926
927 /* queue information table */
928 memcpy_toio(dram_base + tx_context_ofs_addr,
929 sw_queue_infos[port_id],
930 sizeof(sw_queue_infos[port_id]));
931
932 memcpy_toio(dram_base + rx_context_ofs,
933 rx_queue_infos[port_id],
934 sizeof(rx_queue_infos[port_id]));
935
936 /* buffer descriptor offset table*/
937 dram = dram_base + QUEUE_DESCRIPTOR_OFFSET_ADDR +
938 (port_id * NUM_QUEUES * sizeof(u16));
939 writew(sw_queue_infos[port_id][PRUETH_QUEUE1].buffer_desc_offset, dram);
940 writew(sw_queue_infos[port_id][PRUETH_QUEUE2].buffer_desc_offset,
941 dram + 2);
942 writew(sw_queue_infos[port_id][PRUETH_QUEUE3].buffer_desc_offset,
943 dram + 4);
944 writew(sw_queue_infos[port_id][PRUETH_QUEUE4].buffer_desc_offset,
945 dram + 6);
946
947 /* buffer offset table */
948 dram = dram_base + QUEUE_OFFSET_ADDR +
949 port_id * NUM_QUEUES * sizeof(u16);
950 writew(sw_queue_infos[port_id][PRUETH_QUEUE1].buffer_offset, dram);
951 writew(sw_queue_infos[port_id][PRUETH_QUEUE2].buffer_offset,
952 dram + 2);
953 writew(sw_queue_infos[port_id][PRUETH_QUEUE3].buffer_offset,
954 dram + 4);
955 writew(sw_queue_infos[port_id][PRUETH_QUEUE4].buffer_offset,
956 dram + 6);
957
958 /* queue size lookup table */
959 dram = dram_base + QUEUE_SIZE_ADDR +
960 port_id * NUM_QUEUES * sizeof(u16);
961 writew(QUEUE_1_SIZE, dram);
962 writew(QUEUE_2_SIZE, dram + 2);
963 writew(QUEUE_3_SIZE, dram + 4);
964 writew(QUEUE_4_SIZE, dram + 6);
965
966 /* queue table */
967 memcpy_toio(dram_base + queue_desc_ofs,
968 &queue_descs[port_id][0],
969 4 * sizeof(queue_descs[port_id][0]));
970
971 emac->rx_queue_descs = dram1_base + P0_QUEUE_DESC_OFFSET;
972 emac->tx_queue_descs = dram1_base +
973 rx_queue_infos[port_id][PRUETH_QUEUE1].queue_desc_offset;
974
975 return 0;
976 }
977
icssm_prueth_sw_emac_config(struct prueth_emac * emac)978 int icssm_prueth_sw_emac_config(struct prueth_emac *emac)
979 {
980 struct prueth *prueth = emac->prueth;
981 u32 sharedramaddr, ocmcaddr;
982 int ret;
983
984 /* PRU needs local shared RAM address for C28 */
985 sharedramaddr = ICSS_LOCAL_SHARED_RAM;
986 /* PRU needs real global OCMC address for C30*/
987 ocmcaddr = (u32)prueth->mem[PRUETH_MEM_OCMC].pa;
988
989 if (prueth->emac_configured & BIT(emac->port_id))
990 return 0;
991
992 ret = icssm_prueth_sw_port_config(prueth, emac->port_id);
993 if (ret)
994 return ret;
995
996 if (!prueth->emac_configured) {
997 /* Set in constant table C28 of PRUn to ICSS Shared memory */
998 pru_rproc_set_ctable(prueth->pru0, PRU_C28, sharedramaddr);
999 pru_rproc_set_ctable(prueth->pru1, PRU_C28, sharedramaddr);
1000
1001 /* Set in constant table C30 of PRUn to OCMC memory */
1002 pru_rproc_set_ctable(prueth->pru0, PRU_C30, ocmcaddr);
1003 pru_rproc_set_ctable(prueth->pru1, PRU_C30, ocmcaddr);
1004 }
1005 return 0;
1006 }
1007
icssm_prueth_sw_boot_prus(struct prueth * prueth,struct net_device * ndev)1008 int icssm_prueth_sw_boot_prus(struct prueth *prueth, struct net_device *ndev)
1009 {
1010 const struct prueth_firmware *pru_firmwares;
1011 const char *fw_name, *fw_name1;
1012 int ret;
1013
1014 if (prueth->emac_configured)
1015 return 0;
1016
1017 pru_firmwares = &prueth->fw_data->fw_pru[PRUSS_PRU0];
1018 fw_name = pru_firmwares->fw_name[prueth->eth_type];
1019 pru_firmwares = &prueth->fw_data->fw_pru[PRUSS_PRU1];
1020 fw_name1 = pru_firmwares->fw_name[prueth->eth_type];
1021
1022 ret = rproc_set_firmware(prueth->pru0, fw_name);
1023 if (ret) {
1024 netdev_err(ndev, "failed to set PRU0 firmware %s: %d\n",
1025 fw_name, ret);
1026 return ret;
1027 }
1028 ret = rproc_boot(prueth->pru0);
1029 if (ret) {
1030 netdev_err(ndev, "failed to boot PRU0: %d\n", ret);
1031 return ret;
1032 }
1033
1034 ret = rproc_set_firmware(prueth->pru1, fw_name1);
1035 if (ret) {
1036 netdev_err(ndev, "failed to set PRU1 firmware %s: %d\n",
1037 fw_name1, ret);
1038 goto rproc0_shutdown;
1039 }
1040 ret = rproc_boot(prueth->pru1);
1041 if (ret) {
1042 netdev_err(ndev, "failed to boot PRU1: %d\n", ret);
1043 goto rproc0_shutdown;
1044 }
1045
1046 return 0;
1047
1048 rproc0_shutdown:
1049 rproc_shutdown(prueth->pru0);
1050 return ret;
1051 }
1052
icssm_prueth_sw_shutdown_prus(struct prueth_emac * emac,struct net_device * ndev)1053 int icssm_prueth_sw_shutdown_prus(struct prueth_emac *emac,
1054 struct net_device *ndev)
1055 {
1056 struct prueth *prueth = emac->prueth;
1057
1058 if (prueth->emac_configured)
1059 return 0;
1060
1061 rproc_shutdown(prueth->pru0);
1062 rproc_shutdown(prueth->pru1);
1063
1064 return 0;
1065 }
1066