0001
0002
0003 #include <net/switchdev.h>
0004 #include "lan966x_main.h"
0005
0006 #define LAN966X_MAC_COLUMNS 4
0007 #define MACACCESS_CMD_IDLE 0
0008 #define MACACCESS_CMD_LEARN 1
0009 #define MACACCESS_CMD_FORGET 2
0010 #define MACACCESS_CMD_AGE 3
0011 #define MACACCESS_CMD_GET_NEXT 4
0012 #define MACACCESS_CMD_INIT 5
0013 #define MACACCESS_CMD_READ 6
0014 #define MACACCESS_CMD_WRITE 7
0015 #define MACACCESS_CMD_SYNC_GET_NEXT 8
0016
0017 #define LAN966X_MAC_INVALID_ROW -1
0018
0019 struct lan966x_mac_entry {
0020 struct list_head list;
0021 unsigned char mac[ETH_ALEN] __aligned(2);
0022 u16 vid;
0023 u16 port_index;
0024 int row;
0025 };
0026
0027 struct lan966x_mac_raw_entry {
0028 u32 mach;
0029 u32 macl;
0030 u32 maca;
0031 bool processed;
0032 };
0033
0034 static int lan966x_mac_get_status(struct lan966x *lan966x)
0035 {
0036 return lan_rd(lan966x, ANA_MACACCESS);
0037 }
0038
0039 static int lan966x_mac_wait_for_completion(struct lan966x *lan966x)
0040 {
0041 u32 val;
0042
0043 return readx_poll_timeout_atomic(lan966x_mac_get_status,
0044 lan966x, val,
0045 (ANA_MACACCESS_MAC_TABLE_CMD_GET(val)) ==
0046 MACACCESS_CMD_IDLE,
0047 TABLE_UPDATE_SLEEP_US,
0048 TABLE_UPDATE_TIMEOUT_US);
0049 }
0050
0051 static void lan966x_mac_select(struct lan966x *lan966x,
0052 const unsigned char mac[ETH_ALEN],
0053 unsigned int vid)
0054 {
0055 u32 macl = 0, mach = 0;
0056
0057
0058
0059
0060 mach |= vid << 16;
0061 mach |= mac[0] << 8;
0062 mach |= mac[1] << 0;
0063 macl |= mac[2] << 24;
0064 macl |= mac[3] << 16;
0065 macl |= mac[4] << 8;
0066 macl |= mac[5] << 0;
0067
0068 lan_wr(macl, lan966x, ANA_MACLDATA);
0069 lan_wr(mach, lan966x, ANA_MACHDATA);
0070 }
0071
0072 static int __lan966x_mac_learn(struct lan966x *lan966x, int pgid,
0073 bool cpu_copy,
0074 const unsigned char mac[ETH_ALEN],
0075 unsigned int vid,
0076 enum macaccess_entry_type type)
0077 {
0078 int ret;
0079
0080 spin_lock(&lan966x->mac_lock);
0081 lan966x_mac_select(lan966x, mac, vid);
0082
0083
0084 lan_wr(ANA_MACACCESS_VALID_SET(1) |
0085 ANA_MACACCESS_CHANGE2SW_SET(0) |
0086 ANA_MACACCESS_MAC_CPU_COPY_SET(cpu_copy) |
0087 ANA_MACACCESS_DEST_IDX_SET(pgid) |
0088 ANA_MACACCESS_ENTRYTYPE_SET(type) |
0089 ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_LEARN),
0090 lan966x, ANA_MACACCESS);
0091
0092 ret = lan966x_mac_wait_for_completion(lan966x);
0093 spin_unlock(&lan966x->mac_lock);
0094
0095 return ret;
0096 }
0097
0098
0099
0100
0101 int lan966x_mac_ip_learn(struct lan966x *lan966x,
0102 bool cpu_copy,
0103 const unsigned char mac[ETH_ALEN],
0104 unsigned int vid,
0105 enum macaccess_entry_type type)
0106 {
0107 WARN_ON(type != ENTRYTYPE_MACV4 && type != ENTRYTYPE_MACV6);
0108
0109 return __lan966x_mac_learn(lan966x, 0, cpu_copy, mac, vid, type);
0110 }
0111
0112 int lan966x_mac_learn(struct lan966x *lan966x, int port,
0113 const unsigned char mac[ETH_ALEN],
0114 unsigned int vid,
0115 enum macaccess_entry_type type)
0116 {
0117 WARN_ON(type != ENTRYTYPE_NORMAL && type != ENTRYTYPE_LOCKED);
0118
0119 return __lan966x_mac_learn(lan966x, port, false, mac, vid, type);
0120 }
0121
0122 static int lan966x_mac_forget_locked(struct lan966x *lan966x,
0123 const unsigned char mac[ETH_ALEN],
0124 unsigned int vid,
0125 enum macaccess_entry_type type)
0126 {
0127 lockdep_assert_held(&lan966x->mac_lock);
0128
0129 lan966x_mac_select(lan966x, mac, vid);
0130
0131
0132 lan_wr(ANA_MACACCESS_ENTRYTYPE_SET(type) |
0133 ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_FORGET),
0134 lan966x, ANA_MACACCESS);
0135
0136 return lan966x_mac_wait_for_completion(lan966x);
0137 }
0138
0139 int lan966x_mac_forget(struct lan966x *lan966x,
0140 const unsigned char mac[ETH_ALEN],
0141 unsigned int vid,
0142 enum macaccess_entry_type type)
0143 {
0144 int ret;
0145
0146 spin_lock(&lan966x->mac_lock);
0147 ret = lan966x_mac_forget_locked(lan966x, mac, vid, type);
0148 spin_unlock(&lan966x->mac_lock);
0149
0150 return ret;
0151 }
0152
0153 int lan966x_mac_cpu_learn(struct lan966x *lan966x, const char *addr, u16 vid)
0154 {
0155 return lan966x_mac_learn(lan966x, PGID_CPU, addr, vid, ENTRYTYPE_LOCKED);
0156 }
0157
0158 int lan966x_mac_cpu_forget(struct lan966x *lan966x, const char *addr, u16 vid)
0159 {
0160 return lan966x_mac_forget(lan966x, addr, vid, ENTRYTYPE_LOCKED);
0161 }
0162
0163 void lan966x_mac_set_ageing(struct lan966x *lan966x,
0164 u32 ageing)
0165 {
0166 lan_rmw(ANA_AUTOAGE_AGE_PERIOD_SET(ageing / 2),
0167 ANA_AUTOAGE_AGE_PERIOD,
0168 lan966x, ANA_AUTOAGE);
0169 }
0170
0171 void lan966x_mac_init(struct lan966x *lan966x)
0172 {
0173
0174 lan_wr(MACACCESS_CMD_INIT, lan966x, ANA_MACACCESS);
0175 lan966x_mac_wait_for_completion(lan966x);
0176
0177 spin_lock_init(&lan966x->mac_lock);
0178 INIT_LIST_HEAD(&lan966x->mac_entries);
0179 }
0180
0181 static struct lan966x_mac_entry *lan966x_mac_alloc_entry(const unsigned char *mac,
0182 u16 vid, u16 port_index)
0183 {
0184 struct lan966x_mac_entry *mac_entry;
0185
0186 mac_entry = kzalloc(sizeof(*mac_entry), GFP_ATOMIC);
0187 if (!mac_entry)
0188 return NULL;
0189
0190 memcpy(mac_entry->mac, mac, ETH_ALEN);
0191 mac_entry->vid = vid;
0192 mac_entry->port_index = port_index;
0193 mac_entry->row = LAN966X_MAC_INVALID_ROW;
0194 return mac_entry;
0195 }
0196
0197 static struct lan966x_mac_entry *lan966x_mac_find_entry(struct lan966x *lan966x,
0198 const unsigned char *mac,
0199 u16 vid, u16 port_index)
0200 {
0201 struct lan966x_mac_entry *res = NULL;
0202 struct lan966x_mac_entry *mac_entry;
0203
0204 list_for_each_entry(mac_entry, &lan966x->mac_entries, list) {
0205 if (mac_entry->vid == vid &&
0206 ether_addr_equal(mac, mac_entry->mac) &&
0207 mac_entry->port_index == port_index) {
0208 res = mac_entry;
0209 break;
0210 }
0211 }
0212
0213 return res;
0214 }
0215
0216 static int lan966x_mac_lookup(struct lan966x *lan966x,
0217 const unsigned char mac[ETH_ALEN],
0218 unsigned int vid, enum macaccess_entry_type type)
0219 {
0220 int ret;
0221
0222 lan966x_mac_select(lan966x, mac, vid);
0223
0224
0225 lan_wr(ANA_MACACCESS_ENTRYTYPE_SET(type) |
0226 ANA_MACACCESS_VALID_SET(1) |
0227 ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_READ),
0228 lan966x, ANA_MACACCESS);
0229
0230 ret = lan966x_mac_wait_for_completion(lan966x);
0231 if (ret)
0232 return ret;
0233
0234 return ANA_MACACCESS_VALID_GET(lan_rd(lan966x, ANA_MACACCESS));
0235 }
0236
0237 static void lan966x_fdb_call_notifiers(enum switchdev_notifier_type type,
0238 const char *mac, u16 vid,
0239 struct net_device *dev)
0240 {
0241 struct switchdev_notifier_fdb_info info = { 0 };
0242
0243 info.addr = mac;
0244 info.vid = vid;
0245 info.offloaded = true;
0246 call_switchdev_notifiers(type, dev, &info.info, NULL);
0247 }
0248
0249 int lan966x_mac_add_entry(struct lan966x *lan966x, struct lan966x_port *port,
0250 const unsigned char *addr, u16 vid)
0251 {
0252 struct lan966x_mac_entry *mac_entry;
0253
0254 spin_lock(&lan966x->mac_lock);
0255 if (lan966x_mac_lookup(lan966x, addr, vid, ENTRYTYPE_NORMAL)) {
0256 spin_unlock(&lan966x->mac_lock);
0257 return 0;
0258 }
0259
0260
0261
0262
0263
0264
0265
0266 mac_entry = lan966x_mac_find_entry(lan966x, addr, vid, port->chip_port);
0267 if (mac_entry) {
0268 spin_unlock(&lan966x->mac_lock);
0269 goto mac_learn;
0270 }
0271
0272 mac_entry = lan966x_mac_alloc_entry(addr, vid, port->chip_port);
0273 if (!mac_entry) {
0274 spin_unlock(&lan966x->mac_lock);
0275 return -ENOMEM;
0276 }
0277
0278 list_add_tail(&mac_entry->list, &lan966x->mac_entries);
0279 spin_unlock(&lan966x->mac_lock);
0280
0281 lan966x_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, addr, vid, port->dev);
0282
0283 mac_learn:
0284 lan966x_mac_learn(lan966x, port->chip_port, addr, vid, ENTRYTYPE_LOCKED);
0285
0286 return 0;
0287 }
0288
0289 int lan966x_mac_del_entry(struct lan966x *lan966x, const unsigned char *addr,
0290 u16 vid)
0291 {
0292 struct lan966x_mac_entry *mac_entry, *tmp;
0293
0294 spin_lock(&lan966x->mac_lock);
0295 list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries,
0296 list) {
0297 if (mac_entry->vid == vid &&
0298 ether_addr_equal(addr, mac_entry->mac)) {
0299 lan966x_mac_forget_locked(lan966x, mac_entry->mac,
0300 mac_entry->vid,
0301 ENTRYTYPE_LOCKED);
0302
0303 list_del(&mac_entry->list);
0304 kfree(mac_entry);
0305 }
0306 }
0307 spin_unlock(&lan966x->mac_lock);
0308
0309 return 0;
0310 }
0311
0312 void lan966x_mac_purge_entries(struct lan966x *lan966x)
0313 {
0314 struct lan966x_mac_entry *mac_entry, *tmp;
0315
0316 spin_lock(&lan966x->mac_lock);
0317 list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries,
0318 list) {
0319 lan966x_mac_forget_locked(lan966x, mac_entry->mac,
0320 mac_entry->vid, ENTRYTYPE_LOCKED);
0321
0322 list_del(&mac_entry->list);
0323 kfree(mac_entry);
0324 }
0325 spin_unlock(&lan966x->mac_lock);
0326 }
0327
0328 static void lan966x_mac_notifiers(enum switchdev_notifier_type type,
0329 unsigned char *mac, u32 vid,
0330 struct net_device *dev)
0331 {
0332 rtnl_lock();
0333 lan966x_fdb_call_notifiers(type, mac, vid, dev);
0334 rtnl_unlock();
0335 }
0336
0337 static void lan966x_mac_process_raw_entry(struct lan966x_mac_raw_entry *raw_entry,
0338 u8 *mac, u16 *vid, u32 *dest_idx)
0339 {
0340 mac[0] = (raw_entry->mach >> 8) & 0xff;
0341 mac[1] = (raw_entry->mach >> 0) & 0xff;
0342 mac[2] = (raw_entry->macl >> 24) & 0xff;
0343 mac[3] = (raw_entry->macl >> 16) & 0xff;
0344 mac[4] = (raw_entry->macl >> 8) & 0xff;
0345 mac[5] = (raw_entry->macl >> 0) & 0xff;
0346
0347 *vid = (raw_entry->mach >> 16) & 0xfff;
0348 *dest_idx = ANA_MACACCESS_DEST_IDX_GET(raw_entry->maca);
0349 }
0350
0351 static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row,
0352 struct lan966x_mac_raw_entry *raw_entries)
0353 {
0354 struct lan966x_mac_entry *mac_entry, *tmp;
0355 unsigned char mac[ETH_ALEN] __aligned(2);
0356 struct list_head mac_deleted_entries;
0357 u32 dest_idx;
0358 u32 column;
0359 u16 vid;
0360
0361 INIT_LIST_HEAD(&mac_deleted_entries);
0362
0363 spin_lock(&lan966x->mac_lock);
0364 list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries, list) {
0365 bool found = false;
0366
0367 if (mac_entry->row != row)
0368 continue;
0369
0370 for (column = 0; column < LAN966X_MAC_COLUMNS; ++column) {
0371
0372
0373
0374
0375 if (!ANA_MACACCESS_VALID_GET(raw_entries[column].maca))
0376 break;
0377
0378 lan966x_mac_process_raw_entry(&raw_entries[column],
0379 mac, &vid, &dest_idx);
0380 if (WARN_ON(dest_idx >= lan966x->num_phys_ports))
0381 continue;
0382
0383
0384
0385
0386 if (mac_entry->vid == vid &&
0387 ether_addr_equal(mac_entry->mac, mac) &&
0388 mac_entry->port_index == dest_idx) {
0389 raw_entries[column].processed = true;
0390 found = true;
0391 break;
0392 }
0393 }
0394
0395 if (!found) {
0396 list_del(&mac_entry->list);
0397
0398
0399
0400 list_add_tail(&mac_entry->list, &mac_deleted_entries);
0401 }
0402 }
0403 spin_unlock(&lan966x->mac_lock);
0404
0405 list_for_each_entry_safe(mac_entry, tmp, &mac_deleted_entries, list) {
0406
0407
0408
0409 lan966x_mac_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
0410 mac_entry->mac, mac_entry->vid,
0411 lan966x->ports[mac_entry->port_index]->dev);
0412 list_del(&mac_entry->list);
0413 kfree(mac_entry);
0414 }
0415
0416
0417
0418
0419
0420 for (column = 0; column < LAN966X_MAC_COLUMNS; ++column) {
0421
0422
0423
0424 if (!ANA_MACACCESS_VALID_GET(raw_entries[column].maca))
0425 break;
0426
0427
0428 if (raw_entries[column].processed)
0429 continue;
0430
0431 lan966x_mac_process_raw_entry(&raw_entries[column],
0432 mac, &vid, &dest_idx);
0433 if (WARN_ON(dest_idx >= lan966x->num_phys_ports))
0434 continue;
0435
0436 spin_lock(&lan966x->mac_lock);
0437 mac_entry = lan966x_mac_find_entry(lan966x, mac, vid, dest_idx);
0438 if (mac_entry) {
0439 spin_unlock(&lan966x->mac_lock);
0440 continue;
0441 }
0442
0443 mac_entry = lan966x_mac_alloc_entry(mac, vid, dest_idx);
0444 if (!mac_entry) {
0445 spin_unlock(&lan966x->mac_lock);
0446 return;
0447 }
0448
0449 mac_entry->row = row;
0450 list_add_tail(&mac_entry->list, &lan966x->mac_entries);
0451 spin_unlock(&lan966x->mac_lock);
0452
0453 lan966x_mac_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
0454 mac, vid, lan966x->ports[dest_idx]->dev);
0455 }
0456 }
0457
0458 irqreturn_t lan966x_mac_irq_handler(struct lan966x *lan966x)
0459 {
0460 struct lan966x_mac_raw_entry entry[LAN966X_MAC_COLUMNS] = { 0 };
0461 u32 index, column;
0462 bool stop = true;
0463 u32 val;
0464
0465
0466 lan_wr(ANA_MACTINDX_M_INDEX_SET(0) |
0467 ANA_MACTINDX_BUCKET_SET(0),
0468 lan966x, ANA_MACTINDX);
0469
0470 while (1) {
0471 spin_lock(&lan966x->mac_lock);
0472 lan_rmw(ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_SYNC_GET_NEXT),
0473 ANA_MACACCESS_MAC_TABLE_CMD,
0474 lan966x, ANA_MACACCESS);
0475 lan966x_mac_wait_for_completion(lan966x);
0476
0477 val = lan_rd(lan966x, ANA_MACTINDX);
0478 index = ANA_MACTINDX_M_INDEX_GET(val);
0479 column = ANA_MACTINDX_BUCKET_GET(val);
0480
0481
0482
0483
0484
0485
0486
0487
0488
0489
0490
0491 if (index == 0 && column == 0)
0492 stop = false;
0493
0494 if (column == LAN966X_MAC_COLUMNS - 1 &&
0495 index == 0 && stop) {
0496 spin_unlock(&lan966x->mac_lock);
0497 break;
0498 }
0499
0500 entry[column].mach = lan_rd(lan966x, ANA_MACHDATA);
0501 entry[column].macl = lan_rd(lan966x, ANA_MACLDATA);
0502 entry[column].maca = lan_rd(lan966x, ANA_MACACCESS);
0503 spin_unlock(&lan966x->mac_lock);
0504
0505
0506 if (column == LAN966X_MAC_COLUMNS - 1) {
0507 lan966x_mac_irq_process(lan966x, index, entry);
0508
0509
0510
0511 stop = true;
0512 }
0513 }
0514
0515 lan_rmw(ANA_ANAINTR_INTR_SET(0),
0516 ANA_ANAINTR_INTR,
0517 lan966x, ANA_ANAINTR);
0518
0519 return IRQ_HANDLED;
0520 }