]> Git Repo - J-linux.git/blob - drivers/net/ethernet/microchip/lan966x/lan966x_mac.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / net / ethernet / microchip / lan966x / lan966x_mac.c
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(macl, lan966x, ANA_MACLDATA);
70         lan_wr(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(&lan966x->mac_lock);
104         ret = __lan966x_mac_learn_locked(lan966x, pgid, cpu_copy, mac, vid, type);
105         spin_unlock(&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, 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, port, 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, port, 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(&lan966x->mac_lock);
169         ret = lan966x_mac_forget_locked(lan966x, mac, vid, type);
170         spin_unlock(&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, addr, vid, 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, addr, vid, 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(&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(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(mac, 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(type, dev, &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(&lan966x->mac_lock);
279         if (lan966x_mac_lookup(lan966x, addr, vid, ENTRYTYPE_NORMAL)) {
280                 spin_unlock(&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, addr, vid, port->chip_port);
291         if (mac_entry) {
292                 spin_unlock(&lan966x->mac_lock);
293                 goto mac_learn;
294         }
295
296         mac_entry = lan966x_mac_alloc_entry(port, addr, vid);
297         if (!mac_entry) {
298                 spin_unlock(&lan966x->mac_lock);
299                 return -ENOMEM;
300         }
301
302         list_add_tail(&mac_entry->list, &lan966x->mac_entries);
303         spin_unlock(&lan966x->mac_lock);
304
305         lan966x_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, addr, vid,
306                                    port->bond ?: port->dev);
307
308 mac_learn:
309         lan966x_mac_learn(lan966x, port->chip_port, addr, vid, 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(&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(addr, mac_entry->mac)) {
324                         lan966x_mac_forget_locked(lan966x, mac_entry->mac,
325                                                   mac_entry->vid,
326                                                   ENTRYTYPE_LOCKED);
327
328                         list_del(&mac_entry->list);
329                         kfree(mac_entry);
330                 }
331         }
332         spin_unlock(&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(&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_entry->mac,
348                                                   mac_entry->vid,
349                                                   ENTRYTYPE_LOCKED);
350
351                         lan966x_mac_learn_locked(lan966x, dst->chip_port,
352                                                  mac_entry->mac, mac_entry->vid,
353                                                  ENTRYTYPE_LOCKED);
354                         mac_entry->port_index = dst->chip_port;
355                 }
356         }
357         spin_unlock(&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(&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_entry->mac,
371                                                   mac_entry->vid,
372                                                   ENTRYTYPE_LOCKED);
373
374                         list_del(&mac_entry->list);
375                         kfree(mac_entry);
376                 }
377         }
378         spin_unlock(&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(&lan966x->mac_lock);
386         list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries,
387                                  list) {
388                 lan966x_mac_forget_locked(lan966x, mac_entry->mac,
389                                           mac_entry->vid, ENTRYTYPE_LOCKED);
390
391                 list_del(&mac_entry->list);
392                 kfree(mac_entry);
393         }
394         spin_unlock(&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(&mac_deleted_entries);
432
433         spin_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_entries[column],
449                                                       mac, &vid, &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(mac_entry->mac, 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(&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(&mac_entry->list, &mac_deleted_entries);
471                 }
472         }
473         spin_unlock(&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(SWITCHDEV_FDB_DEL_TO_BRIDGE,
481                                       mac_entry->mac, mac_entry->vid,
482                                       port->bond ?: port->dev);
483                 list_del(&mac_entry->list);
484                 kfree(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_entries[column],
503                                               mac, &vid, &dest_idx);
504                 if (WARN_ON(dest_idx >= lan966x->num_phys_ports))
505                         continue;
506
507                 spin_lock(&lan966x->mac_lock);
508                 mac_entry = lan966x_mac_find_entry(lan966x, mac, vid, dest_idx);
509                 if (mac_entry) {
510                         spin_unlock(&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(&lan966x->mac_lock);
518                         return;
519                 }
520
521                 mac_entry->row = row;
522                 list_add_tail(&mac_entry->list, &lan966x->mac_entries);
523                 spin_unlock(&lan966x->mac_lock);
524
525                 lan966x_mac_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
526                                       mac, vid, 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(&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(&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(&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, index, 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 }
This page took 0.060606 seconds and 4 git commands to generate.