1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | |
3 | #include <net/switchdev.h> |
4 | #include "lan966x_main.h" |
5 | |
6 | #define LAN966X_MAC_COLUMNS 4 |
7 | #define MACACCESS_CMD_IDLE 0 |
8 | #define MACACCESS_CMD_LEARN 1 |
9 | #define MACACCESS_CMD_FORGET 2 |
10 | #define MACACCESS_CMD_AGE 3 |
11 | #define MACACCESS_CMD_GET_NEXT 4 |
12 | #define MACACCESS_CMD_INIT 5 |
13 | #define MACACCESS_CMD_READ 6 |
14 | #define MACACCESS_CMD_WRITE 7 |
15 | #define MACACCESS_CMD_SYNC_GET_NEXT 8 |
16 | |
17 | #define LAN966X_MAC_INVALID_ROW -1 |
18 | |
19 | struct lan966x_mac_entry { |
20 | struct list_head list; |
21 | unsigned char mac[ETH_ALEN] __aligned(2); |
22 | u16 vid; |
23 | u16 port_index; |
24 | int row; |
25 | bool lag; |
26 | }; |
27 | |
28 | struct lan966x_mac_raw_entry { |
29 | u32 mach; |
30 | u32 macl; |
31 | u32 maca; |
32 | bool processed; |
33 | }; |
34 | |
35 | static int lan966x_mac_get_status(struct lan966x *lan966x) |
36 | { |
37 | return lan_rd(lan966x, ANA_MACACCESS); |
38 | } |
39 | |
40 | static int lan966x_mac_wait_for_completion(struct lan966x *lan966x) |
41 | { |
42 | u32 val; |
43 | |
44 | return readx_poll_timeout_atomic(lan966x_mac_get_status, |
45 | lan966x, val, |
46 | (ANA_MACACCESS_MAC_TABLE_CMD_GET(val)) == |
47 | MACACCESS_CMD_IDLE, |
48 | TABLE_UPDATE_SLEEP_US, |
49 | TABLE_UPDATE_TIMEOUT_US); |
50 | } |
51 | |
52 | static void lan966x_mac_select(struct lan966x *lan966x, |
53 | const unsigned char mac[ETH_ALEN], |
54 | unsigned int vid) |
55 | { |
56 | u32 macl = 0, mach = 0; |
57 | |
58 | /* Set the MAC address to handle and the vlan associated in a format |
59 | * understood by the hardware. |
60 | */ |
61 | mach |= vid << 16; |
62 | mach |= mac[0] << 8; |
63 | mach |= mac[1] << 0; |
64 | macl |= mac[2] << 24; |
65 | macl |= mac[3] << 16; |
66 | macl |= mac[4] << 8; |
67 | macl |= mac[5] << 0; |
68 | |
69 | lan_wr(val: macl, lan966x, ANA_MACLDATA); |
70 | lan_wr(val: mach, lan966x, ANA_MACHDATA); |
71 | } |
72 | |
73 | static int __lan966x_mac_learn_locked(struct lan966x *lan966x, int pgid, |
74 | bool cpu_copy, |
75 | const unsigned char mac[ETH_ALEN], |
76 | unsigned int vid, |
77 | enum macaccess_entry_type type) |
78 | { |
79 | lockdep_assert_held(&lan966x->mac_lock); |
80 | |
81 | lan966x_mac_select(lan966x, mac, vid); |
82 | |
83 | /* Issue a write command */ |
84 | lan_wr(ANA_MACACCESS_VALID_SET(1) | |
85 | ANA_MACACCESS_CHANGE2SW_SET(0) | |
86 | ANA_MACACCESS_MAC_CPU_COPY_SET(cpu_copy) | |
87 | ANA_MACACCESS_DEST_IDX_SET(pgid) | |
88 | ANA_MACACCESS_ENTRYTYPE_SET(type) | |
89 | ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_LEARN), |
90 | lan966x, ANA_MACACCESS); |
91 | |
92 | return lan966x_mac_wait_for_completion(lan966x); |
93 | } |
94 | |
95 | static int __lan966x_mac_learn(struct lan966x *lan966x, int pgid, |
96 | bool cpu_copy, |
97 | const unsigned char mac[ETH_ALEN], |
98 | unsigned int vid, |
99 | enum macaccess_entry_type type) |
100 | { |
101 | int ret; |
102 | |
103 | spin_lock(lock: &lan966x->mac_lock); |
104 | ret = __lan966x_mac_learn_locked(lan966x, pgid, cpu_copy, mac, vid, type); |
105 | spin_unlock(lock: &lan966x->mac_lock); |
106 | |
107 | return ret; |
108 | } |
109 | |
110 | /* The mask of the front ports is encoded inside the mac parameter via a call |
111 | * to lan966x_mdb_encode_mac(). |
112 | */ |
113 | int lan966x_mac_ip_learn(struct lan966x *lan966x, |
114 | bool cpu_copy, |
115 | const unsigned char mac[ETH_ALEN], |
116 | unsigned int vid, |
117 | enum macaccess_entry_type type) |
118 | { |
119 | WARN_ON(type != ENTRYTYPE_MACV4 && type != ENTRYTYPE_MACV6); |
120 | |
121 | return __lan966x_mac_learn(lan966x, pgid: 0, cpu_copy, mac, vid, type); |
122 | } |
123 | |
124 | int lan966x_mac_learn(struct lan966x *lan966x, int port, |
125 | const unsigned char mac[ETH_ALEN], |
126 | unsigned int vid, |
127 | enum macaccess_entry_type type) |
128 | { |
129 | WARN_ON(type != ENTRYTYPE_NORMAL && type != ENTRYTYPE_LOCKED); |
130 | |
131 | return __lan966x_mac_learn(lan966x, pgid: port, cpu_copy: false, mac, vid, type); |
132 | } |
133 | |
134 | static int lan966x_mac_learn_locked(struct lan966x *lan966x, int port, |
135 | const unsigned char mac[ETH_ALEN], |
136 | unsigned int vid, |
137 | enum macaccess_entry_type type) |
138 | { |
139 | WARN_ON(type != ENTRYTYPE_NORMAL && type != ENTRYTYPE_LOCKED); |
140 | |
141 | return __lan966x_mac_learn_locked(lan966x, pgid: port, cpu_copy: false, mac, vid, type); |
142 | } |
143 | |
144 | static int lan966x_mac_forget_locked(struct lan966x *lan966x, |
145 | const unsigned char mac[ETH_ALEN], |
146 | unsigned int vid, |
147 | enum macaccess_entry_type type) |
148 | { |
149 | lockdep_assert_held(&lan966x->mac_lock); |
150 | |
151 | lan966x_mac_select(lan966x, mac, vid); |
152 | |
153 | /* Issue a forget command */ |
154 | lan_wr(ANA_MACACCESS_ENTRYTYPE_SET(type) | |
155 | ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_FORGET), |
156 | lan966x, ANA_MACACCESS); |
157 | |
158 | return lan966x_mac_wait_for_completion(lan966x); |
159 | } |
160 | |
161 | int lan966x_mac_forget(struct lan966x *lan966x, |
162 | const unsigned char mac[ETH_ALEN], |
163 | unsigned int vid, |
164 | enum macaccess_entry_type type) |
165 | { |
166 | int ret; |
167 | |
168 | spin_lock(lock: &lan966x->mac_lock); |
169 | ret = lan966x_mac_forget_locked(lan966x, mac, vid, type); |
170 | spin_unlock(lock: &lan966x->mac_lock); |
171 | |
172 | return ret; |
173 | } |
174 | |
175 | int lan966x_mac_cpu_learn(struct lan966x *lan966x, const char *addr, u16 vid) |
176 | { |
177 | return lan966x_mac_learn(lan966x, PGID_CPU, mac: addr, vid, type: ENTRYTYPE_LOCKED); |
178 | } |
179 | |
180 | int lan966x_mac_cpu_forget(struct lan966x *lan966x, const char *addr, u16 vid) |
181 | { |
182 | return lan966x_mac_forget(lan966x, mac: addr, vid, type: ENTRYTYPE_LOCKED); |
183 | } |
184 | |
185 | void lan966x_mac_set_ageing(struct lan966x *lan966x, |
186 | u32 ageing) |
187 | { |
188 | lan_rmw(ANA_AUTOAGE_AGE_PERIOD_SET(ageing / 2), |
189 | ANA_AUTOAGE_AGE_PERIOD, |
190 | lan966x, ANA_AUTOAGE); |
191 | } |
192 | |
193 | void lan966x_mac_init(struct lan966x *lan966x) |
194 | { |
195 | /* Clear the MAC table */ |
196 | lan_wr(MACACCESS_CMD_INIT, lan966x, ANA_MACACCESS); |
197 | lan966x_mac_wait_for_completion(lan966x); |
198 | |
199 | spin_lock_init(&lan966x->mac_lock); |
200 | INIT_LIST_HEAD(list: &lan966x->mac_entries); |
201 | } |
202 | |
203 | static struct lan966x_mac_entry *lan966x_mac_alloc_entry(struct lan966x_port *port, |
204 | const unsigned char *mac, |
205 | u16 vid) |
206 | { |
207 | struct lan966x_mac_entry *mac_entry; |
208 | |
209 | mac_entry = kzalloc(size: sizeof(*mac_entry), GFP_ATOMIC); |
210 | if (!mac_entry) |
211 | return NULL; |
212 | |
213 | memcpy(mac_entry->mac, mac, ETH_ALEN); |
214 | mac_entry->vid = vid; |
215 | mac_entry->port_index = port->chip_port; |
216 | mac_entry->row = LAN966X_MAC_INVALID_ROW; |
217 | mac_entry->lag = port->bond ? true : false; |
218 | return mac_entry; |
219 | } |
220 | |
221 | static struct lan966x_mac_entry *lan966x_mac_find_entry(struct lan966x *lan966x, |
222 | const unsigned char *mac, |
223 | u16 vid, u16 port_index) |
224 | { |
225 | struct lan966x_mac_entry *res = NULL; |
226 | struct lan966x_mac_entry *mac_entry; |
227 | |
228 | list_for_each_entry(mac_entry, &lan966x->mac_entries, list) { |
229 | if (mac_entry->vid == vid && |
230 | ether_addr_equal(addr1: mac, addr2: mac_entry->mac) && |
231 | mac_entry->port_index == port_index) { |
232 | res = mac_entry; |
233 | break; |
234 | } |
235 | } |
236 | |
237 | return res; |
238 | } |
239 | |
240 | static int lan966x_mac_lookup(struct lan966x *lan966x, |
241 | const unsigned char mac[ETH_ALEN], |
242 | unsigned int vid, enum macaccess_entry_type type) |
243 | { |
244 | int ret; |
245 | |
246 | lan966x_mac_select(lan966x, mac, vid); |
247 | |
248 | /* Issue a read command */ |
249 | lan_wr(ANA_MACACCESS_ENTRYTYPE_SET(type) | |
250 | ANA_MACACCESS_VALID_SET(1) | |
251 | ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_READ), |
252 | lan966x, ANA_MACACCESS); |
253 | |
254 | ret = lan966x_mac_wait_for_completion(lan966x); |
255 | if (ret) |
256 | return ret; |
257 | |
258 | return ANA_MACACCESS_VALID_GET(lan_rd(lan966x, ANA_MACACCESS)); |
259 | } |
260 | |
261 | static void lan966x_fdb_call_notifiers(enum switchdev_notifier_type type, |
262 | const char *mac, u16 vid, |
263 | struct net_device *dev) |
264 | { |
265 | struct switchdev_notifier_fdb_info info = { 0 }; |
266 | |
267 | info.addr = mac; |
268 | info.vid = vid; |
269 | info.offloaded = true; |
270 | call_switchdev_notifiers(val: type, dev, info: &info.info, NULL); |
271 | } |
272 | |
273 | int lan966x_mac_add_entry(struct lan966x *lan966x, struct lan966x_port *port, |
274 | const unsigned char *addr, u16 vid) |
275 | { |
276 | struct lan966x_mac_entry *mac_entry; |
277 | |
278 | spin_lock(lock: &lan966x->mac_lock); |
279 | if (lan966x_mac_lookup(lan966x, mac: addr, vid, type: ENTRYTYPE_NORMAL)) { |
280 | spin_unlock(lock: &lan966x->mac_lock); |
281 | return 0; |
282 | } |
283 | |
284 | /* In case the entry already exists, don't add it again to SW, |
285 | * just update HW, but we need to look in the actual HW because |
286 | * it is possible for an entry to be learn by HW and before we |
287 | * get the interrupt the frame will reach CPU and the CPU will |
288 | * add the entry but without the extern_learn flag. |
289 | */ |
290 | mac_entry = lan966x_mac_find_entry(lan966x, mac: addr, vid, port_index: port->chip_port); |
291 | if (mac_entry) { |
292 | spin_unlock(lock: &lan966x->mac_lock); |
293 | goto mac_learn; |
294 | } |
295 | |
296 | mac_entry = lan966x_mac_alloc_entry(port, mac: addr, vid); |
297 | if (!mac_entry) { |
298 | spin_unlock(lock: &lan966x->mac_lock); |
299 | return -ENOMEM; |
300 | } |
301 | |
302 | list_add_tail(new: &mac_entry->list, head: &lan966x->mac_entries); |
303 | spin_unlock(lock: &lan966x->mac_lock); |
304 | |
305 | lan966x_fdb_call_notifiers(type: SWITCHDEV_FDB_OFFLOADED, mac: addr, vid, |
306 | dev: port->bond ?: port->dev); |
307 | |
308 | mac_learn: |
309 | lan966x_mac_learn(lan966x, port: port->chip_port, mac: addr, vid, type: ENTRYTYPE_LOCKED); |
310 | |
311 | return 0; |
312 | } |
313 | |
314 | int lan966x_mac_del_entry(struct lan966x *lan966x, const unsigned char *addr, |
315 | u16 vid) |
316 | { |
317 | struct lan966x_mac_entry *mac_entry, *tmp; |
318 | |
319 | spin_lock(lock: &lan966x->mac_lock); |
320 | list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries, |
321 | list) { |
322 | if (mac_entry->vid == vid && |
323 | ether_addr_equal(addr1: addr, addr2: mac_entry->mac)) { |
324 | lan966x_mac_forget_locked(lan966x, mac: mac_entry->mac, |
325 | vid: mac_entry->vid, |
326 | type: ENTRYTYPE_LOCKED); |
327 | |
328 | list_del(entry: &mac_entry->list); |
329 | kfree(objp: mac_entry); |
330 | } |
331 | } |
332 | spin_unlock(lock: &lan966x->mac_lock); |
333 | |
334 | return 0; |
335 | } |
336 | |
337 | void lan966x_mac_lag_replace_port_entry(struct lan966x *lan966x, |
338 | struct lan966x_port *src, |
339 | struct lan966x_port *dst) |
340 | { |
341 | struct lan966x_mac_entry *mac_entry; |
342 | |
343 | spin_lock(lock: &lan966x->mac_lock); |
344 | list_for_each_entry(mac_entry, &lan966x->mac_entries, list) { |
345 | if (mac_entry->port_index == src->chip_port && |
346 | mac_entry->lag) { |
347 | lan966x_mac_forget_locked(lan966x, mac: mac_entry->mac, |
348 | vid: mac_entry->vid, |
349 | type: ENTRYTYPE_LOCKED); |
350 | |
351 | lan966x_mac_learn_locked(lan966x, port: dst->chip_port, |
352 | mac: mac_entry->mac, vid: mac_entry->vid, |
353 | type: ENTRYTYPE_LOCKED); |
354 | mac_entry->port_index = dst->chip_port; |
355 | } |
356 | } |
357 | spin_unlock(lock: &lan966x->mac_lock); |
358 | } |
359 | |
360 | void lan966x_mac_lag_remove_port_entry(struct lan966x *lan966x, |
361 | struct lan966x_port *src) |
362 | { |
363 | struct lan966x_mac_entry *mac_entry, *tmp; |
364 | |
365 | spin_lock(lock: &lan966x->mac_lock); |
366 | list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries, |
367 | list) { |
368 | if (mac_entry->port_index == src->chip_port && |
369 | mac_entry->lag) { |
370 | lan966x_mac_forget_locked(lan966x, mac: mac_entry->mac, |
371 | vid: mac_entry->vid, |
372 | type: ENTRYTYPE_LOCKED); |
373 | |
374 | list_del(entry: &mac_entry->list); |
375 | kfree(objp: mac_entry); |
376 | } |
377 | } |
378 | spin_unlock(lock: &lan966x->mac_lock); |
379 | } |
380 | |
381 | void lan966x_mac_purge_entries(struct lan966x *lan966x) |
382 | { |
383 | struct lan966x_mac_entry *mac_entry, *tmp; |
384 | |
385 | spin_lock(lock: &lan966x->mac_lock); |
386 | list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries, |
387 | list) { |
388 | lan966x_mac_forget_locked(lan966x, mac: mac_entry->mac, |
389 | vid: mac_entry->vid, type: ENTRYTYPE_LOCKED); |
390 | |
391 | list_del(entry: &mac_entry->list); |
392 | kfree(objp: mac_entry); |
393 | } |
394 | spin_unlock(lock: &lan966x->mac_lock); |
395 | } |
396 | |
397 | static void lan966x_mac_notifiers(enum switchdev_notifier_type type, |
398 | unsigned char *mac, u32 vid, |
399 | struct net_device *dev) |
400 | { |
401 | rtnl_lock(); |
402 | lan966x_fdb_call_notifiers(type, mac, vid, dev); |
403 | rtnl_unlock(); |
404 | } |
405 | |
406 | static void lan966x_mac_process_raw_entry(struct lan966x_mac_raw_entry *raw_entry, |
407 | u8 *mac, u16 *vid, u32 *dest_idx) |
408 | { |
409 | mac[0] = (raw_entry->mach >> 8) & 0xff; |
410 | mac[1] = (raw_entry->mach >> 0) & 0xff; |
411 | mac[2] = (raw_entry->macl >> 24) & 0xff; |
412 | mac[3] = (raw_entry->macl >> 16) & 0xff; |
413 | mac[4] = (raw_entry->macl >> 8) & 0xff; |
414 | mac[5] = (raw_entry->macl >> 0) & 0xff; |
415 | |
416 | *vid = (raw_entry->mach >> 16) & 0xfff; |
417 | *dest_idx = ANA_MACACCESS_DEST_IDX_GET(raw_entry->maca); |
418 | } |
419 | |
420 | static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row, |
421 | struct lan966x_mac_raw_entry *raw_entries) |
422 | { |
423 | struct lan966x_mac_entry *mac_entry, *tmp; |
424 | unsigned char mac[ETH_ALEN] __aligned(2); |
425 | struct list_head mac_deleted_entries; |
426 | struct lan966x_port *port; |
427 | u32 dest_idx; |
428 | u32 column; |
429 | u16 vid; |
430 | |
431 | INIT_LIST_HEAD(list: &mac_deleted_entries); |
432 | |
433 | spin_lock(lock: &lan966x->mac_lock); |
434 | list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries, list) { |
435 | bool found = false; |
436 | |
437 | if (mac_entry->row != row) |
438 | continue; |
439 | |
440 | for (column = 0; column < LAN966X_MAC_COLUMNS; ++column) { |
441 | /* All the valid entries are at the start of the row, |
442 | * so when get one invalid entry it can just skip the |
443 | * rest of the columns |
444 | */ |
445 | if (!ANA_MACACCESS_VALID_GET(raw_entries[column].maca)) |
446 | break; |
447 | |
448 | lan966x_mac_process_raw_entry(raw_entry: &raw_entries[column], |
449 | mac, vid: &vid, dest_idx: &dest_idx); |
450 | if (WARN_ON(dest_idx >= lan966x->num_phys_ports)) |
451 | continue; |
452 | |
453 | /* If the entry in SW is found, then there is nothing |
454 | * to do |
455 | */ |
456 | if (mac_entry->vid == vid && |
457 | ether_addr_equal(addr1: mac_entry->mac, addr2: mac) && |
458 | mac_entry->port_index == dest_idx) { |
459 | raw_entries[column].processed = true; |
460 | found = true; |
461 | break; |
462 | } |
463 | } |
464 | |
465 | if (!found) { |
466 | list_del(entry: &mac_entry->list); |
467 | /* Move the entry from SW list to a tmp list such that |
468 | * it would be deleted later |
469 | */ |
470 | list_add_tail(new: &mac_entry->list, head: &mac_deleted_entries); |
471 | } |
472 | } |
473 | spin_unlock(lock: &lan966x->mac_lock); |
474 | |
475 | list_for_each_entry_safe(mac_entry, tmp, &mac_deleted_entries, list) { |
476 | /* Notify the bridge that the entry doesn't exist |
477 | * anymore in the HW |
478 | */ |
479 | port = lan966x->ports[mac_entry->port_index]; |
480 | lan966x_mac_notifiers(type: SWITCHDEV_FDB_DEL_TO_BRIDGE, |
481 | mac: mac_entry->mac, vid: mac_entry->vid, |
482 | dev: port->bond ?: port->dev); |
483 | list_del(entry: &mac_entry->list); |
484 | kfree(objp: mac_entry); |
485 | } |
486 | |
487 | /* Now go to the list of columns and see if any entry was not in the SW |
488 | * list, then that means that the entry is new so it needs to notify the |
489 | * bridge. |
490 | */ |
491 | for (column = 0; column < LAN966X_MAC_COLUMNS; ++column) { |
492 | /* All the valid entries are at the start of the row, so when |
493 | * get one invalid entry it can just skip the rest of the columns |
494 | */ |
495 | if (!ANA_MACACCESS_VALID_GET(raw_entries[column].maca)) |
496 | break; |
497 | |
498 | /* If the entry already exists then don't do anything */ |
499 | if (raw_entries[column].processed) |
500 | continue; |
501 | |
502 | lan966x_mac_process_raw_entry(raw_entry: &raw_entries[column], |
503 | mac, vid: &vid, dest_idx: &dest_idx); |
504 | if (WARN_ON(dest_idx >= lan966x->num_phys_ports)) |
505 | continue; |
506 | |
507 | spin_lock(lock: &lan966x->mac_lock); |
508 | mac_entry = lan966x_mac_find_entry(lan966x, mac, vid, port_index: dest_idx); |
509 | if (mac_entry) { |
510 | spin_unlock(lock: &lan966x->mac_lock); |
511 | continue; |
512 | } |
513 | |
514 | port = lan966x->ports[dest_idx]; |
515 | mac_entry = lan966x_mac_alloc_entry(port, mac, vid); |
516 | if (!mac_entry) { |
517 | spin_unlock(lock: &lan966x->mac_lock); |
518 | return; |
519 | } |
520 | |
521 | mac_entry->row = row; |
522 | list_add_tail(new: &mac_entry->list, head: &lan966x->mac_entries); |
523 | spin_unlock(lock: &lan966x->mac_lock); |
524 | |
525 | lan966x_mac_notifiers(type: SWITCHDEV_FDB_ADD_TO_BRIDGE, |
526 | mac, vid, dev: port->bond ?: port->dev); |
527 | } |
528 | } |
529 | |
530 | irqreturn_t lan966x_mac_irq_handler(struct lan966x *lan966x) |
531 | { |
532 | struct lan966x_mac_raw_entry entry[LAN966X_MAC_COLUMNS] = { 0 }; |
533 | u32 index, column; |
534 | bool stop = true; |
535 | u32 val; |
536 | |
537 | /* Start the scan from 0, 0 */ |
538 | lan_wr(ANA_MACTINDX_M_INDEX_SET(0) | |
539 | ANA_MACTINDX_BUCKET_SET(0), |
540 | lan966x, ANA_MACTINDX); |
541 | |
542 | while (1) { |
543 | spin_lock(lock: &lan966x->mac_lock); |
544 | lan_rmw(ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_SYNC_GET_NEXT), |
545 | ANA_MACACCESS_MAC_TABLE_CMD, |
546 | lan966x, ANA_MACACCESS); |
547 | lan966x_mac_wait_for_completion(lan966x); |
548 | |
549 | val = lan_rd(lan966x, ANA_MACTINDX); |
550 | index = ANA_MACTINDX_M_INDEX_GET(val); |
551 | column = ANA_MACTINDX_BUCKET_GET(val); |
552 | |
553 | /* The SYNC-GET-NEXT returns all the entries(4) in a row in |
554 | * which is suffered a change. By change it means that new entry |
555 | * was added or an entry was removed because of ageing. |
556 | * It would return all the columns for that row. And after that |
557 | * it would return the next row The stop conditions of the |
558 | * SYNC-GET-NEXT is when it reaches 'directly' to row 0 |
559 | * column 3. So if SYNC-GET-NEXT returns row 0 and column 0 |
560 | * then it is required to continue to read more even if it |
561 | * reaches row 0 and column 3. |
562 | */ |
563 | if (index == 0 && column == 0) |
564 | stop = false; |
565 | |
566 | if (column == LAN966X_MAC_COLUMNS - 1 && |
567 | index == 0 && stop) { |
568 | spin_unlock(lock: &lan966x->mac_lock); |
569 | break; |
570 | } |
571 | |
572 | entry[column].mach = lan_rd(lan966x, ANA_MACHDATA); |
573 | entry[column].macl = lan_rd(lan966x, ANA_MACLDATA); |
574 | entry[column].maca = lan_rd(lan966x, ANA_MACACCESS); |
575 | spin_unlock(lock: &lan966x->mac_lock); |
576 | |
577 | /* Once all the columns are read process them */ |
578 | if (column == LAN966X_MAC_COLUMNS - 1) { |
579 | lan966x_mac_irq_process(lan966x, row: index, raw_entries: entry); |
580 | /* A row was processed so it is safe to assume that the |
581 | * next row/column can be the stop condition |
582 | */ |
583 | stop = true; |
584 | } |
585 | } |
586 | |
587 | lan_rmw(ANA_ANAINTR_INTR_SET(0), |
588 | ANA_ANAINTR_INTR, |
589 | lan966x, ANA_ANAINTR); |
590 | |
591 | return IRQ_HANDLED; |
592 | } |
593 | |