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 } 470