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