0001
0002
0003
0004
0005
0006
0007 #include <net/switchdev.h>
0008 #include <linux/if_bridge.h>
0009 #include <linux/iopoll.h>
0010
0011 #include "sparx5_main_regs.h"
0012 #include "sparx5_main.h"
0013
0014
0015 #define MAC_CMD_LEARN 0
0016 #define MAC_CMD_UNLEARN 1
0017 #define MAC_CMD_LOOKUP 2
0018 #define MAC_CMD_READ 3
0019 #define MAC_CMD_WRITE 4
0020 #define MAC_CMD_SCAN 5
0021 #define MAC_CMD_FIND_SMALLEST 6
0022 #define MAC_CMD_CLEAR_ALL 7
0023
0024
0025 #define MAC_ENTRY_ADDR_TYPE_UPSID_PN 0
0026 #define MAC_ENTRY_ADDR_TYPE_UPSID_CPU_OR_INT 1
0027 #define MAC_ENTRY_ADDR_TYPE_GLAG 2
0028 #define MAC_ENTRY_ADDR_TYPE_MC_IDX 3
0029
0030 #define TABLE_UPDATE_SLEEP_US 10
0031 #define TABLE_UPDATE_TIMEOUT_US 100000
0032
0033 struct sparx5_mact_entry {
0034 struct list_head list;
0035 unsigned char mac[ETH_ALEN];
0036 u32 flags;
0037 #define MAC_ENT_ALIVE BIT(0)
0038 #define MAC_ENT_MOVED BIT(1)
0039 #define MAC_ENT_LOCK BIT(2)
0040 u16 vid;
0041 u16 port;
0042 };
0043
0044 static int sparx5_mact_get_status(struct sparx5 *sparx5)
0045 {
0046 return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL);
0047 }
0048
0049 static int sparx5_mact_wait_for_completion(struct sparx5 *sparx5)
0050 {
0051 u32 val;
0052
0053 return readx_poll_timeout(sparx5_mact_get_status,
0054 sparx5, val,
0055 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(val) == 0,
0056 TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
0057 }
0058
0059 static void sparx5_mact_select(struct sparx5 *sparx5,
0060 const unsigned char mac[ETH_ALEN],
0061 u16 vid)
0062 {
0063 u32 macl = 0, mach = 0;
0064
0065
0066
0067
0068 mach |= vid << 16;
0069 mach |= mac[0] << 8;
0070 mach |= mac[1] << 0;
0071 macl |= mac[2] << 24;
0072 macl |= mac[3] << 16;
0073 macl |= mac[4] << 8;
0074 macl |= mac[5] << 0;
0075
0076 spx5_wr(mach, sparx5, LRN_MAC_ACCESS_CFG_0);
0077 spx5_wr(macl, sparx5, LRN_MAC_ACCESS_CFG_1);
0078 }
0079
0080 int sparx5_mact_learn(struct sparx5 *sparx5, int pgid,
0081 const unsigned char mac[ETH_ALEN], u16 vid)
0082 {
0083 int addr, type, ret;
0084
0085 if (pgid < SPX5_PORTS) {
0086 type = MAC_ENTRY_ADDR_TYPE_UPSID_PN;
0087 addr = pgid % 32;
0088 addr += (pgid / 32) << 5;
0089 } else {
0090 type = MAC_ENTRY_ADDR_TYPE_MC_IDX;
0091 addr = pgid - SPX5_PORTS;
0092 }
0093
0094 mutex_lock(&sparx5->lock);
0095
0096 sparx5_mact_select(sparx5, mac, vid);
0097
0098
0099 spx5_wr(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_SET(addr) |
0100 LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_SET(type) |
0101 LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_SET(1) |
0102 LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED_SET(1),
0103 sparx5, LRN_MAC_ACCESS_CFG_2);
0104 spx5_wr(0, sparx5, LRN_MAC_ACCESS_CFG_3);
0105
0106
0107 spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LEARN) |
0108 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
0109 sparx5, LRN_COMMON_ACCESS_CTRL);
0110
0111 ret = sparx5_mact_wait_for_completion(sparx5);
0112
0113 mutex_unlock(&sparx5->lock);
0114
0115 return ret;
0116 }
0117
0118 int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr)
0119 {
0120 struct sparx5_port *port = netdev_priv(dev);
0121 struct sparx5 *sparx5 = port->sparx5;
0122
0123 return sparx5_mact_forget(sparx5, addr, port->pvid);
0124 }
0125
0126 int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr)
0127 {
0128 struct sparx5_port *port = netdev_priv(dev);
0129 struct sparx5 *sparx5 = port->sparx5;
0130
0131 return sparx5_mact_learn(sparx5, PGID_CPU, addr, port->pvid);
0132 }
0133
0134 static int sparx5_mact_get(struct sparx5 *sparx5,
0135 unsigned char mac[ETH_ALEN],
0136 u16 *vid, u32 *pcfg2)
0137 {
0138 u32 mach, macl, cfg2;
0139 int ret = -ENOENT;
0140
0141 cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);
0142 if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) {
0143 mach = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_0);
0144 macl = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_1);
0145 mac[0] = ((mach >> 8) & 0xff);
0146 mac[1] = ((mach >> 0) & 0xff);
0147 mac[2] = ((macl >> 24) & 0xff);
0148 mac[3] = ((macl >> 16) & 0xff);
0149 mac[4] = ((macl >> 8) & 0xff);
0150 mac[5] = ((macl >> 0) & 0xff);
0151 *vid = mach >> 16;
0152 *pcfg2 = cfg2;
0153 ret = 0;
0154 }
0155
0156 return ret;
0157 }
0158
0159 bool sparx5_mact_getnext(struct sparx5 *sparx5,
0160 unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2)
0161 {
0162 u32 cfg2;
0163 int ret;
0164
0165 mutex_lock(&sparx5->lock);
0166
0167 sparx5_mact_select(sparx5, mac, *vid);
0168
0169 spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA_SET(1) |
0170 LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
0171 sparx5, LRN_SCAN_NEXT_CFG);
0172 spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
0173 (MAC_CMD_FIND_SMALLEST) |
0174 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
0175 sparx5, LRN_COMMON_ACCESS_CTRL);
0176
0177 ret = sparx5_mact_wait_for_completion(sparx5);
0178 if (ret == 0) {
0179 ret = sparx5_mact_get(sparx5, mac, vid, &cfg2);
0180 if (ret == 0)
0181 *pcfg2 = cfg2;
0182 }
0183
0184 mutex_unlock(&sparx5->lock);
0185
0186 return ret == 0;
0187 }
0188
0189 bool sparx5_mact_find(struct sparx5 *sparx5,
0190 const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2)
0191 {
0192 int ret;
0193 u32 cfg2;
0194
0195 mutex_lock(&sparx5->lock);
0196
0197 sparx5_mact_select(sparx5, mac, vid);
0198
0199
0200 spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LOOKUP) |
0201 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
0202 sparx5, LRN_COMMON_ACCESS_CTRL);
0203
0204 ret = sparx5_mact_wait_for_completion(sparx5);
0205 if (ret == 0) {
0206 cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);
0207 if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2))
0208 *pcfg2 = cfg2;
0209 else
0210 ret = -ENOENT;
0211 }
0212
0213 mutex_unlock(&sparx5->lock);
0214
0215 return ret;
0216 }
0217
0218 int sparx5_mact_forget(struct sparx5 *sparx5,
0219 const unsigned char mac[ETH_ALEN], u16 vid)
0220 {
0221 int ret;
0222
0223 mutex_lock(&sparx5->lock);
0224
0225 sparx5_mact_select(sparx5, mac, vid);
0226
0227
0228 spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_UNLEARN) |
0229 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
0230 sparx5, LRN_COMMON_ACCESS_CTRL);
0231
0232 ret = sparx5_mact_wait_for_completion(sparx5);
0233
0234 mutex_unlock(&sparx5->lock);
0235
0236 return ret;
0237 }
0238
0239 static struct sparx5_mact_entry *alloc_mact_entry(struct sparx5 *sparx5,
0240 const unsigned char *mac,
0241 u16 vid, u16 port_index)
0242 {
0243 struct sparx5_mact_entry *mact_entry;
0244
0245 mact_entry = devm_kzalloc(sparx5->dev,
0246 sizeof(*mact_entry), GFP_ATOMIC);
0247 if (!mact_entry)
0248 return NULL;
0249
0250 memcpy(mact_entry->mac, mac, ETH_ALEN);
0251 mact_entry->vid = vid;
0252 mact_entry->port = port_index;
0253 return mact_entry;
0254 }
0255
0256 static struct sparx5_mact_entry *find_mact_entry(struct sparx5 *sparx5,
0257 const unsigned char *mac,
0258 u16 vid, u16 port_index)
0259 {
0260 struct sparx5_mact_entry *mact_entry;
0261 struct sparx5_mact_entry *res = NULL;
0262
0263 mutex_lock(&sparx5->mact_lock);
0264 list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
0265 if (mact_entry->vid == vid &&
0266 ether_addr_equal(mac, mact_entry->mac) &&
0267 mact_entry->port == port_index) {
0268 res = mact_entry;
0269 break;
0270 }
0271 }
0272 mutex_unlock(&sparx5->mact_lock);
0273
0274 return res;
0275 }
0276
0277 static void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type,
0278 const char *mac, u16 vid,
0279 struct net_device *dev, bool offloaded)
0280 {
0281 struct switchdev_notifier_fdb_info info = {};
0282
0283 info.addr = mac;
0284 info.vid = vid;
0285 info.offloaded = offloaded;
0286 call_switchdev_notifiers(type, dev, &info.info, NULL);
0287 }
0288
0289 int sparx5_add_mact_entry(struct sparx5 *sparx5,
0290 struct net_device *dev,
0291 u16 portno,
0292 const unsigned char *addr, u16 vid)
0293 {
0294 struct sparx5_mact_entry *mact_entry;
0295 int ret;
0296 u32 cfg2;
0297
0298 ret = sparx5_mact_find(sparx5, addr, vid, &cfg2);
0299 if (!ret)
0300 return 0;
0301
0302
0303
0304
0305
0306
0307
0308 mact_entry = find_mact_entry(sparx5, addr, vid, portno);
0309 if (mact_entry)
0310 goto update_hw;
0311
0312
0313
0314
0315 mact_entry = alloc_mact_entry(sparx5, addr, vid, portno);
0316 if (!mact_entry)
0317 return -ENOMEM;
0318
0319 mutex_lock(&sparx5->mact_lock);
0320 list_add_tail(&mact_entry->list, &sparx5->mact_entries);
0321 mutex_unlock(&sparx5->mact_lock);
0322
0323 update_hw:
0324 ret = sparx5_mact_learn(sparx5, portno, addr, vid);
0325
0326
0327 if (mact_entry->flags == 0) {
0328 mact_entry->flags |= MAC_ENT_LOCK;
0329 sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, addr, vid,
0330 dev, true);
0331 }
0332
0333 return ret;
0334 }
0335
0336 int sparx5_del_mact_entry(struct sparx5 *sparx5,
0337 const unsigned char *addr,
0338 u16 vid)
0339 {
0340 struct sparx5_mact_entry *mact_entry, *tmp;
0341
0342
0343
0344
0345 mutex_lock(&sparx5->mact_lock);
0346 list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
0347 list) {
0348 if ((vid == 0 || mact_entry->vid == vid) &&
0349 ether_addr_equal(addr, mact_entry->mac)) {
0350 list_del(&mact_entry->list);
0351 devm_kfree(sparx5->dev, mact_entry);
0352
0353 sparx5_mact_forget(sparx5, addr, mact_entry->vid);
0354 }
0355 }
0356 mutex_unlock(&sparx5->mact_lock);
0357
0358 return 0;
0359 }
0360
0361 static void sparx5_mact_handle_entry(struct sparx5 *sparx5,
0362 unsigned char mac[ETH_ALEN],
0363 u16 vid, u32 cfg2)
0364 {
0365 struct sparx5_mact_entry *mact_entry;
0366 bool found = false;
0367 u16 port;
0368
0369 if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_GET(cfg2) !=
0370 MAC_ENTRY_ADDR_TYPE_UPSID_PN)
0371 return;
0372
0373 port = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(cfg2);
0374 if (port >= SPX5_PORTS)
0375 return;
0376
0377 if (!test_bit(port, sparx5->bridge_mask))
0378 return;
0379
0380 mutex_lock(&sparx5->mact_lock);
0381 list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
0382 if (mact_entry->vid == vid &&
0383 ether_addr_equal(mac, mact_entry->mac)) {
0384 found = true;
0385 mact_entry->flags |= MAC_ENT_ALIVE;
0386 if (mact_entry->port != port) {
0387 dev_warn(sparx5->dev, "Entry move: %d -> %d\n",
0388 mact_entry->port, port);
0389 mact_entry->port = port;
0390 mact_entry->flags |= MAC_ENT_MOVED;
0391 }
0392
0393 break;
0394 }
0395 }
0396 mutex_unlock(&sparx5->mact_lock);
0397
0398 if (found && !(mact_entry->flags & MAC_ENT_MOVED))
0399
0400 return;
0401
0402 if (!found) {
0403
0404 mact_entry = alloc_mact_entry(sparx5, mac, vid, port);
0405 if (!mact_entry)
0406 return;
0407
0408 mact_entry->flags |= MAC_ENT_ALIVE;
0409 mutex_lock(&sparx5->mact_lock);
0410 list_add_tail(&mact_entry->list, &sparx5->mact_entries);
0411 mutex_unlock(&sparx5->mact_lock);
0412 }
0413
0414
0415 sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
0416 mac, vid, sparx5->ports[port]->ndev,
0417 true);
0418 }
0419
0420 void sparx5_mact_pull_work(struct work_struct *work)
0421 {
0422 struct delayed_work *del_work = to_delayed_work(work);
0423 struct sparx5 *sparx5 = container_of(del_work, struct sparx5,
0424 mact_work);
0425 struct sparx5_mact_entry *mact_entry, *tmp;
0426 unsigned char mac[ETH_ALEN];
0427 u32 cfg2;
0428 u16 vid;
0429 int ret;
0430
0431
0432 mutex_lock(&sparx5->mact_lock);
0433 list_for_each_entry(mact_entry, &sparx5->mact_entries, list)
0434 mact_entry->flags &= MAC_ENT_LOCK;
0435 mutex_unlock(&sparx5->mact_lock);
0436
0437
0438 vid = 0;
0439 memset(mac, 0, sizeof(mac));
0440 do {
0441 mutex_lock(&sparx5->lock);
0442 sparx5_mact_select(sparx5, mac, vid);
0443 spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
0444 sparx5, LRN_SCAN_NEXT_CFG);
0445 spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
0446 (MAC_CMD_FIND_SMALLEST) |
0447 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
0448 sparx5, LRN_COMMON_ACCESS_CTRL);
0449 ret = sparx5_mact_wait_for_completion(sparx5);
0450 if (ret == 0)
0451 ret = sparx5_mact_get(sparx5, mac, &vid, &cfg2);
0452 mutex_unlock(&sparx5->lock);
0453 if (ret == 0)
0454 sparx5_mact_handle_entry(sparx5, mac, vid, cfg2);
0455 } while (ret == 0);
0456
0457 mutex_lock(&sparx5->mact_lock);
0458 list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
0459 list) {
0460
0461 if (mact_entry->flags & (MAC_ENT_ALIVE | MAC_ENT_LOCK))
0462 continue;
0463
0464 sparx5_fdb_call_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
0465 mact_entry->mac, mact_entry->vid,
0466 sparx5->ports[mact_entry->port]->ndev,
0467 true);
0468
0469 list_del(&mact_entry->list);
0470 devm_kfree(sparx5->dev, mact_entry);
0471 }
0472 mutex_unlock(&sparx5->mact_lock);
0473
0474 queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
0475 SPX5_MACT_PULL_DELAY);
0476 }
0477
0478 void sparx5_set_ageing(struct sparx5 *sparx5, int msecs)
0479 {
0480 int value = max(1, msecs / 10);
0481
0482 spx5_rmw(LRN_AUTOAGE_CFG_UNIT_SIZE_SET(2) |
0483 LRN_AUTOAGE_CFG_PERIOD_VAL_SET(value / 2),
0484 LRN_AUTOAGE_CFG_UNIT_SIZE |
0485 LRN_AUTOAGE_CFG_PERIOD_VAL,
0486 sparx5,
0487 LRN_AUTOAGE_CFG(0));
0488 }
0489
0490 void sparx5_mact_init(struct sparx5 *sparx5)
0491 {
0492 mutex_init(&sparx5->lock);
0493
0494
0495 spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) |
0496 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
0497 sparx5, LRN_COMMON_ACCESS_CTRL);
0498
0499 if (sparx5_mact_wait_for_completion(sparx5) != 0)
0500 dev_warn(sparx5->dev, "MAC flush error\n");
0501
0502 sparx5_set_ageing(sparx5, BR_DEFAULT_AGEING_TIME / HZ * 1000);
0503 }