scsi: ufs: core: Fix divide by zero in ufshcd_map_queues()
[platform/kernel/linux-starfive.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 };
26
27 struct lan966x_mac_raw_entry {
28         u32 mach;
29         u32 macl;
30         u32 maca;
31         bool processed;
32 };
33
34 static int lan966x_mac_get_status(struct lan966x *lan966x)
35 {
36         return lan_rd(lan966x, ANA_MACACCESS);
37 }
38
39 static int lan966x_mac_wait_for_completion(struct lan966x *lan966x)
40 {
41         u32 val;
42
43         return readx_poll_timeout(lan966x_mac_get_status,
44                 lan966x, val,
45                 (ANA_MACACCESS_MAC_TABLE_CMD_GET(val)) ==
46                 MACACCESS_CMD_IDLE,
47                 TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
48 }
49
50 static void lan966x_mac_select(struct lan966x *lan966x,
51                                const unsigned char mac[ETH_ALEN],
52                                unsigned int vid)
53 {
54         u32 macl = 0, mach = 0;
55
56         /* Set the MAC address to handle and the vlan associated in a format
57          * understood by the hardware.
58          */
59         mach |= vid    << 16;
60         mach |= mac[0] << 8;
61         mach |= mac[1] << 0;
62         macl |= mac[2] << 24;
63         macl |= mac[3] << 16;
64         macl |= mac[4] << 8;
65         macl |= mac[5] << 0;
66
67         lan_wr(macl, lan966x, ANA_MACLDATA);
68         lan_wr(mach, lan966x, ANA_MACHDATA);
69 }
70
71 static int __lan966x_mac_learn(struct lan966x *lan966x, int pgid,
72                                bool cpu_copy,
73                                const unsigned char mac[ETH_ALEN],
74                                unsigned int vid,
75                                enum macaccess_entry_type type)
76 {
77         lan966x_mac_select(lan966x, mac, vid);
78
79         /* Issue a write command */
80         lan_wr(ANA_MACACCESS_VALID_SET(1) |
81                ANA_MACACCESS_CHANGE2SW_SET(0) |
82                ANA_MACACCESS_MAC_CPU_COPY_SET(cpu_copy) |
83                ANA_MACACCESS_DEST_IDX_SET(pgid) |
84                ANA_MACACCESS_ENTRYTYPE_SET(type) |
85                ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_LEARN),
86                lan966x, ANA_MACACCESS);
87
88         return lan966x_mac_wait_for_completion(lan966x);
89 }
90
91 /* The mask of the front ports is encoded inside the mac parameter via a call
92  * to lan966x_mdb_encode_mac().
93  */
94 int lan966x_mac_ip_learn(struct lan966x *lan966x,
95                          bool cpu_copy,
96                          const unsigned char mac[ETH_ALEN],
97                          unsigned int vid,
98                          enum macaccess_entry_type type)
99 {
100         WARN_ON(type != ENTRYTYPE_MACV4 && type != ENTRYTYPE_MACV6);
101
102         return __lan966x_mac_learn(lan966x, 0, cpu_copy, mac, vid, type);
103 }
104
105 int lan966x_mac_learn(struct lan966x *lan966x, int port,
106                       const unsigned char mac[ETH_ALEN],
107                       unsigned int vid,
108                       enum macaccess_entry_type type)
109 {
110         WARN_ON(type != ENTRYTYPE_NORMAL && type != ENTRYTYPE_LOCKED);
111
112         return __lan966x_mac_learn(lan966x, port, false, mac, vid, type);
113 }
114
115 int lan966x_mac_forget(struct lan966x *lan966x,
116                        const unsigned char mac[ETH_ALEN],
117                        unsigned int vid,
118                        enum macaccess_entry_type type)
119 {
120         lan966x_mac_select(lan966x, mac, vid);
121
122         /* Issue a forget command */
123         lan_wr(ANA_MACACCESS_ENTRYTYPE_SET(type) |
124                ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_FORGET),
125                lan966x, ANA_MACACCESS);
126
127         return lan966x_mac_wait_for_completion(lan966x);
128 }
129
130 int lan966x_mac_cpu_learn(struct lan966x *lan966x, const char *addr, u16 vid)
131 {
132         return lan966x_mac_learn(lan966x, PGID_CPU, addr, vid, ENTRYTYPE_LOCKED);
133 }
134
135 int lan966x_mac_cpu_forget(struct lan966x *lan966x, const char *addr, u16 vid)
136 {
137         return lan966x_mac_forget(lan966x, addr, vid, ENTRYTYPE_LOCKED);
138 }
139
140 void lan966x_mac_set_ageing(struct lan966x *lan966x,
141                             u32 ageing)
142 {
143         lan_rmw(ANA_AUTOAGE_AGE_PERIOD_SET(ageing / 2),
144                 ANA_AUTOAGE_AGE_PERIOD,
145                 lan966x, ANA_AUTOAGE);
146 }
147
148 void lan966x_mac_init(struct lan966x *lan966x)
149 {
150         /* Clear the MAC table */
151         lan_wr(MACACCESS_CMD_INIT, lan966x, ANA_MACACCESS);
152         lan966x_mac_wait_for_completion(lan966x);
153
154         spin_lock_init(&lan966x->mac_lock);
155         INIT_LIST_HEAD(&lan966x->mac_entries);
156 }
157
158 static struct lan966x_mac_entry *lan966x_mac_alloc_entry(const unsigned char *mac,
159                                                          u16 vid, u16 port_index)
160 {
161         struct lan966x_mac_entry *mac_entry;
162
163         mac_entry = kzalloc(sizeof(*mac_entry), GFP_KERNEL);
164         if (!mac_entry)
165                 return NULL;
166
167         memcpy(mac_entry->mac, mac, ETH_ALEN);
168         mac_entry->vid = vid;
169         mac_entry->port_index = port_index;
170         mac_entry->row = LAN966X_MAC_INVALID_ROW;
171         return mac_entry;
172 }
173
174 static struct lan966x_mac_entry *lan966x_mac_find_entry(struct lan966x *lan966x,
175                                                         const unsigned char *mac,
176                                                         u16 vid, u16 port_index)
177 {
178         struct lan966x_mac_entry *res = NULL;
179         struct lan966x_mac_entry *mac_entry;
180
181         spin_lock(&lan966x->mac_lock);
182         list_for_each_entry(mac_entry, &lan966x->mac_entries, list) {
183                 if (mac_entry->vid == vid &&
184                     ether_addr_equal(mac, mac_entry->mac) &&
185                     mac_entry->port_index == port_index) {
186                         res = mac_entry;
187                         break;
188                 }
189         }
190         spin_unlock(&lan966x->mac_lock);
191
192         return res;
193 }
194
195 static int lan966x_mac_lookup(struct lan966x *lan966x,
196                               const unsigned char mac[ETH_ALEN],
197                               unsigned int vid, enum macaccess_entry_type type)
198 {
199         int ret;
200
201         lan966x_mac_select(lan966x, mac, vid);
202
203         /* Issue a read command */
204         lan_wr(ANA_MACACCESS_ENTRYTYPE_SET(type) |
205                ANA_MACACCESS_VALID_SET(1) |
206                ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_READ),
207                lan966x, ANA_MACACCESS);
208
209         ret = lan966x_mac_wait_for_completion(lan966x);
210         if (ret)
211                 return ret;
212
213         return ANA_MACACCESS_VALID_GET(lan_rd(lan966x, ANA_MACACCESS));
214 }
215
216 static void lan966x_fdb_call_notifiers(enum switchdev_notifier_type type,
217                                        const char *mac, u16 vid,
218                                        struct net_device *dev)
219 {
220         struct switchdev_notifier_fdb_info info = { 0 };
221
222         info.addr = mac;
223         info.vid = vid;
224         info.offloaded = true;
225         call_switchdev_notifiers(type, dev, &info.info, NULL);
226 }
227
228 int lan966x_mac_add_entry(struct lan966x *lan966x, struct lan966x_port *port,
229                           const unsigned char *addr, u16 vid)
230 {
231         struct lan966x_mac_entry *mac_entry;
232
233         if (lan966x_mac_lookup(lan966x, addr, vid, ENTRYTYPE_NORMAL))
234                 return 0;
235
236         /* In case the entry already exists, don't add it again to SW,
237          * just update HW, but we need to look in the actual HW because
238          * it is possible for an entry to be learn by HW and before we
239          * get the interrupt the frame will reach CPU and the CPU will
240          * add the entry but without the extern_learn flag.
241          */
242         mac_entry = lan966x_mac_find_entry(lan966x, addr, vid, port->chip_port);
243         if (mac_entry)
244                 return lan966x_mac_learn(lan966x, port->chip_port,
245                                          addr, vid, ENTRYTYPE_LOCKED);
246
247         mac_entry = lan966x_mac_alloc_entry(addr, vid, port->chip_port);
248         if (!mac_entry)
249                 return -ENOMEM;
250
251         spin_lock(&lan966x->mac_lock);
252         list_add_tail(&mac_entry->list, &lan966x->mac_entries);
253         spin_unlock(&lan966x->mac_lock);
254
255         lan966x_mac_learn(lan966x, port->chip_port, addr, vid, ENTRYTYPE_LOCKED);
256         lan966x_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, addr, vid, port->dev);
257
258         return 0;
259 }
260
261 int lan966x_mac_del_entry(struct lan966x *lan966x, const unsigned char *addr,
262                           u16 vid)
263 {
264         struct lan966x_mac_entry *mac_entry, *tmp;
265
266         spin_lock(&lan966x->mac_lock);
267         list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries,
268                                  list) {
269                 if (mac_entry->vid == vid &&
270                     ether_addr_equal(addr, mac_entry->mac)) {
271                         lan966x_mac_forget(lan966x, mac_entry->mac, mac_entry->vid,
272                                            ENTRYTYPE_LOCKED);
273
274                         list_del(&mac_entry->list);
275                         kfree(mac_entry);
276                 }
277         }
278         spin_unlock(&lan966x->mac_lock);
279
280         return 0;
281 }
282
283 void lan966x_mac_purge_entries(struct lan966x *lan966x)
284 {
285         struct lan966x_mac_entry *mac_entry, *tmp;
286
287         spin_lock(&lan966x->mac_lock);
288         list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries,
289                                  list) {
290                 lan966x_mac_forget(lan966x, mac_entry->mac, mac_entry->vid,
291                                    ENTRYTYPE_LOCKED);
292
293                 list_del(&mac_entry->list);
294                 kfree(mac_entry);
295         }
296         spin_unlock(&lan966x->mac_lock);
297 }
298
299 static void lan966x_mac_notifiers(enum switchdev_notifier_type type,
300                                   unsigned char *mac, u32 vid,
301                                   struct net_device *dev)
302 {
303         rtnl_lock();
304         lan966x_fdb_call_notifiers(type, mac, vid, dev);
305         rtnl_unlock();
306 }
307
308 static void lan966x_mac_process_raw_entry(struct lan966x_mac_raw_entry *raw_entry,
309                                           u8 *mac, u16 *vid, u32 *dest_idx)
310 {
311         mac[0] = (raw_entry->mach >> 8)  & 0xff;
312         mac[1] = (raw_entry->mach >> 0)  & 0xff;
313         mac[2] = (raw_entry->macl >> 24) & 0xff;
314         mac[3] = (raw_entry->macl >> 16) & 0xff;
315         mac[4] = (raw_entry->macl >> 8)  & 0xff;
316         mac[5] = (raw_entry->macl >> 0)  & 0xff;
317
318         *vid = (raw_entry->mach >> 16) & 0xfff;
319         *dest_idx = ANA_MACACCESS_DEST_IDX_GET(raw_entry->maca);
320 }
321
322 static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row,
323                                     struct lan966x_mac_raw_entry *raw_entries)
324 {
325         struct lan966x_mac_entry *mac_entry, *tmp;
326         unsigned char mac[ETH_ALEN] __aligned(2);
327         u32 dest_idx;
328         u32 column;
329         u16 vid;
330
331         spin_lock(&lan966x->mac_lock);
332         list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries, list) {
333                 bool found = false;
334
335                 if (mac_entry->row != row)
336                         continue;
337
338                 for (column = 0; column < LAN966X_MAC_COLUMNS; ++column) {
339                         /* All the valid entries are at the start of the row,
340                          * so when get one invalid entry it can just skip the
341                          * rest of the columns
342                          */
343                         if (!ANA_MACACCESS_VALID_GET(raw_entries[column].maca))
344                                 break;
345
346                         lan966x_mac_process_raw_entry(&raw_entries[column],
347                                                       mac, &vid, &dest_idx);
348                         WARN_ON(dest_idx > lan966x->num_phys_ports);
349
350                         /* If the entry in SW is found, then there is nothing
351                          * to do
352                          */
353                         if (mac_entry->vid == vid &&
354                             ether_addr_equal(mac_entry->mac, mac) &&
355                             mac_entry->port_index == dest_idx) {
356                                 raw_entries[column].processed = true;
357                                 found = true;
358                                 break;
359                         }
360                 }
361
362                 if (!found) {
363                         /* Notify the bridge that the entry doesn't exist
364                          * anymore in the HW and remove the entry from the SW
365                          * list
366                          */
367                         lan966x_mac_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
368                                               mac_entry->mac, mac_entry->vid,
369                                               lan966x->ports[mac_entry->port_index]->dev);
370
371                         list_del(&mac_entry->list);
372                         kfree(mac_entry);
373                 }
374         }
375         spin_unlock(&lan966x->mac_lock);
376
377         /* Now go to the list of columns and see if any entry was not in the SW
378          * list, then that means that the entry is new so it needs to notify the
379          * bridge.
380          */
381         for (column = 0; column < LAN966X_MAC_COLUMNS; ++column) {
382                 /* All the valid entries are at the start of the row, so when
383                  * get one invalid entry it can just skip the rest of the columns
384                  */
385                 if (!ANA_MACACCESS_VALID_GET(raw_entries[column].maca))
386                         break;
387
388                 /* If the entry already exists then don't do anything */
389                 if (raw_entries[column].processed)
390                         continue;
391
392                 lan966x_mac_process_raw_entry(&raw_entries[column],
393                                               mac, &vid, &dest_idx);
394                 WARN_ON(dest_idx > lan966x->num_phys_ports);
395
396                 mac_entry = lan966x_mac_alloc_entry(mac, vid, dest_idx);
397                 if (!mac_entry)
398                         return;
399
400                 mac_entry->row = row;
401
402                 spin_lock(&lan966x->mac_lock);
403                 list_add_tail(&mac_entry->list, &lan966x->mac_entries);
404                 spin_unlock(&lan966x->mac_lock);
405
406                 lan966x_mac_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
407                                       mac, vid, lan966x->ports[dest_idx]->dev);
408         }
409 }
410
411 irqreturn_t lan966x_mac_irq_handler(struct lan966x *lan966x)
412 {
413         struct lan966x_mac_raw_entry entry[LAN966X_MAC_COLUMNS] = { 0 };
414         u32 index, column;
415         bool stop = true;
416         u32 val;
417
418         /* Start the scan from 0, 0 */
419         lan_wr(ANA_MACTINDX_M_INDEX_SET(0) |
420                ANA_MACTINDX_BUCKET_SET(0),
421                lan966x, ANA_MACTINDX);
422
423         while (1) {
424                 lan_rmw(ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_SYNC_GET_NEXT),
425                         ANA_MACACCESS_MAC_TABLE_CMD,
426                         lan966x, ANA_MACACCESS);
427                 lan966x_mac_wait_for_completion(lan966x);
428
429                 val = lan_rd(lan966x, ANA_MACTINDX);
430                 index = ANA_MACTINDX_M_INDEX_GET(val);
431                 column = ANA_MACTINDX_BUCKET_GET(val);
432
433                 /* The SYNC-GET-NEXT returns all the entries(4) in a row in
434                  * which is suffered a change. By change it means that new entry
435                  * was added or an entry was removed because of ageing.
436                  * It would return all the columns for that row. And after that
437                  * it would return the next row The stop conditions of the
438                  * SYNC-GET-NEXT is when it reaches 'directly' to row 0
439                  * column 3. So if SYNC-GET-NEXT returns row 0 and column 0
440                  * then it is required to continue to read more even if it
441                  * reaches row 0 and column 3.
442                  */
443                 if (index == 0 && column == 0)
444                         stop = false;
445
446                 if (column == LAN966X_MAC_COLUMNS - 1 &&
447                     index == 0 && stop)
448                         break;
449
450                 entry[column].mach = lan_rd(lan966x, ANA_MACHDATA);
451                 entry[column].macl = lan_rd(lan966x, ANA_MACLDATA);
452                 entry[column].maca = lan_rd(lan966x, ANA_MACACCESS);
453
454                 /* Once all the columns are read process them */
455                 if (column == LAN966X_MAC_COLUMNS - 1) {
456                         lan966x_mac_irq_process(lan966x, index, entry);
457                         /* A row was processed so it is safe to assume that the
458                          * next row/column can be the stop condition
459                          */
460                         stop = true;
461                 }
462         }
463
464         lan_rmw(ANA_ANAINTR_INTR_SET(0),
465                 ANA_ANAINTR_INTR,
466                 lan966x, ANA_ANAINTR);
467
468         return IRQ_HANDLED;
469 }