1 // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 /* 3 * DSA driver for: 4 * Hirschmann Hellcreek TSN switch. 5 * 6 * Copyright (C) 2019-2021 Linutronix GmbH 7 * Author Kurt Kanzenbach <kurt@linutronix.de> 8 */ 9 10 #include <linux/kernel.h> 11 #include <linux/module.h> 12 #include <linux/device.h> 13 #include <linux/of.h> 14 #include <linux/of_mdio.h> 15 #include <linux/platform_device.h> 16 #include <linux/bitops.h> 17 #include <linux/if_bridge.h> 18 #include <linux/if_vlan.h> 19 #include <linux/etherdevice.h> 20 #include <linux/random.h> 21 #include <linux/iopoll.h> 22 #include <linux/mutex.h> 23 #include <linux/delay.h> 24 #include <net/dsa.h> 25 26 #include "hellcreek.h" 27 #include "hellcreek_ptp.h" 28 #include "hellcreek_hwtstamp.h" 29 30 static const struct hellcreek_counter hellcreek_counter[] = { 31 { 0x00, "RxFiltered", }, 32 { 0x01, "RxOctets1k", }, 33 { 0x02, "RxVTAG", }, 34 { 0x03, "RxL2BAD", }, 35 { 0x04, "RxOverloadDrop", }, 36 { 0x05, "RxUC", }, 37 { 0x06, "RxMC", }, 38 { 0x07, "RxBC", }, 39 { 0x08, "RxRS<64", }, 40 { 0x09, "RxRS64", }, 41 { 0x0a, "RxRS65_127", }, 42 { 0x0b, "RxRS128_255", }, 43 { 0x0c, "RxRS256_511", }, 44 { 0x0d, "RxRS512_1023", }, 45 { 0x0e, "RxRS1024_1518", }, 46 { 0x0f, "RxRS>1518", }, 47 { 0x10, "TxTailDropQueue0", }, 48 { 0x11, "TxTailDropQueue1", }, 49 { 0x12, "TxTailDropQueue2", }, 50 { 0x13, "TxTailDropQueue3", }, 51 { 0x14, "TxTailDropQueue4", }, 52 { 0x15, "TxTailDropQueue5", }, 53 { 0x16, "TxTailDropQueue6", }, 54 { 0x17, "TxTailDropQueue7", }, 55 { 0x18, "RxTrafficClass0", }, 56 { 0x19, "RxTrafficClass1", }, 57 { 0x1a, "RxTrafficClass2", }, 58 { 0x1b, "RxTrafficClass3", }, 59 { 0x1c, "RxTrafficClass4", }, 60 { 0x1d, "RxTrafficClass5", }, 61 { 0x1e, "RxTrafficClass6", }, 62 { 0x1f, "RxTrafficClass7", }, 63 { 0x21, "TxOctets1k", }, 64 { 0x22, "TxVTAG", }, 65 { 0x23, "TxL2BAD", }, 66 { 0x25, "TxUC", }, 67 { 0x26, "TxMC", }, 68 { 0x27, "TxBC", }, 69 { 0x28, "TxTS<64", }, 70 { 0x29, "TxTS64", }, 71 { 0x2a, "TxTS65_127", }, 72 { 0x2b, "TxTS128_255", }, 73 { 0x2c, "TxTS256_511", }, 74 { 0x2d, "TxTS512_1023", }, 75 { 0x2e, "TxTS1024_1518", }, 76 { 0x2f, "TxTS>1518", }, 77 { 0x30, "TxTrafficClassOverrun0", }, 78 { 0x31, "TxTrafficClassOverrun1", }, 79 { 0x32, "TxTrafficClassOverrun2", }, 80 { 0x33, "TxTrafficClassOverrun3", }, 81 { 0x34, "TxTrafficClassOverrun4", }, 82 { 0x35, "TxTrafficClassOverrun5", }, 83 { 0x36, "TxTrafficClassOverrun6", }, 84 { 0x37, "TxTrafficClassOverrun7", }, 85 { 0x38, "TxTrafficClass0", }, 86 { 0x39, "TxTrafficClass1", }, 87 { 0x3a, "TxTrafficClass2", }, 88 { 0x3b, "TxTrafficClass3", }, 89 { 0x3c, "TxTrafficClass4", }, 90 { 0x3d, "TxTrafficClass5", }, 91 { 0x3e, "TxTrafficClass6", }, 92 { 0x3f, "TxTrafficClass7", }, 93 }; 94 95 static u16 hellcreek_read(struct hellcreek *hellcreek, unsigned int offset) 96 { 97 return readw(hellcreek->base + offset); 98 } 99 100 static u16 hellcreek_read_ctrl(struct hellcreek *hellcreek) 101 { 102 return readw(hellcreek->base + HR_CTRL_C); 103 } 104 105 static u16 hellcreek_read_stat(struct hellcreek *hellcreek) 106 { 107 return readw(hellcreek->base + HR_SWSTAT); 108 } 109 110 static void hellcreek_write(struct hellcreek *hellcreek, u16 data, 111 unsigned int offset) 112 { 113 writew(data, hellcreek->base + offset); 114 } 115 116 static void hellcreek_select_port(struct hellcreek *hellcreek, int port) 117 { 118 u16 val = port << HR_PSEL_PTWSEL_SHIFT; 119 120 hellcreek_write(hellcreek, val, HR_PSEL); 121 } 122 123 static void hellcreek_select_prio(struct hellcreek *hellcreek, int prio) 124 { 125 u16 val = prio << HR_PSEL_PRTCWSEL_SHIFT; 126 127 hellcreek_write(hellcreek, val, HR_PSEL); 128 } 129 130 static void hellcreek_select_port_prio(struct hellcreek *hellcreek, int port, 131 int prio) 132 { 133 u16 val = port << HR_PSEL_PTWSEL_SHIFT; 134 135 val |= prio << HR_PSEL_PRTCWSEL_SHIFT; 136 137 hellcreek_write(hellcreek, val, HR_PSEL); 138 } 139 140 static void hellcreek_select_counter(struct hellcreek *hellcreek, int counter) 141 { 142 u16 val = counter << HR_CSEL_SHIFT; 143 144 hellcreek_write(hellcreek, val, HR_CSEL); 145 146 /* Data sheet states to wait at least 20 internal clock cycles */ 147 ndelay(200); 148 } 149 150 static void hellcreek_select_vlan(struct hellcreek *hellcreek, int vid, 151 bool pvid) 152 { 153 u16 val = 0; 154 155 /* Set pvid bit first */ 156 if (pvid) 157 val |= HR_VIDCFG_PVID; 158 hellcreek_write(hellcreek, val, HR_VIDCFG); 159 160 /* Set vlan */ 161 val |= vid << HR_VIDCFG_VID_SHIFT; 162 hellcreek_write(hellcreek, val, HR_VIDCFG); 163 } 164 165 static void hellcreek_select_tgd(struct hellcreek *hellcreek, int port) 166 { 167 u16 val = port << TR_TGDSEL_TDGSEL_SHIFT; 168 169 hellcreek_write(hellcreek, val, TR_TGDSEL); 170 } 171 172 static int hellcreek_wait_until_ready(struct hellcreek *hellcreek) 173 { 174 u16 val; 175 176 /* Wait up to 1ms, although 3 us should be enough */ 177 return readx_poll_timeout(hellcreek_read_ctrl, hellcreek, 178 val, val & HR_CTRL_C_READY, 179 3, 1000); 180 } 181 182 static int hellcreek_wait_until_transitioned(struct hellcreek *hellcreek) 183 { 184 u16 val; 185 186 return readx_poll_timeout_atomic(hellcreek_read_ctrl, hellcreek, 187 val, !(val & HR_CTRL_C_TRANSITION), 188 1, 1000); 189 } 190 191 static int hellcreek_wait_fdb_ready(struct hellcreek *hellcreek) 192 { 193 u16 val; 194 195 return readx_poll_timeout_atomic(hellcreek_read_stat, hellcreek, 196 val, !(val & HR_SWSTAT_BUSY), 197 1, 1000); 198 } 199 200 static int hellcreek_detect(struct hellcreek *hellcreek) 201 { 202 u16 id, rel_low, rel_high, date_low, date_high, tgd_ver; 203 u8 tgd_maj, tgd_min; 204 u32 rel, date; 205 206 id = hellcreek_read(hellcreek, HR_MODID_C); 207 rel_low = hellcreek_read(hellcreek, HR_REL_L_C); 208 rel_high = hellcreek_read(hellcreek, HR_REL_H_C); 209 date_low = hellcreek_read(hellcreek, HR_BLD_L_C); 210 date_high = hellcreek_read(hellcreek, HR_BLD_H_C); 211 tgd_ver = hellcreek_read(hellcreek, TR_TGDVER); 212 213 if (id != hellcreek->pdata->module_id) 214 return -ENODEV; 215 216 rel = rel_low | (rel_high << 16); 217 date = date_low | (date_high << 16); 218 tgd_maj = (tgd_ver & TR_TGDVER_REV_MAJ_MASK) >> TR_TGDVER_REV_MAJ_SHIFT; 219 tgd_min = (tgd_ver & TR_TGDVER_REV_MIN_MASK) >> TR_TGDVER_REV_MIN_SHIFT; 220 221 dev_info(hellcreek->dev, "Module ID=%02x Release=%04x Date=%04x TGD Version=%02x.%02x\n", 222 id, rel, date, tgd_maj, tgd_min); 223 224 return 0; 225 } 226 227 static void hellcreek_feature_detect(struct hellcreek *hellcreek) 228 { 229 u16 features; 230 231 features = hellcreek_read(hellcreek, HR_FEABITS0); 232 233 /* Only detect the size of the FDB table. The size and current 234 * utilization can be queried via devlink. 235 */ 236 hellcreek->fdb_entries = ((features & HR_FEABITS0_FDBBINS_MASK) >> 237 HR_FEABITS0_FDBBINS_SHIFT) * 32; 238 } 239 240 static enum dsa_tag_protocol hellcreek_get_tag_protocol(struct dsa_switch *ds, 241 int port, 242 enum dsa_tag_protocol mp) 243 { 244 return DSA_TAG_PROTO_HELLCREEK; 245 } 246 247 static int hellcreek_port_enable(struct dsa_switch *ds, int port, 248 struct phy_device *phy) 249 { 250 struct hellcreek *hellcreek = ds->priv; 251 struct hellcreek_port *hellcreek_port; 252 u16 val; 253 254 hellcreek_port = &hellcreek->ports[port]; 255 256 dev_dbg(hellcreek->dev, "Enable port %d\n", port); 257 258 mutex_lock(&hellcreek->reg_lock); 259 260 hellcreek_select_port(hellcreek, port); 261 val = hellcreek_port->ptcfg; 262 val |= HR_PTCFG_ADMIN_EN; 263 hellcreek_write(hellcreek, val, HR_PTCFG); 264 hellcreek_port->ptcfg = val; 265 266 mutex_unlock(&hellcreek->reg_lock); 267 268 return 0; 269 } 270 271 static void hellcreek_port_disable(struct dsa_switch *ds, int port) 272 { 273 struct hellcreek *hellcreek = ds->priv; 274 struct hellcreek_port *hellcreek_port; 275 u16 val; 276 277 hellcreek_port = &hellcreek->ports[port]; 278 279 dev_dbg(hellcreek->dev, "Disable port %d\n", port); 280 281 mutex_lock(&hellcreek->reg_lock); 282 283 hellcreek_select_port(hellcreek, port); 284 val = hellcreek_port->ptcfg; 285 val &= ~HR_PTCFG_ADMIN_EN; 286 hellcreek_write(hellcreek, val, HR_PTCFG); 287 hellcreek_port->ptcfg = val; 288 289 mutex_unlock(&hellcreek->reg_lock); 290 } 291 292 static void hellcreek_get_strings(struct dsa_switch *ds, int port, 293 u32 stringset, uint8_t *data) 294 { 295 int i; 296 297 for (i = 0; i < ARRAY_SIZE(hellcreek_counter); ++i) { 298 const struct hellcreek_counter *counter = &hellcreek_counter[i]; 299 300 strscpy(data + i * ETH_GSTRING_LEN, 301 counter->name, ETH_GSTRING_LEN); 302 } 303 } 304 305 static int hellcreek_get_sset_count(struct dsa_switch *ds, int port, int sset) 306 { 307 if (sset != ETH_SS_STATS) 308 return 0; 309 310 return ARRAY_SIZE(hellcreek_counter); 311 } 312 313 static void hellcreek_get_ethtool_stats(struct dsa_switch *ds, int port, 314 uint64_t *data) 315 { 316 struct hellcreek *hellcreek = ds->priv; 317 struct hellcreek_port *hellcreek_port; 318 int i; 319 320 hellcreek_port = &hellcreek->ports[port]; 321 322 for (i = 0; i < ARRAY_SIZE(hellcreek_counter); ++i) { 323 const struct hellcreek_counter *counter = &hellcreek_counter[i]; 324 u8 offset = counter->offset + port * 64; 325 u16 high, low; 326 u64 value; 327 328 mutex_lock(&hellcreek->reg_lock); 329 330 hellcreek_select_counter(hellcreek, offset); 331 332 /* The registers are locked internally by selecting the 333 * counter. So low and high can be read without reading high 334 * again. 335 */ 336 high = hellcreek_read(hellcreek, HR_CRDH); 337 low = hellcreek_read(hellcreek, HR_CRDL); 338 value = ((u64)high << 16) | low; 339 340 hellcreek_port->counter_values[i] += value; 341 data[i] = hellcreek_port->counter_values[i]; 342 343 mutex_unlock(&hellcreek->reg_lock); 344 } 345 } 346 347 static u16 hellcreek_private_vid(int port) 348 { 349 return VLAN_N_VID - port + 1; 350 } 351 352 static int hellcreek_vlan_prepare(struct dsa_switch *ds, int port, 353 const struct switchdev_obj_port_vlan *vlan, 354 struct netlink_ext_ack *extack) 355 { 356 struct hellcreek *hellcreek = ds->priv; 357 int i; 358 359 dev_dbg(hellcreek->dev, "VLAN prepare for port %d\n", port); 360 361 /* Restriction: Make sure that nobody uses the "private" VLANs. These 362 * VLANs are internally used by the driver to ensure port 363 * separation. Thus, they cannot be used by someone else. 364 */ 365 for (i = 0; i < hellcreek->pdata->num_ports; ++i) { 366 const u16 restricted_vid = hellcreek_private_vid(i); 367 368 if (!dsa_is_user_port(ds, i)) 369 continue; 370 371 if (vlan->vid == restricted_vid) { 372 NL_SET_ERR_MSG_MOD(extack, "VID restricted by driver"); 373 return -EBUSY; 374 } 375 } 376 377 return 0; 378 } 379 380 static void hellcreek_select_vlan_params(struct hellcreek *hellcreek, int port, 381 int *shift, int *mask) 382 { 383 switch (port) { 384 case 0: 385 *shift = HR_VIDMBRCFG_P0MBR_SHIFT; 386 *mask = HR_VIDMBRCFG_P0MBR_MASK; 387 break; 388 case 1: 389 *shift = HR_VIDMBRCFG_P1MBR_SHIFT; 390 *mask = HR_VIDMBRCFG_P1MBR_MASK; 391 break; 392 case 2: 393 *shift = HR_VIDMBRCFG_P2MBR_SHIFT; 394 *mask = HR_VIDMBRCFG_P2MBR_MASK; 395 break; 396 case 3: 397 *shift = HR_VIDMBRCFG_P3MBR_SHIFT; 398 *mask = HR_VIDMBRCFG_P3MBR_MASK; 399 break; 400 default: 401 *shift = *mask = 0; 402 dev_err(hellcreek->dev, "Unknown port %d selected!\n", port); 403 } 404 } 405 406 static void hellcreek_apply_vlan(struct hellcreek *hellcreek, int port, u16 vid, 407 bool pvid, bool untagged) 408 { 409 int shift, mask; 410 u16 val; 411 412 dev_dbg(hellcreek->dev, "Apply VLAN: port=%d vid=%u pvid=%d untagged=%d", 413 port, vid, pvid, untagged); 414 415 mutex_lock(&hellcreek->reg_lock); 416 417 hellcreek_select_port(hellcreek, port); 418 hellcreek_select_vlan(hellcreek, vid, pvid); 419 420 /* Setup port vlan membership */ 421 hellcreek_select_vlan_params(hellcreek, port, &shift, &mask); 422 val = hellcreek->vidmbrcfg[vid]; 423 val &= ~mask; 424 if (untagged) 425 val |= HELLCREEK_VLAN_UNTAGGED_MEMBER << shift; 426 else 427 val |= HELLCREEK_VLAN_TAGGED_MEMBER << shift; 428 429 hellcreek_write(hellcreek, val, HR_VIDMBRCFG); 430 hellcreek->vidmbrcfg[vid] = val; 431 432 mutex_unlock(&hellcreek->reg_lock); 433 } 434 435 static void hellcreek_unapply_vlan(struct hellcreek *hellcreek, int port, 436 u16 vid) 437 { 438 int shift, mask; 439 u16 val; 440 441 dev_dbg(hellcreek->dev, "Unapply VLAN: port=%d vid=%u\n", port, vid); 442 443 mutex_lock(&hellcreek->reg_lock); 444 445 hellcreek_select_vlan(hellcreek, vid, false); 446 447 /* Setup port vlan membership */ 448 hellcreek_select_vlan_params(hellcreek, port, &shift, &mask); 449 val = hellcreek->vidmbrcfg[vid]; 450 val &= ~mask; 451 val |= HELLCREEK_VLAN_NO_MEMBER << shift; 452 453 hellcreek_write(hellcreek, val, HR_VIDMBRCFG); 454 hellcreek->vidmbrcfg[vid] = val; 455 456 mutex_unlock(&hellcreek->reg_lock); 457 } 458 459 static int hellcreek_vlan_add(struct dsa_switch *ds, int port, 460 const struct switchdev_obj_port_vlan *vlan, 461 struct netlink_ext_ack *extack) 462 { 463 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 464 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 465 struct hellcreek *hellcreek = ds->priv; 466 int err; 467 468 err = hellcreek_vlan_prepare(ds, port, vlan, extack); 469 if (err) 470 return err; 471 472 dev_dbg(hellcreek->dev, "Add VLAN %d on port %d, %s, %s\n", 473 vlan->vid, port, untagged ? "untagged" : "tagged", 474 pvid ? "PVID" : "no PVID"); 475 476 hellcreek_apply_vlan(hellcreek, port, vlan->vid, pvid, untagged); 477 478 return 0; 479 } 480 481 static int hellcreek_vlan_del(struct dsa_switch *ds, int port, 482 const struct switchdev_obj_port_vlan *vlan) 483 { 484 struct hellcreek *hellcreek = ds->priv; 485 486 dev_dbg(hellcreek->dev, "Remove VLAN %d on port %d\n", vlan->vid, port); 487 488 hellcreek_unapply_vlan(hellcreek, port, vlan->vid); 489 490 return 0; 491 } 492 493 static void hellcreek_port_stp_state_set(struct dsa_switch *ds, int port, 494 u8 state) 495 { 496 struct hellcreek *hellcreek = ds->priv; 497 struct hellcreek_port *hellcreek_port; 498 const char *new_state; 499 u16 val; 500 501 mutex_lock(&hellcreek->reg_lock); 502 503 hellcreek_port = &hellcreek->ports[port]; 504 val = hellcreek_port->ptcfg; 505 506 switch (state) { 507 case BR_STATE_DISABLED: 508 new_state = "DISABLED"; 509 val |= HR_PTCFG_BLOCKED; 510 val &= ~HR_PTCFG_LEARNING_EN; 511 break; 512 case BR_STATE_BLOCKING: 513 new_state = "BLOCKING"; 514 val |= HR_PTCFG_BLOCKED; 515 val &= ~HR_PTCFG_LEARNING_EN; 516 break; 517 case BR_STATE_LISTENING: 518 new_state = "LISTENING"; 519 val |= HR_PTCFG_BLOCKED; 520 val &= ~HR_PTCFG_LEARNING_EN; 521 break; 522 case BR_STATE_LEARNING: 523 new_state = "LEARNING"; 524 val |= HR_PTCFG_BLOCKED; 525 val |= HR_PTCFG_LEARNING_EN; 526 break; 527 case BR_STATE_FORWARDING: 528 new_state = "FORWARDING"; 529 val &= ~HR_PTCFG_BLOCKED; 530 val |= HR_PTCFG_LEARNING_EN; 531 break; 532 default: 533 new_state = "UNKNOWN"; 534 } 535 536 hellcreek_select_port(hellcreek, port); 537 hellcreek_write(hellcreek, val, HR_PTCFG); 538 hellcreek_port->ptcfg = val; 539 540 mutex_unlock(&hellcreek->reg_lock); 541 542 dev_dbg(hellcreek->dev, "Configured STP state for port %d: %s\n", 543 port, new_state); 544 } 545 546 static void hellcreek_setup_ingressflt(struct hellcreek *hellcreek, int port, 547 bool enable) 548 { 549 struct hellcreek_port *hellcreek_port = &hellcreek->ports[port]; 550 u16 ptcfg; 551 552 mutex_lock(&hellcreek->reg_lock); 553 554 ptcfg = hellcreek_port->ptcfg; 555 556 if (enable) 557 ptcfg |= HR_PTCFG_INGRESSFLT; 558 else 559 ptcfg &= ~HR_PTCFG_INGRESSFLT; 560 561 hellcreek_select_port(hellcreek, port); 562 hellcreek_write(hellcreek, ptcfg, HR_PTCFG); 563 hellcreek_port->ptcfg = ptcfg; 564 565 mutex_unlock(&hellcreek->reg_lock); 566 } 567 568 static void hellcreek_setup_vlan_awareness(struct hellcreek *hellcreek, 569 bool enable) 570 { 571 u16 swcfg; 572 573 mutex_lock(&hellcreek->reg_lock); 574 575 swcfg = hellcreek->swcfg; 576 577 if (enable) 578 swcfg |= HR_SWCFG_VLAN_UNAWARE; 579 else 580 swcfg &= ~HR_SWCFG_VLAN_UNAWARE; 581 582 hellcreek_write(hellcreek, swcfg, HR_SWCFG); 583 584 mutex_unlock(&hellcreek->reg_lock); 585 } 586 587 /* Default setup for DSA: VLAN <X>: CPU and Port <X> egress untagged. */ 588 static void hellcreek_setup_vlan_membership(struct dsa_switch *ds, int port, 589 bool enabled) 590 { 591 const u16 vid = hellcreek_private_vid(port); 592 int upstream = dsa_upstream_port(ds, port); 593 struct hellcreek *hellcreek = ds->priv; 594 595 /* Apply vid to port as egress untagged and port vlan id */ 596 if (enabled) 597 hellcreek_apply_vlan(hellcreek, port, vid, true, true); 598 else 599 hellcreek_unapply_vlan(hellcreek, port, vid); 600 601 /* Apply vid to cpu port as well */ 602 if (enabled) 603 hellcreek_apply_vlan(hellcreek, upstream, vid, false, true); 604 else 605 hellcreek_unapply_vlan(hellcreek, upstream, vid); 606 } 607 608 static void hellcreek_port_set_ucast_flood(struct hellcreek *hellcreek, 609 int port, bool enable) 610 { 611 struct hellcreek_port *hellcreek_port; 612 u16 val; 613 614 hellcreek_port = &hellcreek->ports[port]; 615 616 dev_dbg(hellcreek->dev, "%s unicast flooding on port %d\n", 617 enable ? "Enable" : "Disable", port); 618 619 mutex_lock(&hellcreek->reg_lock); 620 621 hellcreek_select_port(hellcreek, port); 622 val = hellcreek_port->ptcfg; 623 if (enable) 624 val &= ~HR_PTCFG_UUC_FLT; 625 else 626 val |= HR_PTCFG_UUC_FLT; 627 hellcreek_write(hellcreek, val, HR_PTCFG); 628 hellcreek_port->ptcfg = val; 629 630 mutex_unlock(&hellcreek->reg_lock); 631 } 632 633 static void hellcreek_port_set_mcast_flood(struct hellcreek *hellcreek, 634 int port, bool enable) 635 { 636 struct hellcreek_port *hellcreek_port; 637 u16 val; 638 639 hellcreek_port = &hellcreek->ports[port]; 640 641 dev_dbg(hellcreek->dev, "%s multicast flooding on port %d\n", 642 enable ? "Enable" : "Disable", port); 643 644 mutex_lock(&hellcreek->reg_lock); 645 646 hellcreek_select_port(hellcreek, port); 647 val = hellcreek_port->ptcfg; 648 if (enable) 649 val &= ~HR_PTCFG_UMC_FLT; 650 else 651 val |= HR_PTCFG_UMC_FLT; 652 hellcreek_write(hellcreek, val, HR_PTCFG); 653 hellcreek_port->ptcfg = val; 654 655 mutex_unlock(&hellcreek->reg_lock); 656 } 657 658 static int hellcreek_pre_bridge_flags(struct dsa_switch *ds, int port, 659 struct switchdev_brport_flags flags, 660 struct netlink_ext_ack *extack) 661 { 662 if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD)) 663 return -EINVAL; 664 665 return 0; 666 } 667 668 static int hellcreek_bridge_flags(struct dsa_switch *ds, int port, 669 struct switchdev_brport_flags flags, 670 struct netlink_ext_ack *extack) 671 { 672 struct hellcreek *hellcreek = ds->priv; 673 674 if (flags.mask & BR_FLOOD) 675 hellcreek_port_set_ucast_flood(hellcreek, port, 676 !!(flags.val & BR_FLOOD)); 677 678 if (flags.mask & BR_MCAST_FLOOD) 679 hellcreek_port_set_mcast_flood(hellcreek, port, 680 !!(flags.val & BR_MCAST_FLOOD)); 681 682 return 0; 683 } 684 685 static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port, 686 struct dsa_bridge bridge, 687 bool *tx_fwd_offload, 688 struct netlink_ext_ack *extack) 689 { 690 struct hellcreek *hellcreek = ds->priv; 691 692 dev_dbg(hellcreek->dev, "Port %d joins a bridge\n", port); 693 694 /* When joining a vlan_filtering bridge, keep the switch VLAN aware */ 695 if (!ds->vlan_filtering) 696 hellcreek_setup_vlan_awareness(hellcreek, false); 697 698 /* Drop private vlans */ 699 hellcreek_setup_vlan_membership(ds, port, false); 700 701 return 0; 702 } 703 704 static void hellcreek_port_bridge_leave(struct dsa_switch *ds, int port, 705 struct dsa_bridge bridge) 706 { 707 struct hellcreek *hellcreek = ds->priv; 708 709 dev_dbg(hellcreek->dev, "Port %d leaves a bridge\n", port); 710 711 /* Enable VLAN awareness */ 712 hellcreek_setup_vlan_awareness(hellcreek, true); 713 714 /* Enable private vlans */ 715 hellcreek_setup_vlan_membership(ds, port, true); 716 } 717 718 static int __hellcreek_fdb_add(struct hellcreek *hellcreek, 719 const struct hellcreek_fdb_entry *entry) 720 { 721 u16 meta = 0; 722 723 dev_dbg(hellcreek->dev, "Add static FDB entry: MAC=%pM, MASK=0x%02x, " 724 "OBT=%d, PASS_BLOCKED=%d, REPRIO_EN=%d, PRIO=%d\n", entry->mac, 725 entry->portmask, entry->is_obt, entry->pass_blocked, 726 entry->reprio_en, entry->reprio_tc); 727 728 /* Add mac address */ 729 hellcreek_write(hellcreek, entry->mac[1] | (entry->mac[0] << 8), HR_FDBWDH); 730 hellcreek_write(hellcreek, entry->mac[3] | (entry->mac[2] << 8), HR_FDBWDM); 731 hellcreek_write(hellcreek, entry->mac[5] | (entry->mac[4] << 8), HR_FDBWDL); 732 733 /* Meta data */ 734 meta |= entry->portmask << HR_FDBWRM0_PORTMASK_SHIFT; 735 if (entry->is_obt) 736 meta |= HR_FDBWRM0_OBT; 737 if (entry->pass_blocked) 738 meta |= HR_FDBWRM0_PASS_BLOCKED; 739 if (entry->reprio_en) { 740 meta |= HR_FDBWRM0_REPRIO_EN; 741 meta |= entry->reprio_tc << HR_FDBWRM0_REPRIO_TC_SHIFT; 742 } 743 hellcreek_write(hellcreek, meta, HR_FDBWRM0); 744 745 /* Commit */ 746 hellcreek_write(hellcreek, 0x00, HR_FDBWRCMD); 747 748 /* Wait until done */ 749 return hellcreek_wait_fdb_ready(hellcreek); 750 } 751 752 static int __hellcreek_fdb_del(struct hellcreek *hellcreek, 753 const struct hellcreek_fdb_entry *entry) 754 { 755 dev_dbg(hellcreek->dev, "Delete FDB entry: MAC=%pM!\n", entry->mac); 756 757 /* Delete by matching idx */ 758 hellcreek_write(hellcreek, entry->idx | HR_FDBWRCMD_FDBDEL, HR_FDBWRCMD); 759 760 /* Wait until done */ 761 return hellcreek_wait_fdb_ready(hellcreek); 762 } 763 764 static void hellcreek_populate_fdb_entry(struct hellcreek *hellcreek, 765 struct hellcreek_fdb_entry *entry, 766 size_t idx) 767 { 768 unsigned char addr[ETH_ALEN]; 769 u16 meta, mac; 770 771 /* Read values */ 772 meta = hellcreek_read(hellcreek, HR_FDBMDRD); 773 mac = hellcreek_read(hellcreek, HR_FDBRDL); 774 addr[5] = mac & 0xff; 775 addr[4] = (mac & 0xff00) >> 8; 776 mac = hellcreek_read(hellcreek, HR_FDBRDM); 777 addr[3] = mac & 0xff; 778 addr[2] = (mac & 0xff00) >> 8; 779 mac = hellcreek_read(hellcreek, HR_FDBRDH); 780 addr[1] = mac & 0xff; 781 addr[0] = (mac & 0xff00) >> 8; 782 783 /* Populate @entry */ 784 memcpy(entry->mac, addr, sizeof(addr)); 785 entry->idx = idx; 786 entry->portmask = (meta & HR_FDBMDRD_PORTMASK_MASK) >> 787 HR_FDBMDRD_PORTMASK_SHIFT; 788 entry->age = (meta & HR_FDBMDRD_AGE_MASK) >> 789 HR_FDBMDRD_AGE_SHIFT; 790 entry->is_obt = !!(meta & HR_FDBMDRD_OBT); 791 entry->pass_blocked = !!(meta & HR_FDBMDRD_PASS_BLOCKED); 792 entry->is_static = !!(meta & HR_FDBMDRD_STATIC); 793 entry->reprio_tc = (meta & HR_FDBMDRD_REPRIO_TC_MASK) >> 794 HR_FDBMDRD_REPRIO_TC_SHIFT; 795 entry->reprio_en = !!(meta & HR_FDBMDRD_REPRIO_EN); 796 } 797 798 /* Retrieve the index of a FDB entry by mac address. Currently we search through 799 * the complete table in hardware. If that's too slow, we might have to cache 800 * the complete FDB table in software. 801 */ 802 static int hellcreek_fdb_get(struct hellcreek *hellcreek, 803 const unsigned char *dest, 804 struct hellcreek_fdb_entry *entry) 805 { 806 size_t i; 807 808 /* Set read pointer to zero: The read of HR_FDBMAX (read-only register) 809 * should reset the internal pointer. But, that doesn't work. The vendor 810 * suggested a subsequent write as workaround. Same for HR_FDBRDH below. 811 */ 812 hellcreek_read(hellcreek, HR_FDBMAX); 813 hellcreek_write(hellcreek, 0x00, HR_FDBMAX); 814 815 /* We have to read the complete table, because the switch/driver might 816 * enter new entries anywhere. 817 */ 818 for (i = 0; i < hellcreek->fdb_entries; ++i) { 819 struct hellcreek_fdb_entry tmp = { 0 }; 820 821 /* Read entry */ 822 hellcreek_populate_fdb_entry(hellcreek, &tmp, i); 823 824 /* Force next entry */ 825 hellcreek_write(hellcreek, 0x00, HR_FDBRDH); 826 827 if (memcmp(tmp.mac, dest, ETH_ALEN)) 828 continue; 829 830 /* Match found */ 831 memcpy(entry, &tmp, sizeof(*entry)); 832 833 return 0; 834 } 835 836 return -ENOENT; 837 } 838 839 static int hellcreek_fdb_add(struct dsa_switch *ds, int port, 840 const unsigned char *addr, u16 vid, 841 struct dsa_db db) 842 { 843 struct hellcreek_fdb_entry entry = { 0 }; 844 struct hellcreek *hellcreek = ds->priv; 845 int ret; 846 847 dev_dbg(hellcreek->dev, "Add FDB entry for MAC=%pM\n", addr); 848 849 mutex_lock(&hellcreek->reg_lock); 850 851 ret = hellcreek_fdb_get(hellcreek, addr, &entry); 852 if (ret) { 853 /* Not found */ 854 memcpy(entry.mac, addr, sizeof(entry.mac)); 855 entry.portmask = BIT(port); 856 857 ret = __hellcreek_fdb_add(hellcreek, &entry); 858 if (ret) { 859 dev_err(hellcreek->dev, "Failed to add FDB entry!\n"); 860 goto out; 861 } 862 } else { 863 /* Found */ 864 ret = __hellcreek_fdb_del(hellcreek, &entry); 865 if (ret) { 866 dev_err(hellcreek->dev, "Failed to delete FDB entry!\n"); 867 goto out; 868 } 869 870 entry.portmask |= BIT(port); 871 872 ret = __hellcreek_fdb_add(hellcreek, &entry); 873 if (ret) { 874 dev_err(hellcreek->dev, "Failed to add FDB entry!\n"); 875 goto out; 876 } 877 } 878 879 out: 880 mutex_unlock(&hellcreek->reg_lock); 881 882 return ret; 883 } 884 885 static int hellcreek_fdb_del(struct dsa_switch *ds, int port, 886 const unsigned char *addr, u16 vid, 887 struct dsa_db db) 888 { 889 struct hellcreek_fdb_entry entry = { 0 }; 890 struct hellcreek *hellcreek = ds->priv; 891 int ret; 892 893 dev_dbg(hellcreek->dev, "Delete FDB entry for MAC=%pM\n", addr); 894 895 mutex_lock(&hellcreek->reg_lock); 896 897 ret = hellcreek_fdb_get(hellcreek, addr, &entry); 898 if (ret) { 899 /* Not found */ 900 dev_err(hellcreek->dev, "FDB entry for deletion not found!\n"); 901 } else { 902 /* Found */ 903 ret = __hellcreek_fdb_del(hellcreek, &entry); 904 if (ret) { 905 dev_err(hellcreek->dev, "Failed to delete FDB entry!\n"); 906 goto out; 907 } 908 909 entry.portmask &= ~BIT(port); 910 911 if (entry.portmask != 0x00) { 912 ret = __hellcreek_fdb_add(hellcreek, &entry); 913 if (ret) { 914 dev_err(hellcreek->dev, "Failed to add FDB entry!\n"); 915 goto out; 916 } 917 } 918 } 919 920 out: 921 mutex_unlock(&hellcreek->reg_lock); 922 923 return ret; 924 } 925 926 static int hellcreek_fdb_dump(struct dsa_switch *ds, int port, 927 dsa_fdb_dump_cb_t *cb, void *data) 928 { 929 struct hellcreek *hellcreek = ds->priv; 930 u16 entries; 931 int ret = 0; 932 size_t i; 933 934 mutex_lock(&hellcreek->reg_lock); 935 936 /* Set read pointer to zero: The read of HR_FDBMAX (read-only register) 937 * should reset the internal pointer. But, that doesn't work. The vendor 938 * suggested a subsequent write as workaround. Same for HR_FDBRDH below. 939 */ 940 entries = hellcreek_read(hellcreek, HR_FDBMAX); 941 hellcreek_write(hellcreek, 0x00, HR_FDBMAX); 942 943 dev_dbg(hellcreek->dev, "FDB dump for port %d, entries=%d!\n", port, entries); 944 945 /* Read table */ 946 for (i = 0; i < hellcreek->fdb_entries; ++i) { 947 struct hellcreek_fdb_entry entry = { 0 }; 948 949 /* Read entry */ 950 hellcreek_populate_fdb_entry(hellcreek, &entry, i); 951 952 /* Force next entry */ 953 hellcreek_write(hellcreek, 0x00, HR_FDBRDH); 954 955 /* Check valid */ 956 if (is_zero_ether_addr(entry.mac)) 957 continue; 958 959 /* Check port mask */ 960 if (!(entry.portmask & BIT(port))) 961 continue; 962 963 ret = cb(entry.mac, 0, entry.is_static, data); 964 if (ret) 965 break; 966 } 967 968 mutex_unlock(&hellcreek->reg_lock); 969 970 return ret; 971 } 972 973 static int hellcreek_vlan_filtering(struct dsa_switch *ds, int port, 974 bool vlan_filtering, 975 struct netlink_ext_ack *extack) 976 { 977 struct hellcreek *hellcreek = ds->priv; 978 979 dev_dbg(hellcreek->dev, "%s VLAN filtering on port %d\n", 980 vlan_filtering ? "Enable" : "Disable", port); 981 982 /* Configure port to drop packages with not known vids */ 983 hellcreek_setup_ingressflt(hellcreek, port, vlan_filtering); 984 985 /* Enable VLAN awareness on the switch. This save due to 986 * ds->vlan_filtering_is_global. 987 */ 988 hellcreek_setup_vlan_awareness(hellcreek, vlan_filtering); 989 990 return 0; 991 } 992 993 static int hellcreek_enable_ip_core(struct hellcreek *hellcreek) 994 { 995 int ret; 996 u16 val; 997 998 mutex_lock(&hellcreek->reg_lock); 999 1000 val = hellcreek_read(hellcreek, HR_CTRL_C); 1001 val |= HR_CTRL_C_ENABLE; 1002 hellcreek_write(hellcreek, val, HR_CTRL_C); 1003 ret = hellcreek_wait_until_transitioned(hellcreek); 1004 1005 mutex_unlock(&hellcreek->reg_lock); 1006 1007 return ret; 1008 } 1009 1010 static void hellcreek_setup_cpu_and_tunnel_port(struct hellcreek *hellcreek) 1011 { 1012 struct hellcreek_port *tunnel_port = &hellcreek->ports[TUNNEL_PORT]; 1013 struct hellcreek_port *cpu_port = &hellcreek->ports[CPU_PORT]; 1014 u16 ptcfg = 0; 1015 1016 ptcfg |= HR_PTCFG_LEARNING_EN | HR_PTCFG_ADMIN_EN; 1017 1018 mutex_lock(&hellcreek->reg_lock); 1019 1020 hellcreek_select_port(hellcreek, CPU_PORT); 1021 hellcreek_write(hellcreek, ptcfg, HR_PTCFG); 1022 1023 hellcreek_select_port(hellcreek, TUNNEL_PORT); 1024 hellcreek_write(hellcreek, ptcfg, HR_PTCFG); 1025 1026 cpu_port->ptcfg = ptcfg; 1027 tunnel_port->ptcfg = ptcfg; 1028 1029 mutex_unlock(&hellcreek->reg_lock); 1030 } 1031 1032 static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek) 1033 { 1034 int i; 1035 1036 /* The switch has multiple egress queues per port. The queue is selected 1037 * via the PCP field in the VLAN header. The switch internally deals 1038 * with traffic classes instead of PCP values and this mapping is 1039 * configurable. 1040 * 1041 * The default mapping is (PCP - TC): 1042 * 7 - 7 1043 * 6 - 6 1044 * 5 - 5 1045 * 4 - 4 1046 * 3 - 3 1047 * 2 - 1 1048 * 1 - 0 1049 * 0 - 2 1050 * 1051 * The default should be an identity mapping. 1052 */ 1053 1054 for (i = 0; i < 8; ++i) { 1055 mutex_lock(&hellcreek->reg_lock); 1056 1057 hellcreek_select_prio(hellcreek, i); 1058 hellcreek_write(hellcreek, 1059 i << HR_PRTCCFG_PCP_TC_MAP_SHIFT, 1060 HR_PRTCCFG); 1061 1062 mutex_unlock(&hellcreek->reg_lock); 1063 } 1064 } 1065 1066 static int hellcreek_setup_fdb(struct hellcreek *hellcreek) 1067 { 1068 static struct hellcreek_fdb_entry l2_ptp = { 1069 /* MAC: 01-1B-19-00-00-00 */ 1070 .mac = { 0x01, 0x1b, 0x19, 0x00, 0x00, 0x00 }, 1071 .portmask = 0x03, /* Management ports */ 1072 .age = 0, 1073 .is_obt = 0, 1074 .pass_blocked = 0, 1075 .is_static = 1, 1076 .reprio_tc = 6, /* TC: 6 as per IEEE 802.1AS */ 1077 .reprio_en = 1, 1078 }; 1079 static struct hellcreek_fdb_entry udp4_ptp = { 1080 /* MAC: 01-00-5E-00-01-81 */ 1081 .mac = { 0x01, 0x00, 0x5e, 0x00, 0x01, 0x81 }, 1082 .portmask = 0x03, /* Management ports */ 1083 .age = 0, 1084 .is_obt = 0, 1085 .pass_blocked = 0, 1086 .is_static = 1, 1087 .reprio_tc = 6, 1088 .reprio_en = 1, 1089 }; 1090 static struct hellcreek_fdb_entry udp6_ptp = { 1091 /* MAC: 33-33-00-00-01-81 */ 1092 .mac = { 0x33, 0x33, 0x00, 0x00, 0x01, 0x81 }, 1093 .portmask = 0x03, /* Management ports */ 1094 .age = 0, 1095 .is_obt = 0, 1096 .pass_blocked = 0, 1097 .is_static = 1, 1098 .reprio_tc = 6, 1099 .reprio_en = 1, 1100 }; 1101 static struct hellcreek_fdb_entry l2_p2p = { 1102 /* MAC: 01-80-C2-00-00-0E */ 1103 .mac = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }, 1104 .portmask = 0x03, /* Management ports */ 1105 .age = 0, 1106 .is_obt = 0, 1107 .pass_blocked = 1, 1108 .is_static = 1, 1109 .reprio_tc = 6, /* TC: 6 as per IEEE 802.1AS */ 1110 .reprio_en = 1, 1111 }; 1112 static struct hellcreek_fdb_entry udp4_p2p = { 1113 /* MAC: 01-00-5E-00-00-6B */ 1114 .mac = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x6b }, 1115 .portmask = 0x03, /* Management ports */ 1116 .age = 0, 1117 .is_obt = 0, 1118 .pass_blocked = 1, 1119 .is_static = 1, 1120 .reprio_tc = 6, 1121 .reprio_en = 1, 1122 }; 1123 static struct hellcreek_fdb_entry udp6_p2p = { 1124 /* MAC: 33-33-00-00-00-6B */ 1125 .mac = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x6b }, 1126 .portmask = 0x03, /* Management ports */ 1127 .age = 0, 1128 .is_obt = 0, 1129 .pass_blocked = 1, 1130 .is_static = 1, 1131 .reprio_tc = 6, 1132 .reprio_en = 1, 1133 }; 1134 static struct hellcreek_fdb_entry stp = { 1135 /* MAC: 01-80-C2-00-00-00 */ 1136 .mac = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }, 1137 .portmask = 0x03, /* Management ports */ 1138 .age = 0, 1139 .is_obt = 0, 1140 .pass_blocked = 1, 1141 .is_static = 1, 1142 .reprio_tc = 6, 1143 .reprio_en = 1, 1144 }; 1145 int ret; 1146 1147 mutex_lock(&hellcreek->reg_lock); 1148 ret = __hellcreek_fdb_add(hellcreek, &l2_ptp); 1149 if (ret) 1150 goto out; 1151 ret = __hellcreek_fdb_add(hellcreek, &udp4_ptp); 1152 if (ret) 1153 goto out; 1154 ret = __hellcreek_fdb_add(hellcreek, &udp6_ptp); 1155 if (ret) 1156 goto out; 1157 ret = __hellcreek_fdb_add(hellcreek, &l2_p2p); 1158 if (ret) 1159 goto out; 1160 ret = __hellcreek_fdb_add(hellcreek, &udp4_p2p); 1161 if (ret) 1162 goto out; 1163 ret = __hellcreek_fdb_add(hellcreek, &udp6_p2p); 1164 if (ret) 1165 goto out; 1166 ret = __hellcreek_fdb_add(hellcreek, &stp); 1167 out: 1168 mutex_unlock(&hellcreek->reg_lock); 1169 1170 return ret; 1171 } 1172 1173 static int hellcreek_devlink_info_get(struct dsa_switch *ds, 1174 struct devlink_info_req *req, 1175 struct netlink_ext_ack *extack) 1176 { 1177 struct hellcreek *hellcreek = ds->priv; 1178 1179 return devlink_info_version_fixed_put(req, 1180 DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, 1181 hellcreek->pdata->name); 1182 } 1183 1184 static u64 hellcreek_devlink_vlan_table_get(void *priv) 1185 { 1186 struct hellcreek *hellcreek = priv; 1187 u64 count = 0; 1188 int i; 1189 1190 mutex_lock(&hellcreek->reg_lock); 1191 for (i = 0; i < VLAN_N_VID; ++i) 1192 if (hellcreek->vidmbrcfg[i]) 1193 count++; 1194 mutex_unlock(&hellcreek->reg_lock); 1195 1196 return count; 1197 } 1198 1199 static u64 hellcreek_devlink_fdb_table_get(void *priv) 1200 { 1201 struct hellcreek *hellcreek = priv; 1202 u64 count = 0; 1203 1204 /* Reading this register has side effects. Synchronize against the other 1205 * FDB operations. 1206 */ 1207 mutex_lock(&hellcreek->reg_lock); 1208 count = hellcreek_read(hellcreek, HR_FDBMAX); 1209 mutex_unlock(&hellcreek->reg_lock); 1210 1211 return count; 1212 } 1213 1214 static int hellcreek_setup_devlink_resources(struct dsa_switch *ds) 1215 { 1216 struct devlink_resource_size_params size_vlan_params; 1217 struct devlink_resource_size_params size_fdb_params; 1218 struct hellcreek *hellcreek = ds->priv; 1219 int err; 1220 1221 devlink_resource_size_params_init(&size_vlan_params, VLAN_N_VID, 1222 VLAN_N_VID, 1223 1, DEVLINK_RESOURCE_UNIT_ENTRY); 1224 1225 devlink_resource_size_params_init(&size_fdb_params, 1226 hellcreek->fdb_entries, 1227 hellcreek->fdb_entries, 1228 1, DEVLINK_RESOURCE_UNIT_ENTRY); 1229 1230 err = dsa_devlink_resource_register(ds, "VLAN", VLAN_N_VID, 1231 HELLCREEK_DEVLINK_PARAM_ID_VLAN_TABLE, 1232 DEVLINK_RESOURCE_ID_PARENT_TOP, 1233 &size_vlan_params); 1234 if (err) 1235 goto out; 1236 1237 err = dsa_devlink_resource_register(ds, "FDB", hellcreek->fdb_entries, 1238 HELLCREEK_DEVLINK_PARAM_ID_FDB_TABLE, 1239 DEVLINK_RESOURCE_ID_PARENT_TOP, 1240 &size_fdb_params); 1241 if (err) 1242 goto out; 1243 1244 dsa_devlink_resource_occ_get_register(ds, 1245 HELLCREEK_DEVLINK_PARAM_ID_VLAN_TABLE, 1246 hellcreek_devlink_vlan_table_get, 1247 hellcreek); 1248 1249 dsa_devlink_resource_occ_get_register(ds, 1250 HELLCREEK_DEVLINK_PARAM_ID_FDB_TABLE, 1251 hellcreek_devlink_fdb_table_get, 1252 hellcreek); 1253 1254 return 0; 1255 1256 out: 1257 dsa_devlink_resources_unregister(ds); 1258 1259 return err; 1260 } 1261 1262 static int hellcreek_devlink_region_vlan_snapshot(struct devlink *dl, 1263 const struct devlink_region_ops *ops, 1264 struct netlink_ext_ack *extack, 1265 u8 **data) 1266 { 1267 struct hellcreek_devlink_vlan_entry *table, *entry; 1268 struct dsa_switch *ds = dsa_devlink_to_ds(dl); 1269 struct hellcreek *hellcreek = ds->priv; 1270 int i; 1271 1272 table = kcalloc(VLAN_N_VID, sizeof(*entry), GFP_KERNEL); 1273 if (!table) 1274 return -ENOMEM; 1275 1276 entry = table; 1277 1278 mutex_lock(&hellcreek->reg_lock); 1279 for (i = 0; i < VLAN_N_VID; ++i, ++entry) { 1280 entry->member = hellcreek->vidmbrcfg[i]; 1281 entry->vid = i; 1282 } 1283 mutex_unlock(&hellcreek->reg_lock); 1284 1285 *data = (u8 *)table; 1286 1287 return 0; 1288 } 1289 1290 static int hellcreek_devlink_region_fdb_snapshot(struct devlink *dl, 1291 const struct devlink_region_ops *ops, 1292 struct netlink_ext_ack *extack, 1293 u8 **data) 1294 { 1295 struct dsa_switch *ds = dsa_devlink_to_ds(dl); 1296 struct hellcreek_fdb_entry *table, *entry; 1297 struct hellcreek *hellcreek = ds->priv; 1298 size_t i; 1299 1300 table = kcalloc(hellcreek->fdb_entries, sizeof(*entry), GFP_KERNEL); 1301 if (!table) 1302 return -ENOMEM; 1303 1304 entry = table; 1305 1306 mutex_lock(&hellcreek->reg_lock); 1307 1308 /* Start table read */ 1309 hellcreek_read(hellcreek, HR_FDBMAX); 1310 hellcreek_write(hellcreek, 0x00, HR_FDBMAX); 1311 1312 for (i = 0; i < hellcreek->fdb_entries; ++i, ++entry) { 1313 /* Read current entry */ 1314 hellcreek_populate_fdb_entry(hellcreek, entry, i); 1315 1316 /* Advance read pointer */ 1317 hellcreek_write(hellcreek, 0x00, HR_FDBRDH); 1318 } 1319 1320 mutex_unlock(&hellcreek->reg_lock); 1321 1322 *data = (u8 *)table; 1323 1324 return 0; 1325 } 1326 1327 static struct devlink_region_ops hellcreek_region_vlan_ops = { 1328 .name = "vlan", 1329 .snapshot = hellcreek_devlink_region_vlan_snapshot, 1330 .destructor = kfree, 1331 }; 1332 1333 static struct devlink_region_ops hellcreek_region_fdb_ops = { 1334 .name = "fdb", 1335 .snapshot = hellcreek_devlink_region_fdb_snapshot, 1336 .destructor = kfree, 1337 }; 1338 1339 static int hellcreek_setup_devlink_regions(struct dsa_switch *ds) 1340 { 1341 struct hellcreek *hellcreek = ds->priv; 1342 struct devlink_region_ops *ops; 1343 struct devlink_region *region; 1344 u64 size; 1345 int ret; 1346 1347 /* VLAN table */ 1348 size = VLAN_N_VID * sizeof(struct hellcreek_devlink_vlan_entry); 1349 ops = &hellcreek_region_vlan_ops; 1350 1351 region = dsa_devlink_region_create(ds, ops, 1, size); 1352 if (IS_ERR(region)) 1353 return PTR_ERR(region); 1354 1355 hellcreek->vlan_region = region; 1356 1357 /* FDB table */ 1358 size = hellcreek->fdb_entries * sizeof(struct hellcreek_fdb_entry); 1359 ops = &hellcreek_region_fdb_ops; 1360 1361 region = dsa_devlink_region_create(ds, ops, 1, size); 1362 if (IS_ERR(region)) { 1363 ret = PTR_ERR(region); 1364 goto err_fdb; 1365 } 1366 1367 hellcreek->fdb_region = region; 1368 1369 return 0; 1370 1371 err_fdb: 1372 dsa_devlink_region_destroy(hellcreek->vlan_region); 1373 1374 return ret; 1375 } 1376 1377 static void hellcreek_teardown_devlink_regions(struct dsa_switch *ds) 1378 { 1379 struct hellcreek *hellcreek = ds->priv; 1380 1381 dsa_devlink_region_destroy(hellcreek->fdb_region); 1382 dsa_devlink_region_destroy(hellcreek->vlan_region); 1383 } 1384 1385 static int hellcreek_setup(struct dsa_switch *ds) 1386 { 1387 struct hellcreek *hellcreek = ds->priv; 1388 u16 swcfg = 0; 1389 int ret, i; 1390 1391 dev_dbg(hellcreek->dev, "Set up the switch\n"); 1392 1393 /* Let's go */ 1394 ret = hellcreek_enable_ip_core(hellcreek); 1395 if (ret) { 1396 dev_err(hellcreek->dev, "Failed to enable IP core!\n"); 1397 return ret; 1398 } 1399 1400 /* Enable CPU/Tunnel ports */ 1401 hellcreek_setup_cpu_and_tunnel_port(hellcreek); 1402 1403 /* Switch config: Keep defaults, enable FDB aging and learning and tag 1404 * each frame from/to cpu port for DSA tagging. Also enable the length 1405 * aware shaping mode. This eliminates the need for Qbv guard bands. 1406 */ 1407 swcfg |= HR_SWCFG_FDBAGE_EN | 1408 HR_SWCFG_FDBLRN_EN | 1409 HR_SWCFG_ALWAYS_OBT | 1410 (HR_SWCFG_LAS_ON << HR_SWCFG_LAS_MODE_SHIFT); 1411 hellcreek->swcfg = swcfg; 1412 hellcreek_write(hellcreek, swcfg, HR_SWCFG); 1413 1414 /* Initial vlan membership to reflect port separation */ 1415 for (i = 0; i < ds->num_ports; ++i) { 1416 if (!dsa_is_user_port(ds, i)) 1417 continue; 1418 1419 hellcreek_setup_vlan_membership(ds, i, true); 1420 } 1421 1422 /* Configure PCP <-> TC mapping */ 1423 hellcreek_setup_tc_identity_mapping(hellcreek); 1424 1425 /* The VLAN awareness is a global switch setting. Therefore, mixed vlan 1426 * filtering setups are not supported. 1427 */ 1428 ds->vlan_filtering_is_global = true; 1429 ds->needs_standalone_vlan_filtering = true; 1430 1431 /* Intercept _all_ PTP multicast traffic */ 1432 ret = hellcreek_setup_fdb(hellcreek); 1433 if (ret) { 1434 dev_err(hellcreek->dev, 1435 "Failed to insert static PTP FDB entries\n"); 1436 return ret; 1437 } 1438 1439 /* Register devlink resources with DSA */ 1440 ret = hellcreek_setup_devlink_resources(ds); 1441 if (ret) { 1442 dev_err(hellcreek->dev, 1443 "Failed to setup devlink resources!\n"); 1444 return ret; 1445 } 1446 1447 ret = hellcreek_setup_devlink_regions(ds); 1448 if (ret) { 1449 dev_err(hellcreek->dev, 1450 "Failed to setup devlink regions!\n"); 1451 goto err_regions; 1452 } 1453 1454 return 0; 1455 1456 err_regions: 1457 dsa_devlink_resources_unregister(ds); 1458 1459 return ret; 1460 } 1461 1462 static void hellcreek_teardown(struct dsa_switch *ds) 1463 { 1464 hellcreek_teardown_devlink_regions(ds); 1465 dsa_devlink_resources_unregister(ds); 1466 } 1467 1468 static void hellcreek_phylink_get_caps(struct dsa_switch *ds, int port, 1469 struct phylink_config *config) 1470 { 1471 struct hellcreek *hellcreek = ds->priv; 1472 1473 __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); 1474 __set_bit(PHY_INTERFACE_MODE_RGMII, config->supported_interfaces); 1475 1476 /* Include GMII - the hardware does not support this interface 1477 * mode, but it's the default interface mode for phylib, so we 1478 * need it for compatibility with existing DT. 1479 */ 1480 __set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces); 1481 1482 /* The MAC settings are a hardware configuration option and cannot be 1483 * changed at run time or by strapping. Therefore the attached PHYs 1484 * should be programmed to only advertise settings which are supported 1485 * by the hardware. 1486 */ 1487 if (hellcreek->pdata->is_100_mbits) 1488 config->mac_capabilities = MAC_100FD; 1489 else 1490 config->mac_capabilities = MAC_1000FD; 1491 } 1492 1493 static int 1494 hellcreek_port_prechangeupper(struct dsa_switch *ds, int port, 1495 struct netdev_notifier_changeupper_info *info) 1496 { 1497 struct hellcreek *hellcreek = ds->priv; 1498 bool used = true; 1499 int ret = -EBUSY; 1500 u16 vid; 1501 int i; 1502 1503 dev_dbg(hellcreek->dev, "Pre change upper for port %d\n", port); 1504 1505 /* 1506 * Deny VLAN devices on top of lan ports with the same VLAN ids, because 1507 * it breaks the port separation due to the private VLANs. Example: 1508 * 1509 * lan0.100 *and* lan1.100 cannot be used in parallel. However, lan0.99 1510 * and lan1.100 works. 1511 */ 1512 1513 if (!is_vlan_dev(info->upper_dev)) 1514 return 0; 1515 1516 vid = vlan_dev_vlan_id(info->upper_dev); 1517 1518 /* For all ports, check bitmaps */ 1519 mutex_lock(&hellcreek->vlan_lock); 1520 for (i = 0; i < hellcreek->pdata->num_ports; ++i) { 1521 if (!dsa_is_user_port(ds, i)) 1522 continue; 1523 1524 if (port == i) 1525 continue; 1526 1527 used = used && test_bit(vid, hellcreek->ports[i].vlan_dev_bitmap); 1528 } 1529 1530 if (used) 1531 goto out; 1532 1533 /* Update bitmap */ 1534 set_bit(vid, hellcreek->ports[port].vlan_dev_bitmap); 1535 1536 ret = 0; 1537 1538 out: 1539 mutex_unlock(&hellcreek->vlan_lock); 1540 1541 return ret; 1542 } 1543 1544 static void hellcreek_setup_maxsdu(struct hellcreek *hellcreek, int port, 1545 const struct tc_taprio_qopt_offload *schedule) 1546 { 1547 int tc; 1548 1549 for (tc = 0; tc < 8; ++tc) { 1550 u32 max_sdu = schedule->max_sdu[tc] + VLAN_ETH_HLEN - ETH_FCS_LEN; 1551 u16 val; 1552 1553 if (!schedule->max_sdu[tc]) 1554 continue; 1555 1556 dev_dbg(hellcreek->dev, "Configure max-sdu %u for tc %d on port %d\n", 1557 max_sdu, tc, port); 1558 1559 hellcreek_select_port_prio(hellcreek, port, tc); 1560 1561 val = (max_sdu & HR_PTPRTCCFG_MAXSDU_MASK) << HR_PTPRTCCFG_MAXSDU_SHIFT; 1562 1563 hellcreek_write(hellcreek, val, HR_PTPRTCCFG); 1564 } 1565 } 1566 1567 static void hellcreek_reset_maxsdu(struct hellcreek *hellcreek, int port) 1568 { 1569 int tc; 1570 1571 for (tc = 0; tc < 8; ++tc) { 1572 u16 val; 1573 1574 hellcreek_select_port_prio(hellcreek, port, tc); 1575 1576 val = (HELLCREEK_DEFAULT_MAX_SDU & HR_PTPRTCCFG_MAXSDU_MASK) 1577 << HR_PTPRTCCFG_MAXSDU_SHIFT; 1578 1579 hellcreek_write(hellcreek, val, HR_PTPRTCCFG); 1580 } 1581 } 1582 1583 static void hellcreek_setup_gcl(struct hellcreek *hellcreek, int port, 1584 const struct tc_taprio_qopt_offload *schedule) 1585 { 1586 const struct tc_taprio_sched_entry *cur, *initial, *next; 1587 size_t i; 1588 1589 cur = initial = &schedule->entries[0]; 1590 next = cur + 1; 1591 1592 for (i = 1; i <= schedule->num_entries; ++i) { 1593 u16 data; 1594 u8 gates; 1595 1596 if (i == schedule->num_entries) 1597 gates = initial->gate_mask ^ 1598 cur->gate_mask; 1599 else 1600 gates = next->gate_mask ^ 1601 cur->gate_mask; 1602 1603 data = gates; 1604 1605 if (i == schedule->num_entries) 1606 data |= TR_GCLDAT_GCLWRLAST; 1607 1608 /* Gates states */ 1609 hellcreek_write(hellcreek, data, TR_GCLDAT); 1610 1611 /* Time interval */ 1612 hellcreek_write(hellcreek, 1613 cur->interval & 0x0000ffff, 1614 TR_GCLTIL); 1615 hellcreek_write(hellcreek, 1616 (cur->interval & 0xffff0000) >> 16, 1617 TR_GCLTIH); 1618 1619 /* Commit entry */ 1620 data = ((i - 1) << TR_GCLCMD_GCLWRADR_SHIFT) | 1621 (initial->gate_mask << 1622 TR_GCLCMD_INIT_GATE_STATES_SHIFT); 1623 hellcreek_write(hellcreek, data, TR_GCLCMD); 1624 1625 cur++; 1626 next++; 1627 } 1628 } 1629 1630 static void hellcreek_set_cycle_time(struct hellcreek *hellcreek, 1631 const struct tc_taprio_qopt_offload *schedule) 1632 { 1633 u32 cycle_time = schedule->cycle_time; 1634 1635 hellcreek_write(hellcreek, cycle_time & 0x0000ffff, TR_CTWRL); 1636 hellcreek_write(hellcreek, (cycle_time & 0xffff0000) >> 16, TR_CTWRH); 1637 } 1638 1639 static void hellcreek_switch_schedule(struct hellcreek *hellcreek, 1640 ktime_t start_time) 1641 { 1642 struct timespec64 ts = ktime_to_timespec64(start_time); 1643 1644 /* Start schedule at this point of time */ 1645 hellcreek_write(hellcreek, ts.tv_nsec & 0x0000ffff, TR_ESTWRL); 1646 hellcreek_write(hellcreek, (ts.tv_nsec & 0xffff0000) >> 16, TR_ESTWRH); 1647 1648 /* Arm timer, set seconds and switch schedule */ 1649 hellcreek_write(hellcreek, TR_ESTCMD_ESTARM | TR_ESTCMD_ESTSWCFG | 1650 ((ts.tv_sec & TR_ESTCMD_ESTSEC_MASK) << 1651 TR_ESTCMD_ESTSEC_SHIFT), TR_ESTCMD); 1652 } 1653 1654 static bool hellcreek_schedule_startable(struct hellcreek *hellcreek, int port) 1655 { 1656 struct hellcreek_port *hellcreek_port = &hellcreek->ports[port]; 1657 s64 base_time_ns, current_ns; 1658 1659 /* The switch allows a schedule to be started only eight seconds within 1660 * the future. Therefore, check the current PTP time if the schedule is 1661 * startable or not. 1662 */ 1663 1664 /* Use the "cached" time. That should be alright, as it's updated quite 1665 * frequently in the PTP code. 1666 */ 1667 mutex_lock(&hellcreek->ptp_lock); 1668 current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts; 1669 mutex_unlock(&hellcreek->ptp_lock); 1670 1671 /* Calculate difference to admin base time */ 1672 base_time_ns = ktime_to_ns(hellcreek_port->current_schedule->base_time); 1673 1674 return base_time_ns - current_ns < (s64)4 * NSEC_PER_SEC; 1675 } 1676 1677 static void hellcreek_start_schedule(struct hellcreek *hellcreek, int port) 1678 { 1679 struct hellcreek_port *hellcreek_port = &hellcreek->ports[port]; 1680 ktime_t base_time, current_time; 1681 s64 current_ns; 1682 u32 cycle_time; 1683 1684 /* First select port */ 1685 hellcreek_select_tgd(hellcreek, port); 1686 1687 /* Forward base time into the future if needed */ 1688 mutex_lock(&hellcreek->ptp_lock); 1689 current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts; 1690 mutex_unlock(&hellcreek->ptp_lock); 1691 1692 current_time = ns_to_ktime(current_ns); 1693 base_time = hellcreek_port->current_schedule->base_time; 1694 cycle_time = hellcreek_port->current_schedule->cycle_time; 1695 1696 if (ktime_compare(current_time, base_time) > 0) { 1697 s64 n; 1698 1699 n = div64_s64(ktime_sub_ns(current_time, base_time), 1700 cycle_time); 1701 base_time = ktime_add_ns(base_time, (n + 1) * cycle_time); 1702 } 1703 1704 /* Set admin base time and switch schedule */ 1705 hellcreek_switch_schedule(hellcreek, base_time); 1706 1707 taprio_offload_free(hellcreek_port->current_schedule); 1708 hellcreek_port->current_schedule = NULL; 1709 1710 dev_dbg(hellcreek->dev, "Armed EST timer for port %d\n", 1711 hellcreek_port->port); 1712 } 1713 1714 static void hellcreek_check_schedule(struct work_struct *work) 1715 { 1716 struct delayed_work *dw = to_delayed_work(work); 1717 struct hellcreek_port *hellcreek_port; 1718 struct hellcreek *hellcreek; 1719 bool startable; 1720 1721 hellcreek_port = dw_to_hellcreek_port(dw); 1722 hellcreek = hellcreek_port->hellcreek; 1723 1724 mutex_lock(&hellcreek->reg_lock); 1725 1726 /* Check starting time */ 1727 startable = hellcreek_schedule_startable(hellcreek, 1728 hellcreek_port->port); 1729 if (startable) { 1730 hellcreek_start_schedule(hellcreek, hellcreek_port->port); 1731 mutex_unlock(&hellcreek->reg_lock); 1732 return; 1733 } 1734 1735 mutex_unlock(&hellcreek->reg_lock); 1736 1737 /* Reschedule */ 1738 schedule_delayed_work(&hellcreek_port->schedule_work, 1739 HELLCREEK_SCHEDULE_PERIOD); 1740 } 1741 1742 static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port, 1743 struct tc_taprio_qopt_offload *taprio) 1744 { 1745 struct hellcreek *hellcreek = ds->priv; 1746 struct hellcreek_port *hellcreek_port; 1747 bool startable; 1748 u16 ctrl; 1749 1750 hellcreek_port = &hellcreek->ports[port]; 1751 1752 dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n", 1753 port); 1754 1755 /* First cancel delayed work */ 1756 cancel_delayed_work_sync(&hellcreek_port->schedule_work); 1757 1758 mutex_lock(&hellcreek->reg_lock); 1759 1760 if (hellcreek_port->current_schedule) { 1761 taprio_offload_free(hellcreek_port->current_schedule); 1762 hellcreek_port->current_schedule = NULL; 1763 } 1764 hellcreek_port->current_schedule = taprio_offload_get(taprio); 1765 1766 /* Configure max sdu */ 1767 hellcreek_setup_maxsdu(hellcreek, port, hellcreek_port->current_schedule); 1768 1769 /* Select tdg */ 1770 hellcreek_select_tgd(hellcreek, port); 1771 1772 /* Enable gating and keep defaults */ 1773 ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN; 1774 hellcreek_write(hellcreek, ctrl, TR_TGDCTRL); 1775 1776 /* Cancel pending schedule */ 1777 hellcreek_write(hellcreek, 0x00, TR_ESTCMD); 1778 1779 /* Setup a new schedule */ 1780 hellcreek_setup_gcl(hellcreek, port, hellcreek_port->current_schedule); 1781 1782 /* Configure cycle time */ 1783 hellcreek_set_cycle_time(hellcreek, hellcreek_port->current_schedule); 1784 1785 /* Check starting time */ 1786 startable = hellcreek_schedule_startable(hellcreek, port); 1787 if (startable) { 1788 hellcreek_start_schedule(hellcreek, port); 1789 mutex_unlock(&hellcreek->reg_lock); 1790 return 0; 1791 } 1792 1793 mutex_unlock(&hellcreek->reg_lock); 1794 1795 /* Schedule periodic schedule check */ 1796 schedule_delayed_work(&hellcreek_port->schedule_work, 1797 HELLCREEK_SCHEDULE_PERIOD); 1798 1799 return 0; 1800 } 1801 1802 static int hellcreek_port_del_schedule(struct dsa_switch *ds, int port) 1803 { 1804 struct hellcreek *hellcreek = ds->priv; 1805 struct hellcreek_port *hellcreek_port; 1806 1807 hellcreek_port = &hellcreek->ports[port]; 1808 1809 dev_dbg(hellcreek->dev, "Remove traffic schedule on port %d\n", port); 1810 1811 /* First cancel delayed work */ 1812 cancel_delayed_work_sync(&hellcreek_port->schedule_work); 1813 1814 mutex_lock(&hellcreek->reg_lock); 1815 1816 if (hellcreek_port->current_schedule) { 1817 taprio_offload_free(hellcreek_port->current_schedule); 1818 hellcreek_port->current_schedule = NULL; 1819 } 1820 1821 /* Reset max sdu */ 1822 hellcreek_reset_maxsdu(hellcreek, port); 1823 1824 /* Select tgd */ 1825 hellcreek_select_tgd(hellcreek, port); 1826 1827 /* Disable gating and return to regular switching flow */ 1828 hellcreek_write(hellcreek, 0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT, 1829 TR_TGDCTRL); 1830 1831 mutex_unlock(&hellcreek->reg_lock); 1832 1833 return 0; 1834 } 1835 1836 static bool hellcreek_validate_schedule(struct hellcreek *hellcreek, 1837 struct tc_taprio_qopt_offload *schedule) 1838 { 1839 size_t i; 1840 1841 /* Does this hellcreek version support Qbv in hardware? */ 1842 if (!hellcreek->pdata->qbv_support) 1843 return false; 1844 1845 /* cycle time can only be 32bit */ 1846 if (schedule->cycle_time > (u32)-1) 1847 return false; 1848 1849 /* cycle time extension is not supported */ 1850 if (schedule->cycle_time_extension) 1851 return false; 1852 1853 /* Only set command is supported */ 1854 for (i = 0; i < schedule->num_entries; ++i) 1855 if (schedule->entries[i].command != TC_TAPRIO_CMD_SET_GATES) 1856 return false; 1857 1858 return true; 1859 } 1860 1861 static int hellcreek_tc_query_caps(struct tc_query_caps_base *base) 1862 { 1863 switch (base->type) { 1864 case TC_SETUP_QDISC_TAPRIO: { 1865 struct tc_taprio_caps *caps = base->caps; 1866 1867 caps->supports_queue_max_sdu = true; 1868 1869 return 0; 1870 } 1871 default: 1872 return -EOPNOTSUPP; 1873 } 1874 } 1875 1876 static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port, 1877 enum tc_setup_type type, void *type_data) 1878 { 1879 struct hellcreek *hellcreek = ds->priv; 1880 1881 switch (type) { 1882 case TC_QUERY_CAPS: 1883 return hellcreek_tc_query_caps(type_data); 1884 case TC_SETUP_QDISC_TAPRIO: { 1885 struct tc_taprio_qopt_offload *taprio = type_data; 1886 1887 switch (taprio->cmd) { 1888 case TAPRIO_CMD_REPLACE: 1889 if (!hellcreek_validate_schedule(hellcreek, taprio)) 1890 return -EOPNOTSUPP; 1891 1892 return hellcreek_port_set_schedule(ds, port, taprio); 1893 case TAPRIO_CMD_DESTROY: 1894 return hellcreek_port_del_schedule(ds, port); 1895 default: 1896 return -EOPNOTSUPP; 1897 } 1898 } 1899 default: 1900 return -EOPNOTSUPP; 1901 } 1902 } 1903 1904 static const struct dsa_switch_ops hellcreek_ds_ops = { 1905 .devlink_info_get = hellcreek_devlink_info_get, 1906 .get_ethtool_stats = hellcreek_get_ethtool_stats, 1907 .get_sset_count = hellcreek_get_sset_count, 1908 .get_strings = hellcreek_get_strings, 1909 .get_tag_protocol = hellcreek_get_tag_protocol, 1910 .get_ts_info = hellcreek_get_ts_info, 1911 .phylink_get_caps = hellcreek_phylink_get_caps, 1912 .port_bridge_flags = hellcreek_bridge_flags, 1913 .port_bridge_join = hellcreek_port_bridge_join, 1914 .port_bridge_leave = hellcreek_port_bridge_leave, 1915 .port_disable = hellcreek_port_disable, 1916 .port_enable = hellcreek_port_enable, 1917 .port_fdb_add = hellcreek_fdb_add, 1918 .port_fdb_del = hellcreek_fdb_del, 1919 .port_fdb_dump = hellcreek_fdb_dump, 1920 .port_hwtstamp_set = hellcreek_port_hwtstamp_set, 1921 .port_hwtstamp_get = hellcreek_port_hwtstamp_get, 1922 .port_pre_bridge_flags = hellcreek_pre_bridge_flags, 1923 .port_prechangeupper = hellcreek_port_prechangeupper, 1924 .port_rxtstamp = hellcreek_port_rxtstamp, 1925 .port_setup_tc = hellcreek_port_setup_tc, 1926 .port_stp_state_set = hellcreek_port_stp_state_set, 1927 .port_txtstamp = hellcreek_port_txtstamp, 1928 .port_vlan_add = hellcreek_vlan_add, 1929 .port_vlan_del = hellcreek_vlan_del, 1930 .port_vlan_filtering = hellcreek_vlan_filtering, 1931 .setup = hellcreek_setup, 1932 .teardown = hellcreek_teardown, 1933 }; 1934 1935 static int hellcreek_probe(struct platform_device *pdev) 1936 { 1937 struct device *dev = &pdev->dev; 1938 struct hellcreek *hellcreek; 1939 struct resource *res; 1940 int ret, i; 1941 1942 hellcreek = devm_kzalloc(dev, sizeof(*hellcreek), GFP_KERNEL); 1943 if (!hellcreek) 1944 return -ENOMEM; 1945 1946 hellcreek->vidmbrcfg = devm_kcalloc(dev, VLAN_N_VID, 1947 sizeof(*hellcreek->vidmbrcfg), 1948 GFP_KERNEL); 1949 if (!hellcreek->vidmbrcfg) 1950 return -ENOMEM; 1951 1952 hellcreek->pdata = of_device_get_match_data(dev); 1953 1954 hellcreek->ports = devm_kcalloc(dev, hellcreek->pdata->num_ports, 1955 sizeof(*hellcreek->ports), 1956 GFP_KERNEL); 1957 if (!hellcreek->ports) 1958 return -ENOMEM; 1959 1960 for (i = 0; i < hellcreek->pdata->num_ports; ++i) { 1961 struct hellcreek_port *port = &hellcreek->ports[i]; 1962 1963 port->counter_values = 1964 devm_kcalloc(dev, 1965 ARRAY_SIZE(hellcreek_counter), 1966 sizeof(*port->counter_values), 1967 GFP_KERNEL); 1968 if (!port->counter_values) 1969 return -ENOMEM; 1970 1971 port->vlan_dev_bitmap = devm_bitmap_zalloc(dev, VLAN_N_VID, 1972 GFP_KERNEL); 1973 if (!port->vlan_dev_bitmap) 1974 return -ENOMEM; 1975 1976 port->hellcreek = hellcreek; 1977 port->port = i; 1978 1979 INIT_DELAYED_WORK(&port->schedule_work, 1980 hellcreek_check_schedule); 1981 } 1982 1983 mutex_init(&hellcreek->reg_lock); 1984 mutex_init(&hellcreek->vlan_lock); 1985 mutex_init(&hellcreek->ptp_lock); 1986 1987 hellcreek->dev = dev; 1988 1989 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tsn"); 1990 if (!res) { 1991 dev_err(dev, "No memory region provided!\n"); 1992 return -ENODEV; 1993 } 1994 1995 hellcreek->base = devm_ioremap_resource(dev, res); 1996 if (IS_ERR(hellcreek->base)) 1997 return PTR_ERR(hellcreek->base); 1998 1999 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ptp"); 2000 if (!res) { 2001 dev_err(dev, "No PTP memory region provided!\n"); 2002 return -ENODEV; 2003 } 2004 2005 hellcreek->ptp_base = devm_ioremap_resource(dev, res); 2006 if (IS_ERR(hellcreek->ptp_base)) 2007 return PTR_ERR(hellcreek->ptp_base); 2008 2009 ret = hellcreek_detect(hellcreek); 2010 if (ret) { 2011 dev_err(dev, "No (known) chip found!\n"); 2012 return ret; 2013 } 2014 2015 ret = hellcreek_wait_until_ready(hellcreek); 2016 if (ret) { 2017 dev_err(dev, "Switch didn't become ready!\n"); 2018 return ret; 2019 } 2020 2021 hellcreek_feature_detect(hellcreek); 2022 2023 hellcreek->ds = devm_kzalloc(dev, sizeof(*hellcreek->ds), GFP_KERNEL); 2024 if (!hellcreek->ds) 2025 return -ENOMEM; 2026 2027 hellcreek->ds->dev = dev; 2028 hellcreek->ds->priv = hellcreek; 2029 hellcreek->ds->ops = &hellcreek_ds_ops; 2030 hellcreek->ds->num_ports = hellcreek->pdata->num_ports; 2031 hellcreek->ds->num_tx_queues = HELLCREEK_NUM_EGRESS_QUEUES; 2032 2033 ret = dsa_register_switch(hellcreek->ds); 2034 if (ret) { 2035 dev_err_probe(dev, ret, "Unable to register switch\n"); 2036 return ret; 2037 } 2038 2039 ret = hellcreek_ptp_setup(hellcreek); 2040 if (ret) { 2041 dev_err(dev, "Failed to setup PTP!\n"); 2042 goto err_ptp_setup; 2043 } 2044 2045 ret = hellcreek_hwtstamp_setup(hellcreek); 2046 if (ret) { 2047 dev_err(dev, "Failed to setup hardware timestamping!\n"); 2048 goto err_tstamp_setup; 2049 } 2050 2051 platform_set_drvdata(pdev, hellcreek); 2052 2053 return 0; 2054 2055 err_tstamp_setup: 2056 hellcreek_ptp_free(hellcreek); 2057 err_ptp_setup: 2058 dsa_unregister_switch(hellcreek->ds); 2059 2060 return ret; 2061 } 2062 2063 static int hellcreek_remove(struct platform_device *pdev) 2064 { 2065 struct hellcreek *hellcreek = platform_get_drvdata(pdev); 2066 2067 if (!hellcreek) 2068 return 0; 2069 2070 hellcreek_hwtstamp_free(hellcreek); 2071 hellcreek_ptp_free(hellcreek); 2072 dsa_unregister_switch(hellcreek->ds); 2073 2074 return 0; 2075 } 2076 2077 static void hellcreek_shutdown(struct platform_device *pdev) 2078 { 2079 struct hellcreek *hellcreek = platform_get_drvdata(pdev); 2080 2081 if (!hellcreek) 2082 return; 2083 2084 dsa_switch_shutdown(hellcreek->ds); 2085 2086 platform_set_drvdata(pdev, NULL); 2087 } 2088 2089 static const struct hellcreek_platform_data de1soc_r1_pdata = { 2090 .name = "r4c30", 2091 .num_ports = 4, 2092 .is_100_mbits = 1, 2093 .qbv_support = 1, 2094 .qbv_on_cpu_port = 1, 2095 .qbu_support = 0, 2096 .module_id = 0x4c30, 2097 }; 2098 2099 static const struct of_device_id hellcreek_of_match[] = { 2100 { 2101 .compatible = "hirschmann,hellcreek-de1soc-r1", 2102 .data = &de1soc_r1_pdata, 2103 }, 2104 { /* sentinel */ }, 2105 }; 2106 MODULE_DEVICE_TABLE(of, hellcreek_of_match); 2107 2108 static struct platform_driver hellcreek_driver = { 2109 .probe = hellcreek_probe, 2110 .remove = hellcreek_remove, 2111 .shutdown = hellcreek_shutdown, 2112 .driver = { 2113 .name = "hellcreek", 2114 .of_match_table = hellcreek_of_match, 2115 }, 2116 }; 2117 module_platform_driver(hellcreek_driver); 2118 2119 MODULE_AUTHOR("Kurt Kanzenbach <kurt@linutronix.de>"); 2120 MODULE_DESCRIPTION("Hirschmann Hellcreek driver"); 2121 MODULE_LICENSE("Dual MIT/GPL"); 2122