1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Microchip Sparx5 Switch driver 3 * 4 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries. 5 */ 6 7 #include <net/switchdev.h> 8 #include <linux/if_bridge.h> 9 #include <linux/iopoll.h> 10 11 #include "sparx5_main_regs.h" 12 #include "sparx5_main.h" 13 14 /* Commands for Mac Table Command register */ 15 #define MAC_CMD_LEARN 0 /* Insert (Learn) 1 entry */ 16 #define MAC_CMD_UNLEARN 1 /* Unlearn (Forget) 1 entry */ 17 #define MAC_CMD_LOOKUP 2 /* Look up 1 entry */ 18 #define MAC_CMD_READ 3 /* Read entry at Mac Table Index */ 19 #define MAC_CMD_WRITE 4 /* Write entry at Mac Table Index */ 20 #define MAC_CMD_SCAN 5 /* Scan (Age or find next) */ 21 #define MAC_CMD_FIND_SMALLEST 6 /* Get next entry */ 22 #define MAC_CMD_CLEAR_ALL 7 /* Delete all entries in table */ 23 24 /* Commands for MAC_ENTRY_ADDR_TYPE */ 25 #define MAC_ENTRY_ADDR_TYPE_UPSID_PN 0 26 #define MAC_ENTRY_ADDR_TYPE_UPSID_CPU_OR_INT 1 27 #define MAC_ENTRY_ADDR_TYPE_GLAG 2 28 #define MAC_ENTRY_ADDR_TYPE_MC_IDX 3 29 30 #define TABLE_UPDATE_SLEEP_US 10 31 #define TABLE_UPDATE_TIMEOUT_US 100000 32 33 struct sparx5_mact_entry { 34 struct list_head list; 35 unsigned char mac[ETH_ALEN]; 36 u32 flags; 37 #define MAC_ENT_ALIVE BIT(0) 38 #define MAC_ENT_MOVED BIT(1) 39 #define MAC_ENT_LOCK BIT(2) 40 u16 vid; 41 u16 port; 42 }; 43 44 static int sparx5_mact_get_status(struct sparx5 *sparx5) 45 { 46 return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL); 47 } 48 49 static int sparx5_mact_wait_for_completion(struct sparx5 *sparx5) 50 { 51 u32 val; 52 53 return readx_poll_timeout(sparx5_mact_get_status, 54 sparx5, val, 55 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(val) == 0, 56 TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US); 57 } 58 59 static void sparx5_mact_select(struct sparx5 *sparx5, 60 const unsigned char mac[ETH_ALEN], 61 u16 vid) 62 { 63 u32 macl = 0, mach = 0; 64 65 /* Set the MAC address to handle and the vlan associated in a format 66 * understood by the hardware. 67 */ 68 mach |= vid << 16; 69 mach |= mac[0] << 8; 70 mach |= mac[1] << 0; 71 macl |= mac[2] << 24; 72 macl |= mac[3] << 16; 73 macl |= mac[4] << 8; 74 macl |= mac[5] << 0; 75 76 spx5_wr(mach, sparx5, LRN_MAC_ACCESS_CFG_0); 77 spx5_wr(macl, sparx5, LRN_MAC_ACCESS_CFG_1); 78 } 79 80 int sparx5_mact_learn(struct sparx5 *sparx5, int pgid, 81 const unsigned char mac[ETH_ALEN], u16 vid) 82 { 83 int addr, type, ret; 84 85 if (pgid < SPX5_PORTS) { 86 type = MAC_ENTRY_ADDR_TYPE_UPSID_PN; 87 addr = pgid % 32; 88 addr += (pgid / 32) << 5; /* Add upsid */ 89 } else { 90 type = MAC_ENTRY_ADDR_TYPE_MC_IDX; 91 addr = pgid - SPX5_PORTS; 92 } 93 94 mutex_lock(&sparx5->lock); 95 96 sparx5_mact_select(sparx5, mac, vid); 97 98 /* MAC entry properties */ 99 spx5_wr(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_SET(addr) | 100 LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_SET(type) | 101 LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_SET(1) | 102 LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED_SET(1), 103 sparx5, LRN_MAC_ACCESS_CFG_2); 104 spx5_wr(0, sparx5, LRN_MAC_ACCESS_CFG_3); 105 106 /* Insert/learn new entry */ 107 spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LEARN) | 108 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1), 109 sparx5, LRN_COMMON_ACCESS_CTRL); 110 111 ret = sparx5_mact_wait_for_completion(sparx5); 112 113 mutex_unlock(&sparx5->lock); 114 115 return ret; 116 } 117 118 int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr) 119 { 120 struct sparx5_port *port = netdev_priv(dev); 121 struct sparx5 *sparx5 = port->sparx5; 122 123 return sparx5_mact_forget(sparx5, addr, port->pvid); 124 } 125 126 int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr) 127 { 128 struct sparx5_port *port = netdev_priv(dev); 129 struct sparx5 *sparx5 = port->sparx5; 130 131 return sparx5_mact_learn(sparx5, PGID_CPU, addr, port->pvid); 132 } 133 134 static int sparx5_mact_get(struct sparx5 *sparx5, 135 unsigned char mac[ETH_ALEN], 136 u16 *vid, u32 *pcfg2) 137 { 138 u32 mach, macl, cfg2; 139 int ret = -ENOENT; 140 141 cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2); 142 if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) { 143 mach = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_0); 144 macl = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_1); 145 mac[0] = ((mach >> 8) & 0xff); 146 mac[1] = ((mach >> 0) & 0xff); 147 mac[2] = ((macl >> 24) & 0xff); 148 mac[3] = ((macl >> 16) & 0xff); 149 mac[4] = ((macl >> 8) & 0xff); 150 mac[5] = ((macl >> 0) & 0xff); 151 *vid = mach >> 16; 152 *pcfg2 = cfg2; 153 ret = 0; 154 } 155 156 return ret; 157 } 158 159 bool sparx5_mact_getnext(struct sparx5 *sparx5, 160 unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2) 161 { 162 u32 cfg2; 163 int ret; 164 165 mutex_lock(&sparx5->lock); 166 167 sparx5_mact_select(sparx5, mac, *vid); 168 169 spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA_SET(1) | 170 LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1), 171 sparx5, LRN_SCAN_NEXT_CFG); 172 spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET 173 (MAC_CMD_FIND_SMALLEST) | 174 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1), 175 sparx5, LRN_COMMON_ACCESS_CTRL); 176 177 ret = sparx5_mact_wait_for_completion(sparx5); 178 if (ret == 0) { 179 ret = sparx5_mact_get(sparx5, mac, vid, &cfg2); 180 if (ret == 0) 181 *pcfg2 = cfg2; 182 } 183 184 mutex_unlock(&sparx5->lock); 185 186 return ret == 0; 187 } 188 189 bool sparx5_mact_find(struct sparx5 *sparx5, 190 const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2) 191 { 192 int ret; 193 u32 cfg2; 194 195 mutex_lock(&sparx5->lock); 196 197 sparx5_mact_select(sparx5, mac, vid); 198 199 /* Issue a lookup command */ 200 spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LOOKUP) | 201 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1), 202 sparx5, LRN_COMMON_ACCESS_CTRL); 203 204 ret = sparx5_mact_wait_for_completion(sparx5); 205 if (ret == 0) { 206 cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2); 207 if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) 208 *pcfg2 = cfg2; 209 else 210 ret = -ENOENT; 211 } 212 213 mutex_unlock(&sparx5->lock); 214 215 return ret == 0; 216 } 217 218 static int sparx5_mact_lookup(struct sparx5 *sparx5, 219 const unsigned char mac[ETH_ALEN], 220 u16 vid) 221 { 222 u32 pcfg2; 223 224 if (sparx5_mact_find(sparx5, mac, vid, &pcfg2)) 225 return 1; 226 227 return 0; 228 } 229 230 int sparx5_mact_forget(struct sparx5 *sparx5, 231 const unsigned char mac[ETH_ALEN], u16 vid) 232 { 233 int ret; 234 235 mutex_lock(&sparx5->lock); 236 237 sparx5_mact_select(sparx5, mac, vid); 238 239 /* Issue an unlearn command */ 240 spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_UNLEARN) | 241 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1), 242 sparx5, LRN_COMMON_ACCESS_CTRL); 243 244 ret = sparx5_mact_wait_for_completion(sparx5); 245 246 mutex_unlock(&sparx5->lock); 247 248 return ret; 249 } 250 251 static struct sparx5_mact_entry *alloc_mact_entry(struct sparx5 *sparx5, 252 const unsigned char *mac, 253 u16 vid, u16 port_index) 254 { 255 struct sparx5_mact_entry *mact_entry; 256 257 mact_entry = devm_kzalloc(sparx5->dev, 258 sizeof(*mact_entry), GFP_ATOMIC); 259 if (!mact_entry) 260 return NULL; 261 262 memcpy(mact_entry->mac, mac, ETH_ALEN); 263 mact_entry->vid = vid; 264 mact_entry->port = port_index; 265 return mact_entry; 266 } 267 268 static struct sparx5_mact_entry *find_mact_entry(struct sparx5 *sparx5, 269 const unsigned char *mac, 270 u16 vid, u16 port_index) 271 { 272 struct sparx5_mact_entry *mact_entry; 273 struct sparx5_mact_entry *res = NULL; 274 275 mutex_lock(&sparx5->mact_lock); 276 list_for_each_entry(mact_entry, &sparx5->mact_entries, list) { 277 if (mact_entry->vid == vid && 278 ether_addr_equal(mac, mact_entry->mac) && 279 mact_entry->port == port_index) { 280 res = mact_entry; 281 break; 282 } 283 } 284 mutex_unlock(&sparx5->mact_lock); 285 286 return res; 287 } 288 289 static void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type, 290 const char *mac, u16 vid, 291 struct net_device *dev, bool offloaded) 292 { 293 struct switchdev_notifier_fdb_info info = {}; 294 295 info.addr = mac; 296 info.vid = vid; 297 info.offloaded = offloaded; 298 call_switchdev_notifiers(type, dev, &info.info, NULL); 299 } 300 301 int sparx5_add_mact_entry(struct sparx5 *sparx5, 302 struct net_device *dev, 303 u16 portno, 304 const unsigned char *addr, u16 vid) 305 { 306 struct sparx5_mact_entry *mact_entry; 307 int ret; 308 309 ret = sparx5_mact_lookup(sparx5, addr, vid); 310 if (ret) 311 return 0; 312 313 /* In case the entry already exists, don't add it again to SW, 314 * just update HW, but we need to look in the actual HW because 315 * it is possible for an entry to be learn by HW and before the 316 * mact thread to start the frame will reach CPU and the CPU will 317 * add the entry but without the extern_learn flag. 318 */ 319 mact_entry = find_mact_entry(sparx5, addr, vid, portno); 320 if (mact_entry) 321 goto update_hw; 322 323 /* Add the entry in SW MAC table not to get the notification when 324 * SW is pulling again 325 */ 326 mact_entry = alloc_mact_entry(sparx5, addr, vid, portno); 327 if (!mact_entry) 328 return -ENOMEM; 329 330 mutex_lock(&sparx5->mact_lock); 331 list_add_tail(&mact_entry->list, &sparx5->mact_entries); 332 mutex_unlock(&sparx5->mact_lock); 333 334 update_hw: 335 ret = sparx5_mact_learn(sparx5, portno, addr, vid); 336 337 /* New entry? */ 338 if (mact_entry->flags == 0) { 339 mact_entry->flags |= MAC_ENT_LOCK; /* Don't age this */ 340 sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, addr, vid, 341 dev, true); 342 } 343 344 return ret; 345 } 346 347 int sparx5_del_mact_entry(struct sparx5 *sparx5, 348 const unsigned char *addr, 349 u16 vid) 350 { 351 struct sparx5_mact_entry *mact_entry, *tmp; 352 353 /* Delete the entry in SW MAC table not to get the notification when 354 * SW is pulling again 355 */ 356 mutex_lock(&sparx5->mact_lock); 357 list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries, 358 list) { 359 if ((vid == 0 || mact_entry->vid == vid) && 360 ether_addr_equal(addr, mact_entry->mac)) { 361 list_del(&mact_entry->list); 362 devm_kfree(sparx5->dev, mact_entry); 363 364 sparx5_mact_forget(sparx5, addr, mact_entry->vid); 365 } 366 } 367 mutex_unlock(&sparx5->mact_lock); 368 369 return 0; 370 } 371 372 static void sparx5_mact_handle_entry(struct sparx5 *sparx5, 373 unsigned char mac[ETH_ALEN], 374 u16 vid, u32 cfg2) 375 { 376 struct sparx5_mact_entry *mact_entry; 377 bool found = false; 378 u16 port; 379 380 if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_GET(cfg2) != 381 MAC_ENTRY_ADDR_TYPE_UPSID_PN) 382 return; 383 384 port = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(cfg2); 385 if (port >= SPX5_PORTS) 386 return; 387 388 if (!test_bit(port, sparx5->bridge_mask)) 389 return; 390 391 mutex_lock(&sparx5->mact_lock); 392 list_for_each_entry(mact_entry, &sparx5->mact_entries, list) { 393 if (mact_entry->vid == vid && 394 ether_addr_equal(mac, mact_entry->mac)) { 395 found = true; 396 mact_entry->flags |= MAC_ENT_ALIVE; 397 if (mact_entry->port != port) { 398 dev_warn(sparx5->dev, "Entry move: %d -> %d\n", 399 mact_entry->port, port); 400 mact_entry->port = port; 401 mact_entry->flags |= MAC_ENT_MOVED; 402 } 403 /* Entry handled */ 404 break; 405 } 406 } 407 mutex_unlock(&sparx5->mact_lock); 408 409 if (found && !(mact_entry->flags & MAC_ENT_MOVED)) 410 /* Present, not moved */ 411 return; 412 413 if (!found) { 414 /* Entry not found - now add */ 415 mact_entry = alloc_mact_entry(sparx5, mac, vid, port); 416 if (!mact_entry) 417 return; 418 419 mact_entry->flags |= MAC_ENT_ALIVE; 420 mutex_lock(&sparx5->mact_lock); 421 list_add_tail(&mact_entry->list, &sparx5->mact_entries); 422 mutex_unlock(&sparx5->mact_lock); 423 } 424 425 /* New or moved entry - notify bridge */ 426 sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, 427 mac, vid, sparx5->ports[port]->ndev, 428 true); 429 } 430 431 void sparx5_mact_pull_work(struct work_struct *work) 432 { 433 struct delayed_work *del_work = to_delayed_work(work); 434 struct sparx5 *sparx5 = container_of(del_work, struct sparx5, 435 mact_work); 436 struct sparx5_mact_entry *mact_entry, *tmp; 437 unsigned char mac[ETH_ALEN]; 438 u32 cfg2; 439 u16 vid; 440 int ret; 441 442 /* Reset MAC entry flags */ 443 mutex_lock(&sparx5->mact_lock); 444 list_for_each_entry(mact_entry, &sparx5->mact_entries, list) 445 mact_entry->flags &= MAC_ENT_LOCK; 446 mutex_unlock(&sparx5->mact_lock); 447 448 /* MAIN mac address processing loop */ 449 vid = 0; 450 memset(mac, 0, sizeof(mac)); 451 do { 452 mutex_lock(&sparx5->lock); 453 sparx5_mact_select(sparx5, mac, vid); 454 spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1), 455 sparx5, LRN_SCAN_NEXT_CFG); 456 spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET 457 (MAC_CMD_FIND_SMALLEST) | 458 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1), 459 sparx5, LRN_COMMON_ACCESS_CTRL); 460 ret = sparx5_mact_wait_for_completion(sparx5); 461 if (ret == 0) 462 ret = sparx5_mact_get(sparx5, mac, &vid, &cfg2); 463 mutex_unlock(&sparx5->lock); 464 if (ret == 0) 465 sparx5_mact_handle_entry(sparx5, mac, vid, cfg2); 466 } while (ret == 0); 467 468 mutex_lock(&sparx5->mact_lock); 469 list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries, 470 list) { 471 /* If the entry is in HW or permanent, then skip */ 472 if (mact_entry->flags & (MAC_ENT_ALIVE | MAC_ENT_LOCK)) 473 continue; 474 475 sparx5_fdb_call_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, 476 mact_entry->mac, mact_entry->vid, 477 sparx5->ports[mact_entry->port]->ndev, 478 true); 479 480 list_del(&mact_entry->list); 481 devm_kfree(sparx5->dev, mact_entry); 482 } 483 mutex_unlock(&sparx5->mact_lock); 484 485 queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work, 486 SPX5_MACT_PULL_DELAY); 487 } 488 489 void sparx5_set_ageing(struct sparx5 *sparx5, int msecs) 490 { 491 int value = max(1, msecs / 10); /* unit 10 ms */ 492 493 spx5_rmw(LRN_AUTOAGE_CFG_UNIT_SIZE_SET(2) | /* 10 ms */ 494 LRN_AUTOAGE_CFG_PERIOD_VAL_SET(value / 2), /* one bit ageing */ 495 LRN_AUTOAGE_CFG_UNIT_SIZE | 496 LRN_AUTOAGE_CFG_PERIOD_VAL, 497 sparx5, 498 LRN_AUTOAGE_CFG(0)); 499 } 500 501 void sparx5_mact_init(struct sparx5 *sparx5) 502 { 503 mutex_init(&sparx5->lock); 504 505 /* Flush MAC table */ 506 spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) | 507 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1), 508 sparx5, LRN_COMMON_ACCESS_CTRL); 509 510 if (sparx5_mact_wait_for_completion(sparx5) != 0) 511 dev_warn(sparx5->dev, "MAC flush error\n"); 512 513 sparx5_set_ageing(sparx5, BR_DEFAULT_AGEING_TIME / HZ * 1000); 514 } 515