xref: /linux/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c (revision bf4afc53b77aeaa48b5409da5c8da6bb4eff7f43)
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 
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 
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 
239 static u8 icssm_pru_lock_done(struct fdb_tbl *fdb_tbl)
240 {
241 	return readb(&fdb_tbl->locks->pru_locks);
242 }
243 
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 
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 
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
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 
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
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
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
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 
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 
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 
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 *
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 
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 
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 
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 
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  */
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  */
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 
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 
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 
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 
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 
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 
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 
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 
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