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_device.h> 15 #include <linux/of_mdio.h> 16 #include <linux/platform_device.h> 17 #include <linux/bitops.h> 18 #include <linux/if_bridge.h> 19 #include <linux/if_vlan.h> 20 #include <linux/etherdevice.h> 21 #include <linux/random.h> 22 #include <linux/iopoll.h> 23 #include <linux/mutex.h> 24 #include <linux/delay.h> 25 #include <net/dsa.h> 26 27 #include "hellcreek.h" 28 #include "hellcreek_ptp.h" 29 #include "hellcreek_hwtstamp.h" 30 31 static const struct hellcreek_counter hellcreek_counter[] = { 32 { 0x00, "RxFiltered", }, 33 { 0x01, "RxOctets1k", }, 34 { 0x02, "RxVTAG", }, 35 { 0x03, "RxL2BAD", }, 36 { 0x04, "RxOverloadDrop", }, 37 { 0x05, "RxUC", }, 38 { 0x06, "RxMC", }, 39 { 0x07, "RxBC", }, 40 { 0x08, "RxRS<64", }, 41 { 0x09, "RxRS64", }, 42 { 0x0a, "RxRS65_127", }, 43 { 0x0b, "RxRS128_255", }, 44 { 0x0c, "RxRS256_511", }, 45 { 0x0d, "RxRS512_1023", }, 46 { 0x0e, "RxRS1024_1518", }, 47 { 0x0f, "RxRS>1518", }, 48 { 0x10, "TxTailDropQueue0", }, 49 { 0x11, "TxTailDropQueue1", }, 50 { 0x12, "TxTailDropQueue2", }, 51 { 0x13, "TxTailDropQueue3", }, 52 { 0x14, "TxTailDropQueue4", }, 53 { 0x15, "TxTailDropQueue5", }, 54 { 0x16, "TxTailDropQueue6", }, 55 { 0x17, "TxTailDropQueue7", }, 56 { 0x18, "RxTrafficClass0", }, 57 { 0x19, "RxTrafficClass1", }, 58 { 0x1a, "RxTrafficClass2", }, 59 { 0x1b, "RxTrafficClass3", }, 60 { 0x1c, "RxTrafficClass4", }, 61 { 0x1d, "RxTrafficClass5", }, 62 { 0x1e, "RxTrafficClass6", }, 63 { 0x1f, "RxTrafficClass7", }, 64 { 0x21, "TxOctets1k", }, 65 { 0x22, "TxVTAG", }, 66 { 0x23, "TxL2BAD", }, 67 { 0x25, "TxUC", }, 68 { 0x26, "TxMC", }, 69 { 0x27, "TxBC", }, 70 { 0x28, "TxTS<64", }, 71 { 0x29, "TxTS64", }, 72 { 0x2a, "TxTS65_127", }, 73 { 0x2b, "TxTS128_255", }, 74 { 0x2c, "TxTS256_511", }, 75 { 0x2d, "TxTS512_1023", }, 76 { 0x2e, "TxTS1024_1518", }, 77 { 0x2f, "TxTS>1518", }, 78 { 0x30, "TxTrafficClassOverrun0", }, 79 { 0x31, "TxTrafficClassOverrun1", }, 80 { 0x32, "TxTrafficClassOverrun2", }, 81 { 0x33, "TxTrafficClassOverrun3", }, 82 { 0x34, "TxTrafficClassOverrun4", }, 83 { 0x35, "TxTrafficClassOverrun5", }, 84 { 0x36, "TxTrafficClassOverrun6", }, 85 { 0x37, "TxTrafficClassOverrun7", }, 86 { 0x38, "TxTrafficClass0", }, 87 { 0x39, "TxTrafficClass1", }, 88 { 0x3a, "TxTrafficClass2", }, 89 { 0x3b, "TxTrafficClass3", }, 90 { 0x3c, "TxTrafficClass4", }, 91 { 0x3d, "TxTrafficClass5", }, 92 { 0x3e, "TxTrafficClass6", }, 93 { 0x3f, "TxTrafficClass7", }, 94 }; 95 96 static u16 hellcreek_read(struct hellcreek *hellcreek, unsigned int offset) 97 { 98 return readw(hellcreek->base + offset); 99 } 100 101 static u16 hellcreek_read_ctrl(struct hellcreek *hellcreek) 102 { 103 return readw(hellcreek->base + HR_CTRL_C); 104 } 105 106 static u16 hellcreek_read_stat(struct hellcreek *hellcreek) 107 { 108 return readw(hellcreek->base + HR_SWSTAT); 109 } 110 111 static void hellcreek_write(struct hellcreek *hellcreek, u16 data, 112 unsigned int offset) 113 { 114 writew(data, hellcreek->base + offset); 115 } 116 117 static void hellcreek_select_port(struct hellcreek *hellcreek, int port) 118 { 119 u16 val = port << HR_PSEL_PTWSEL_SHIFT; 120 121 hellcreek_write(hellcreek, val, HR_PSEL); 122 } 123 124 static void hellcreek_select_prio(struct hellcreek *hellcreek, int prio) 125 { 126 u16 val = prio << HR_PSEL_PRTCWSEL_SHIFT; 127 128 hellcreek_write(hellcreek, val, HR_PSEL); 129 } 130 131 static void hellcreek_select_counter(struct hellcreek *hellcreek, int counter) 132 { 133 u16 val = counter << HR_CSEL_SHIFT; 134 135 hellcreek_write(hellcreek, val, HR_CSEL); 136 137 /* Data sheet states to wait at least 20 internal clock cycles */ 138 ndelay(200); 139 } 140 141 static void hellcreek_select_vlan(struct hellcreek *hellcreek, int vid, 142 bool pvid) 143 { 144 u16 val = 0; 145 146 /* Set pvid bit first */ 147 if (pvid) 148 val |= HR_VIDCFG_PVID; 149 hellcreek_write(hellcreek, val, HR_VIDCFG); 150 151 /* Set vlan */ 152 val |= vid << HR_VIDCFG_VID_SHIFT; 153 hellcreek_write(hellcreek, val, HR_VIDCFG); 154 } 155 156 static void hellcreek_select_tgd(struct hellcreek *hellcreek, int port) 157 { 158 u16 val = port << TR_TGDSEL_TDGSEL_SHIFT; 159 160 hellcreek_write(hellcreek, val, TR_TGDSEL); 161 } 162 163 static int hellcreek_wait_until_ready(struct hellcreek *hellcreek) 164 { 165 u16 val; 166 167 /* Wait up to 1ms, although 3 us should be enough */ 168 return readx_poll_timeout(hellcreek_read_ctrl, hellcreek, 169 val, val & HR_CTRL_C_READY, 170 3, 1000); 171 } 172 173 static int hellcreek_wait_until_transitioned(struct hellcreek *hellcreek) 174 { 175 u16 val; 176 177 return readx_poll_timeout_atomic(hellcreek_read_ctrl, hellcreek, 178 val, !(val & HR_CTRL_C_TRANSITION), 179 1, 1000); 180 } 181 182 static int hellcreek_wait_fdb_ready(struct hellcreek *hellcreek) 183 { 184 u16 val; 185 186 return readx_poll_timeout_atomic(hellcreek_read_stat, hellcreek, 187 val, !(val & HR_SWSTAT_BUSY), 188 1, 1000); 189 } 190 191 static int hellcreek_detect(struct hellcreek *hellcreek) 192 { 193 u16 id, rel_low, rel_high, date_low, date_high, tgd_ver; 194 u8 tgd_maj, tgd_min; 195 u32 rel, date; 196 197 id = hellcreek_read(hellcreek, HR_MODID_C); 198 rel_low = hellcreek_read(hellcreek, HR_REL_L_C); 199 rel_high = hellcreek_read(hellcreek, HR_REL_H_C); 200 date_low = hellcreek_read(hellcreek, HR_BLD_L_C); 201 date_high = hellcreek_read(hellcreek, HR_BLD_H_C); 202 tgd_ver = hellcreek_read(hellcreek, TR_TGDVER); 203 204 if (id != hellcreek->pdata->module_id) 205 return -ENODEV; 206 207 rel = rel_low | (rel_high << 16); 208 date = date_low | (date_high << 16); 209 tgd_maj = (tgd_ver & TR_TGDVER_REV_MAJ_MASK) >> TR_TGDVER_REV_MAJ_SHIFT; 210 tgd_min = (tgd_ver & TR_TGDVER_REV_MIN_MASK) >> TR_TGDVER_REV_MIN_SHIFT; 211 212 dev_info(hellcreek->dev, "Module ID=%02x Release=%04x Date=%04x TGD Version=%02x.%02x\n", 213 id, rel, date, tgd_maj, tgd_min); 214 215 return 0; 216 } 217 218 static void hellcreek_feature_detect(struct hellcreek *hellcreek) 219 { 220 u16 features; 221 222 features = hellcreek_read(hellcreek, HR_FEABITS0); 223 224 /* Currently we only detect the size of the FDB table */ 225 hellcreek->fdb_entries = ((features & HR_FEABITS0_FDBBINS_MASK) >> 226 HR_FEABITS0_FDBBINS_SHIFT) * 32; 227 228 dev_info(hellcreek->dev, "Feature detect: FDB entries=%zu\n", 229 hellcreek->fdb_entries); 230 } 231 232 static enum dsa_tag_protocol hellcreek_get_tag_protocol(struct dsa_switch *ds, 233 int port, 234 enum dsa_tag_protocol mp) 235 { 236 return DSA_TAG_PROTO_HELLCREEK; 237 } 238 239 static int hellcreek_port_enable(struct dsa_switch *ds, int port, 240 struct phy_device *phy) 241 { 242 struct hellcreek *hellcreek = ds->priv; 243 struct hellcreek_port *hellcreek_port; 244 u16 val; 245 246 hellcreek_port = &hellcreek->ports[port]; 247 248 dev_dbg(hellcreek->dev, "Enable port %d\n", port); 249 250 mutex_lock(&hellcreek->reg_lock); 251 252 hellcreek_select_port(hellcreek, port); 253 val = hellcreek_port->ptcfg; 254 val |= HR_PTCFG_ADMIN_EN; 255 hellcreek_write(hellcreek, val, HR_PTCFG); 256 hellcreek_port->ptcfg = val; 257 258 mutex_unlock(&hellcreek->reg_lock); 259 260 return 0; 261 } 262 263 static void hellcreek_port_disable(struct dsa_switch *ds, int port) 264 { 265 struct hellcreek *hellcreek = ds->priv; 266 struct hellcreek_port *hellcreek_port; 267 u16 val; 268 269 hellcreek_port = &hellcreek->ports[port]; 270 271 dev_dbg(hellcreek->dev, "Disable port %d\n", port); 272 273 mutex_lock(&hellcreek->reg_lock); 274 275 hellcreek_select_port(hellcreek, port); 276 val = hellcreek_port->ptcfg; 277 val &= ~HR_PTCFG_ADMIN_EN; 278 hellcreek_write(hellcreek, val, HR_PTCFG); 279 hellcreek_port->ptcfg = val; 280 281 mutex_unlock(&hellcreek->reg_lock); 282 } 283 284 static void hellcreek_get_strings(struct dsa_switch *ds, int port, 285 u32 stringset, uint8_t *data) 286 { 287 int i; 288 289 for (i = 0; i < ARRAY_SIZE(hellcreek_counter); ++i) { 290 const struct hellcreek_counter *counter = &hellcreek_counter[i]; 291 292 strlcpy(data + i * ETH_GSTRING_LEN, 293 counter->name, ETH_GSTRING_LEN); 294 } 295 } 296 297 static int hellcreek_get_sset_count(struct dsa_switch *ds, int port, int sset) 298 { 299 if (sset != ETH_SS_STATS) 300 return 0; 301 302 return ARRAY_SIZE(hellcreek_counter); 303 } 304 305 static void hellcreek_get_ethtool_stats(struct dsa_switch *ds, int port, 306 uint64_t *data) 307 { 308 struct hellcreek *hellcreek = ds->priv; 309 struct hellcreek_port *hellcreek_port; 310 int i; 311 312 hellcreek_port = &hellcreek->ports[port]; 313 314 for (i = 0; i < ARRAY_SIZE(hellcreek_counter); ++i) { 315 const struct hellcreek_counter *counter = &hellcreek_counter[i]; 316 u8 offset = counter->offset + port * 64; 317 u16 high, low; 318 u64 value; 319 320 mutex_lock(&hellcreek->reg_lock); 321 322 hellcreek_select_counter(hellcreek, offset); 323 324 /* The registers are locked internally by selecting the 325 * counter. So low and high can be read without reading high 326 * again. 327 */ 328 high = hellcreek_read(hellcreek, HR_CRDH); 329 low = hellcreek_read(hellcreek, HR_CRDL); 330 value = ((u64)high << 16) | low; 331 332 hellcreek_port->counter_values[i] += value; 333 data[i] = hellcreek_port->counter_values[i]; 334 335 mutex_unlock(&hellcreek->reg_lock); 336 } 337 } 338 339 static u16 hellcreek_private_vid(int port) 340 { 341 return VLAN_N_VID - port + 1; 342 } 343 344 static int hellcreek_vlan_prepare(struct dsa_switch *ds, int port, 345 const struct switchdev_obj_port_vlan *vlan) 346 { 347 struct hellcreek *hellcreek = ds->priv; 348 int i; 349 350 dev_dbg(hellcreek->dev, "VLAN prepare for port %d\n", port); 351 352 /* Restriction: Make sure that nobody uses the "private" VLANs. These 353 * VLANs are internally used by the driver to ensure port 354 * separation. Thus, they cannot be used by someone else. 355 */ 356 for (i = 0; i < hellcreek->pdata->num_ports; ++i) { 357 const u16 restricted_vid = hellcreek_private_vid(i); 358 359 if (!dsa_is_user_port(ds, i)) 360 continue; 361 362 if (vlan->vid == restricted_vid) 363 return -EBUSY; 364 } 365 366 return 0; 367 } 368 369 static void hellcreek_select_vlan_params(struct hellcreek *hellcreek, int port, 370 int *shift, int *mask) 371 { 372 switch (port) { 373 case 0: 374 *shift = HR_VIDMBRCFG_P0MBR_SHIFT; 375 *mask = HR_VIDMBRCFG_P0MBR_MASK; 376 break; 377 case 1: 378 *shift = HR_VIDMBRCFG_P1MBR_SHIFT; 379 *mask = HR_VIDMBRCFG_P1MBR_MASK; 380 break; 381 case 2: 382 *shift = HR_VIDMBRCFG_P2MBR_SHIFT; 383 *mask = HR_VIDMBRCFG_P2MBR_MASK; 384 break; 385 case 3: 386 *shift = HR_VIDMBRCFG_P3MBR_SHIFT; 387 *mask = HR_VIDMBRCFG_P3MBR_MASK; 388 break; 389 default: 390 *shift = *mask = 0; 391 dev_err(hellcreek->dev, "Unknown port %d selected!\n", port); 392 } 393 } 394 395 static void hellcreek_apply_vlan(struct hellcreek *hellcreek, int port, u16 vid, 396 bool pvid, bool untagged) 397 { 398 int shift, mask; 399 u16 val; 400 401 dev_dbg(hellcreek->dev, "Apply VLAN: port=%d vid=%u pvid=%d untagged=%d", 402 port, vid, pvid, untagged); 403 404 mutex_lock(&hellcreek->reg_lock); 405 406 hellcreek_select_port(hellcreek, port); 407 hellcreek_select_vlan(hellcreek, vid, pvid); 408 409 /* Setup port vlan membership */ 410 hellcreek_select_vlan_params(hellcreek, port, &shift, &mask); 411 val = hellcreek->vidmbrcfg[vid]; 412 val &= ~mask; 413 if (untagged) 414 val |= HELLCREEK_VLAN_UNTAGGED_MEMBER << shift; 415 else 416 val |= HELLCREEK_VLAN_TAGGED_MEMBER << shift; 417 418 hellcreek_write(hellcreek, val, HR_VIDMBRCFG); 419 hellcreek->vidmbrcfg[vid] = val; 420 421 mutex_unlock(&hellcreek->reg_lock); 422 } 423 424 static void hellcreek_unapply_vlan(struct hellcreek *hellcreek, int port, 425 u16 vid) 426 { 427 int shift, mask; 428 u16 val; 429 430 dev_dbg(hellcreek->dev, "Unapply VLAN: port=%d vid=%u\n", port, vid); 431 432 mutex_lock(&hellcreek->reg_lock); 433 434 hellcreek_select_vlan(hellcreek, vid, 0); 435 436 /* Setup port vlan membership */ 437 hellcreek_select_vlan_params(hellcreek, port, &shift, &mask); 438 val = hellcreek->vidmbrcfg[vid]; 439 val &= ~mask; 440 val |= HELLCREEK_VLAN_NO_MEMBER << shift; 441 442 hellcreek_write(hellcreek, val, HR_VIDMBRCFG); 443 hellcreek->vidmbrcfg[vid] = val; 444 445 mutex_unlock(&hellcreek->reg_lock); 446 } 447 448 static int hellcreek_vlan_add(struct dsa_switch *ds, int port, 449 const struct switchdev_obj_port_vlan *vlan) 450 { 451 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 452 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 453 struct hellcreek *hellcreek = ds->priv; 454 int err; 455 456 err = hellcreek_vlan_prepare(ds, port, vlan); 457 if (err) 458 return err; 459 460 dev_dbg(hellcreek->dev, "Add VLAN %d on port %d, %s, %s\n", 461 vlan->vid, port, untagged ? "untagged" : "tagged", 462 pvid ? "PVID" : "no PVID"); 463 464 hellcreek_apply_vlan(hellcreek, port, vlan->vid, pvid, untagged); 465 466 return 0; 467 } 468 469 static int hellcreek_vlan_del(struct dsa_switch *ds, int port, 470 const struct switchdev_obj_port_vlan *vlan) 471 { 472 struct hellcreek *hellcreek = ds->priv; 473 474 dev_dbg(hellcreek->dev, "Remove VLAN %d on port %d\n", vlan->vid, port); 475 476 hellcreek_unapply_vlan(hellcreek, port, vlan->vid); 477 478 return 0; 479 } 480 481 static void hellcreek_port_stp_state_set(struct dsa_switch *ds, int port, 482 u8 state) 483 { 484 struct hellcreek *hellcreek = ds->priv; 485 struct hellcreek_port *hellcreek_port; 486 const char *new_state; 487 u16 val; 488 489 mutex_lock(&hellcreek->reg_lock); 490 491 hellcreek_port = &hellcreek->ports[port]; 492 val = hellcreek_port->ptcfg; 493 494 switch (state) { 495 case BR_STATE_DISABLED: 496 new_state = "DISABLED"; 497 val |= HR_PTCFG_BLOCKED; 498 val &= ~HR_PTCFG_LEARNING_EN; 499 break; 500 case BR_STATE_BLOCKING: 501 new_state = "BLOCKING"; 502 val |= HR_PTCFG_BLOCKED; 503 val &= ~HR_PTCFG_LEARNING_EN; 504 break; 505 case BR_STATE_LISTENING: 506 new_state = "LISTENING"; 507 val |= HR_PTCFG_BLOCKED; 508 val &= ~HR_PTCFG_LEARNING_EN; 509 break; 510 case BR_STATE_LEARNING: 511 new_state = "LEARNING"; 512 val |= HR_PTCFG_BLOCKED; 513 val |= HR_PTCFG_LEARNING_EN; 514 break; 515 case BR_STATE_FORWARDING: 516 new_state = "FORWARDING"; 517 val &= ~HR_PTCFG_BLOCKED; 518 val |= HR_PTCFG_LEARNING_EN; 519 break; 520 default: 521 new_state = "UNKNOWN"; 522 } 523 524 hellcreek_select_port(hellcreek, port); 525 hellcreek_write(hellcreek, val, HR_PTCFG); 526 hellcreek_port->ptcfg = val; 527 528 mutex_unlock(&hellcreek->reg_lock); 529 530 dev_dbg(hellcreek->dev, "Configured STP state for port %d: %s\n", 531 port, new_state); 532 } 533 534 static void hellcreek_setup_ingressflt(struct hellcreek *hellcreek, int port, 535 bool enable) 536 { 537 struct hellcreek_port *hellcreek_port = &hellcreek->ports[port]; 538 u16 ptcfg; 539 540 mutex_lock(&hellcreek->reg_lock); 541 542 ptcfg = hellcreek_port->ptcfg; 543 544 if (enable) 545 ptcfg |= HR_PTCFG_INGRESSFLT; 546 else 547 ptcfg &= ~HR_PTCFG_INGRESSFLT; 548 549 hellcreek_select_port(hellcreek, port); 550 hellcreek_write(hellcreek, ptcfg, HR_PTCFG); 551 hellcreek_port->ptcfg = ptcfg; 552 553 mutex_unlock(&hellcreek->reg_lock); 554 } 555 556 static void hellcreek_setup_vlan_awareness(struct hellcreek *hellcreek, 557 bool enable) 558 { 559 u16 swcfg; 560 561 mutex_lock(&hellcreek->reg_lock); 562 563 swcfg = hellcreek->swcfg; 564 565 if (enable) 566 swcfg |= HR_SWCFG_VLAN_UNAWARE; 567 else 568 swcfg &= ~HR_SWCFG_VLAN_UNAWARE; 569 570 hellcreek_write(hellcreek, swcfg, HR_SWCFG); 571 572 mutex_unlock(&hellcreek->reg_lock); 573 } 574 575 /* Default setup for DSA: VLAN <X>: CPU and Port <X> egress untagged. */ 576 static void hellcreek_setup_vlan_membership(struct dsa_switch *ds, int port, 577 bool enabled) 578 { 579 const u16 vid = hellcreek_private_vid(port); 580 int upstream = dsa_upstream_port(ds, port); 581 struct hellcreek *hellcreek = ds->priv; 582 583 /* Apply vid to port as egress untagged and port vlan id */ 584 if (enabled) 585 hellcreek_apply_vlan(hellcreek, port, vid, true, true); 586 else 587 hellcreek_unapply_vlan(hellcreek, port, vid); 588 589 /* Apply vid to cpu port as well */ 590 if (enabled) 591 hellcreek_apply_vlan(hellcreek, upstream, vid, false, true); 592 else 593 hellcreek_unapply_vlan(hellcreek, upstream, vid); 594 } 595 596 static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port, 597 struct net_device *br) 598 { 599 struct hellcreek *hellcreek = ds->priv; 600 601 dev_dbg(hellcreek->dev, "Port %d joins a bridge\n", port); 602 603 /* When joining a vlan_filtering bridge, keep the switch VLAN aware */ 604 if (!ds->vlan_filtering) 605 hellcreek_setup_vlan_awareness(hellcreek, false); 606 607 /* Drop private vlans */ 608 hellcreek_setup_vlan_membership(ds, port, false); 609 610 return 0; 611 } 612 613 static void hellcreek_port_bridge_leave(struct dsa_switch *ds, int port, 614 struct net_device *br) 615 { 616 struct hellcreek *hellcreek = ds->priv; 617 618 dev_dbg(hellcreek->dev, "Port %d leaves a bridge\n", port); 619 620 /* Enable VLAN awareness */ 621 hellcreek_setup_vlan_awareness(hellcreek, true); 622 623 /* Enable private vlans */ 624 hellcreek_setup_vlan_membership(ds, port, true); 625 } 626 627 static int __hellcreek_fdb_add(struct hellcreek *hellcreek, 628 const struct hellcreek_fdb_entry *entry) 629 { 630 u16 meta = 0; 631 632 dev_dbg(hellcreek->dev, "Add static FDB entry: MAC=%pM, MASK=0x%02x, " 633 "OBT=%d, REPRIO_EN=%d, PRIO=%d\n", entry->mac, entry->portmask, 634 entry->is_obt, entry->reprio_en, entry->reprio_tc); 635 636 /* Add mac address */ 637 hellcreek_write(hellcreek, entry->mac[1] | (entry->mac[0] << 8), HR_FDBWDH); 638 hellcreek_write(hellcreek, entry->mac[3] | (entry->mac[2] << 8), HR_FDBWDM); 639 hellcreek_write(hellcreek, entry->mac[5] | (entry->mac[4] << 8), HR_FDBWDL); 640 641 /* Meta data */ 642 meta |= entry->portmask << HR_FDBWRM0_PORTMASK_SHIFT; 643 if (entry->is_obt) 644 meta |= HR_FDBWRM0_OBT; 645 if (entry->reprio_en) { 646 meta |= HR_FDBWRM0_REPRIO_EN; 647 meta |= entry->reprio_tc << HR_FDBWRM0_REPRIO_TC_SHIFT; 648 } 649 hellcreek_write(hellcreek, meta, HR_FDBWRM0); 650 651 /* Commit */ 652 hellcreek_write(hellcreek, 0x00, HR_FDBWRCMD); 653 654 /* Wait until done */ 655 return hellcreek_wait_fdb_ready(hellcreek); 656 } 657 658 static int __hellcreek_fdb_del(struct hellcreek *hellcreek, 659 const struct hellcreek_fdb_entry *entry) 660 { 661 dev_dbg(hellcreek->dev, "Delete FDB entry: MAC=%pM!\n", entry->mac); 662 663 /* Delete by matching idx */ 664 hellcreek_write(hellcreek, entry->idx | HR_FDBWRCMD_FDBDEL, HR_FDBWRCMD); 665 666 /* Wait until done */ 667 return hellcreek_wait_fdb_ready(hellcreek); 668 } 669 670 /* Retrieve the index of a FDB entry by mac address. Currently we search through 671 * the complete table in hardware. If that's too slow, we might have to cache 672 * the complete FDB table in software. 673 */ 674 static int hellcreek_fdb_get(struct hellcreek *hellcreek, 675 const unsigned char *dest, 676 struct hellcreek_fdb_entry *entry) 677 { 678 size_t i; 679 680 /* Set read pointer to zero: The read of HR_FDBMAX (read-only register) 681 * should reset the internal pointer. But, that doesn't work. The vendor 682 * suggested a subsequent write as workaround. Same for HR_FDBRDH below. 683 */ 684 hellcreek_read(hellcreek, HR_FDBMAX); 685 hellcreek_write(hellcreek, 0x00, HR_FDBMAX); 686 687 /* We have to read the complete table, because the switch/driver might 688 * enter new entries anywhere. 689 */ 690 for (i = 0; i < hellcreek->fdb_entries; ++i) { 691 unsigned char addr[ETH_ALEN]; 692 u16 meta, mac; 693 694 meta = hellcreek_read(hellcreek, HR_FDBMDRD); 695 mac = hellcreek_read(hellcreek, HR_FDBRDL); 696 addr[5] = mac & 0xff; 697 addr[4] = (mac & 0xff00) >> 8; 698 mac = hellcreek_read(hellcreek, HR_FDBRDM); 699 addr[3] = mac & 0xff; 700 addr[2] = (mac & 0xff00) >> 8; 701 mac = hellcreek_read(hellcreek, HR_FDBRDH); 702 addr[1] = mac & 0xff; 703 addr[0] = (mac & 0xff00) >> 8; 704 705 /* Force next entry */ 706 hellcreek_write(hellcreek, 0x00, HR_FDBRDH); 707 708 if (memcmp(addr, dest, ETH_ALEN)) 709 continue; 710 711 /* Match found */ 712 entry->idx = i; 713 entry->portmask = (meta & HR_FDBMDRD_PORTMASK_MASK) >> 714 HR_FDBMDRD_PORTMASK_SHIFT; 715 entry->age = (meta & HR_FDBMDRD_AGE_MASK) >> 716 HR_FDBMDRD_AGE_SHIFT; 717 entry->is_obt = !!(meta & HR_FDBMDRD_OBT); 718 entry->pass_blocked = !!(meta & HR_FDBMDRD_PASS_BLOCKED); 719 entry->is_static = !!(meta & HR_FDBMDRD_STATIC); 720 entry->reprio_tc = (meta & HR_FDBMDRD_REPRIO_TC_MASK) >> 721 HR_FDBMDRD_REPRIO_TC_SHIFT; 722 entry->reprio_en = !!(meta & HR_FDBMDRD_REPRIO_EN); 723 memcpy(entry->mac, addr, sizeof(addr)); 724 725 return 0; 726 } 727 728 return -ENOENT; 729 } 730 731 static int hellcreek_fdb_add(struct dsa_switch *ds, int port, 732 const unsigned char *addr, u16 vid) 733 { 734 struct hellcreek_fdb_entry entry = { 0 }; 735 struct hellcreek *hellcreek = ds->priv; 736 int ret; 737 738 dev_dbg(hellcreek->dev, "Add FDB entry for MAC=%pM\n", addr); 739 740 mutex_lock(&hellcreek->reg_lock); 741 742 ret = hellcreek_fdb_get(hellcreek, addr, &entry); 743 if (ret) { 744 /* Not found */ 745 memcpy(entry.mac, addr, sizeof(entry.mac)); 746 entry.portmask = BIT(port); 747 748 ret = __hellcreek_fdb_add(hellcreek, &entry); 749 if (ret) { 750 dev_err(hellcreek->dev, "Failed to add FDB entry!\n"); 751 goto out; 752 } 753 } else { 754 /* Found */ 755 ret = __hellcreek_fdb_del(hellcreek, &entry); 756 if (ret) { 757 dev_err(hellcreek->dev, "Failed to delete FDB entry!\n"); 758 goto out; 759 } 760 761 entry.portmask |= BIT(port); 762 763 ret = __hellcreek_fdb_add(hellcreek, &entry); 764 if (ret) { 765 dev_err(hellcreek->dev, "Failed to add FDB entry!\n"); 766 goto out; 767 } 768 } 769 770 out: 771 mutex_unlock(&hellcreek->reg_lock); 772 773 return ret; 774 } 775 776 static int hellcreek_fdb_del(struct dsa_switch *ds, int port, 777 const unsigned char *addr, u16 vid) 778 { 779 struct hellcreek_fdb_entry entry = { 0 }; 780 struct hellcreek *hellcreek = ds->priv; 781 int ret; 782 783 dev_dbg(hellcreek->dev, "Delete FDB entry for MAC=%pM\n", addr); 784 785 mutex_lock(&hellcreek->reg_lock); 786 787 ret = hellcreek_fdb_get(hellcreek, addr, &entry); 788 if (ret) { 789 /* Not found */ 790 dev_err(hellcreek->dev, "FDB entry for deletion not found!\n"); 791 } else { 792 /* Found */ 793 ret = __hellcreek_fdb_del(hellcreek, &entry); 794 if (ret) { 795 dev_err(hellcreek->dev, "Failed to delete FDB entry!\n"); 796 goto out; 797 } 798 799 entry.portmask &= ~BIT(port); 800 801 if (entry.portmask != 0x00) { 802 ret = __hellcreek_fdb_add(hellcreek, &entry); 803 if (ret) { 804 dev_err(hellcreek->dev, "Failed to add FDB entry!\n"); 805 goto out; 806 } 807 } 808 } 809 810 out: 811 mutex_unlock(&hellcreek->reg_lock); 812 813 return ret; 814 } 815 816 static int hellcreek_fdb_dump(struct dsa_switch *ds, int port, 817 dsa_fdb_dump_cb_t *cb, void *data) 818 { 819 struct hellcreek *hellcreek = ds->priv; 820 u16 entries; 821 size_t i; 822 823 mutex_lock(&hellcreek->reg_lock); 824 825 /* Set read pointer to zero: The read of HR_FDBMAX (read-only register) 826 * should reset the internal pointer. But, that doesn't work. The vendor 827 * suggested a subsequent write as workaround. Same for HR_FDBRDH below. 828 */ 829 entries = hellcreek_read(hellcreek, HR_FDBMAX); 830 hellcreek_write(hellcreek, 0x00, HR_FDBMAX); 831 832 dev_dbg(hellcreek->dev, "FDB dump for port %d, entries=%d!\n", port, entries); 833 834 /* Read table */ 835 for (i = 0; i < hellcreek->fdb_entries; ++i) { 836 unsigned char null_addr[ETH_ALEN] = { 0 }; 837 struct hellcreek_fdb_entry entry = { 0 }; 838 u16 meta, mac; 839 840 meta = hellcreek_read(hellcreek, HR_FDBMDRD); 841 mac = hellcreek_read(hellcreek, HR_FDBRDL); 842 entry.mac[5] = mac & 0xff; 843 entry.mac[4] = (mac & 0xff00) >> 8; 844 mac = hellcreek_read(hellcreek, HR_FDBRDM); 845 entry.mac[3] = mac & 0xff; 846 entry.mac[2] = (mac & 0xff00) >> 8; 847 mac = hellcreek_read(hellcreek, HR_FDBRDH); 848 entry.mac[1] = mac & 0xff; 849 entry.mac[0] = (mac & 0xff00) >> 8; 850 851 /* Force next entry */ 852 hellcreek_write(hellcreek, 0x00, HR_FDBRDH); 853 854 /* Check valid */ 855 if (!memcmp(entry.mac, null_addr, ETH_ALEN)) 856 continue; 857 858 entry.portmask = (meta & HR_FDBMDRD_PORTMASK_MASK) >> 859 HR_FDBMDRD_PORTMASK_SHIFT; 860 entry.is_static = !!(meta & HR_FDBMDRD_STATIC); 861 862 /* Check port mask */ 863 if (!(entry.portmask & BIT(port))) 864 continue; 865 866 cb(entry.mac, 0, entry.is_static, data); 867 } 868 869 mutex_unlock(&hellcreek->reg_lock); 870 871 return 0; 872 } 873 874 static int hellcreek_vlan_filtering(struct dsa_switch *ds, int port, 875 bool vlan_filtering) 876 { 877 struct hellcreek *hellcreek = ds->priv; 878 879 dev_dbg(hellcreek->dev, "%s VLAN filtering on port %d\n", 880 vlan_filtering ? "Enable" : "Disable", port); 881 882 /* Configure port to drop packages with not known vids */ 883 hellcreek_setup_ingressflt(hellcreek, port, vlan_filtering); 884 885 /* Enable VLAN awareness on the switch. This save due to 886 * ds->vlan_filtering_is_global. 887 */ 888 hellcreek_setup_vlan_awareness(hellcreek, vlan_filtering); 889 890 return 0; 891 } 892 893 static int hellcreek_enable_ip_core(struct hellcreek *hellcreek) 894 { 895 int ret; 896 u16 val; 897 898 mutex_lock(&hellcreek->reg_lock); 899 900 val = hellcreek_read(hellcreek, HR_CTRL_C); 901 val |= HR_CTRL_C_ENABLE; 902 hellcreek_write(hellcreek, val, HR_CTRL_C); 903 ret = hellcreek_wait_until_transitioned(hellcreek); 904 905 mutex_unlock(&hellcreek->reg_lock); 906 907 return ret; 908 } 909 910 static void hellcreek_setup_cpu_and_tunnel_port(struct hellcreek *hellcreek) 911 { 912 struct hellcreek_port *tunnel_port = &hellcreek->ports[TUNNEL_PORT]; 913 struct hellcreek_port *cpu_port = &hellcreek->ports[CPU_PORT]; 914 u16 ptcfg = 0; 915 916 ptcfg |= HR_PTCFG_LEARNING_EN | HR_PTCFG_ADMIN_EN; 917 918 mutex_lock(&hellcreek->reg_lock); 919 920 hellcreek_select_port(hellcreek, CPU_PORT); 921 hellcreek_write(hellcreek, ptcfg, HR_PTCFG); 922 923 hellcreek_select_port(hellcreek, TUNNEL_PORT); 924 hellcreek_write(hellcreek, ptcfg, HR_PTCFG); 925 926 cpu_port->ptcfg = ptcfg; 927 tunnel_port->ptcfg = ptcfg; 928 929 mutex_unlock(&hellcreek->reg_lock); 930 } 931 932 static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek) 933 { 934 int i; 935 936 /* The switch has multiple egress queues per port. The queue is selected 937 * via the PCP field in the VLAN header. The switch internally deals 938 * with traffic classes instead of PCP values and this mapping is 939 * configurable. 940 * 941 * The default mapping is (PCP - TC): 942 * 7 - 7 943 * 6 - 6 944 * 5 - 5 945 * 4 - 4 946 * 3 - 3 947 * 2 - 1 948 * 1 - 0 949 * 0 - 2 950 * 951 * The default should be an identity mapping. 952 */ 953 954 for (i = 0; i < 8; ++i) { 955 mutex_lock(&hellcreek->reg_lock); 956 957 hellcreek_select_prio(hellcreek, i); 958 hellcreek_write(hellcreek, 959 i << HR_PRTCCFG_PCP_TC_MAP_SHIFT, 960 HR_PRTCCFG); 961 962 mutex_unlock(&hellcreek->reg_lock); 963 } 964 } 965 966 static int hellcreek_setup_fdb(struct hellcreek *hellcreek) 967 { 968 static struct hellcreek_fdb_entry ptp = { 969 /* MAC: 01-1B-19-00-00-00 */ 970 .mac = { 0x01, 0x1b, 0x19, 0x00, 0x00, 0x00 }, 971 .portmask = 0x03, /* Management ports */ 972 .age = 0, 973 .is_obt = 0, 974 .pass_blocked = 0, 975 .is_static = 1, 976 .reprio_tc = 6, /* TC: 6 as per IEEE 802.1AS */ 977 .reprio_en = 1, 978 }; 979 static struct hellcreek_fdb_entry p2p = { 980 /* MAC: 01-80-C2-00-00-0E */ 981 .mac = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }, 982 .portmask = 0x03, /* Management ports */ 983 .age = 0, 984 .is_obt = 0, 985 .pass_blocked = 0, 986 .is_static = 1, 987 .reprio_tc = 6, /* TC: 6 as per IEEE 802.1AS */ 988 .reprio_en = 1, 989 }; 990 int ret; 991 992 mutex_lock(&hellcreek->reg_lock); 993 ret = __hellcreek_fdb_add(hellcreek, &ptp); 994 if (ret) 995 goto out; 996 ret = __hellcreek_fdb_add(hellcreek, &p2p); 997 out: 998 mutex_unlock(&hellcreek->reg_lock); 999 1000 return ret; 1001 } 1002 1003 static int hellcreek_setup(struct dsa_switch *ds) 1004 { 1005 struct hellcreek *hellcreek = ds->priv; 1006 u16 swcfg = 0; 1007 int ret, i; 1008 1009 dev_dbg(hellcreek->dev, "Set up the switch\n"); 1010 1011 /* Let's go */ 1012 ret = hellcreek_enable_ip_core(hellcreek); 1013 if (ret) { 1014 dev_err(hellcreek->dev, "Failed to enable IP core!\n"); 1015 return ret; 1016 } 1017 1018 /* Enable CPU/Tunnel ports */ 1019 hellcreek_setup_cpu_and_tunnel_port(hellcreek); 1020 1021 /* Switch config: Keep defaults, enable FDB aging and learning and tag 1022 * each frame from/to cpu port for DSA tagging. Also enable the length 1023 * aware shaping mode. This eliminates the need for Qbv guard bands. 1024 */ 1025 swcfg |= HR_SWCFG_FDBAGE_EN | 1026 HR_SWCFG_FDBLRN_EN | 1027 HR_SWCFG_ALWAYS_OBT | 1028 (HR_SWCFG_LAS_ON << HR_SWCFG_LAS_MODE_SHIFT); 1029 hellcreek->swcfg = swcfg; 1030 hellcreek_write(hellcreek, swcfg, HR_SWCFG); 1031 1032 /* Initial vlan membership to reflect port separation */ 1033 for (i = 0; i < ds->num_ports; ++i) { 1034 if (!dsa_is_user_port(ds, i)) 1035 continue; 1036 1037 hellcreek_setup_vlan_membership(ds, i, true); 1038 } 1039 1040 /* Configure PCP <-> TC mapping */ 1041 hellcreek_setup_tc_identity_mapping(hellcreek); 1042 1043 /* The VLAN awareness is a global switch setting. Therefore, mixed vlan 1044 * filtering setups are not supported. 1045 */ 1046 ds->vlan_filtering_is_global = true; 1047 1048 /* Intercept _all_ PTP multicast traffic */ 1049 ret = hellcreek_setup_fdb(hellcreek); 1050 if (ret) { 1051 dev_err(hellcreek->dev, 1052 "Failed to insert static PTP FDB entries\n"); 1053 return ret; 1054 } 1055 1056 return 0; 1057 } 1058 1059 static void hellcreek_phylink_validate(struct dsa_switch *ds, int port, 1060 unsigned long *supported, 1061 struct phylink_link_state *state) 1062 { 1063 __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; 1064 struct hellcreek *hellcreek = ds->priv; 1065 1066 dev_dbg(hellcreek->dev, "Phylink validate for port %d\n", port); 1067 1068 /* The MAC settings are a hardware configuration option and cannot be 1069 * changed at run time or by strapping. Therefore the attached PHYs 1070 * should be programmed to only advertise settings which are supported 1071 * by the hardware. 1072 */ 1073 if (hellcreek->pdata->is_100_mbits) 1074 phylink_set(mask, 100baseT_Full); 1075 else 1076 phylink_set(mask, 1000baseT_Full); 1077 1078 bitmap_and(supported, supported, mask, 1079 __ETHTOOL_LINK_MODE_MASK_NBITS); 1080 bitmap_and(state->advertising, state->advertising, mask, 1081 __ETHTOOL_LINK_MODE_MASK_NBITS); 1082 } 1083 1084 static int 1085 hellcreek_port_prechangeupper(struct dsa_switch *ds, int port, 1086 struct netdev_notifier_changeupper_info *info) 1087 { 1088 struct hellcreek *hellcreek = ds->priv; 1089 bool used = true; 1090 int ret = -EBUSY; 1091 u16 vid; 1092 int i; 1093 1094 dev_dbg(hellcreek->dev, "Pre change upper for port %d\n", port); 1095 1096 /* 1097 * Deny VLAN devices on top of lan ports with the same VLAN ids, because 1098 * it breaks the port separation due to the private VLANs. Example: 1099 * 1100 * lan0.100 *and* lan1.100 cannot be used in parallel. However, lan0.99 1101 * and lan1.100 works. 1102 */ 1103 1104 if (!is_vlan_dev(info->upper_dev)) 1105 return 0; 1106 1107 vid = vlan_dev_vlan_id(info->upper_dev); 1108 1109 /* For all ports, check bitmaps */ 1110 mutex_lock(&hellcreek->vlan_lock); 1111 for (i = 0; i < hellcreek->pdata->num_ports; ++i) { 1112 if (!dsa_is_user_port(ds, i)) 1113 continue; 1114 1115 if (port == i) 1116 continue; 1117 1118 used = used && test_bit(vid, hellcreek->ports[i].vlan_dev_bitmap); 1119 } 1120 1121 if (used) 1122 goto out; 1123 1124 /* Update bitmap */ 1125 set_bit(vid, hellcreek->ports[port].vlan_dev_bitmap); 1126 1127 ret = 0; 1128 1129 out: 1130 mutex_unlock(&hellcreek->vlan_lock); 1131 1132 return ret; 1133 } 1134 1135 static void hellcreek_setup_gcl(struct hellcreek *hellcreek, int port, 1136 const struct tc_taprio_qopt_offload *schedule) 1137 { 1138 const struct tc_taprio_sched_entry *cur, *initial, *next; 1139 size_t i; 1140 1141 cur = initial = &schedule->entries[0]; 1142 next = cur + 1; 1143 1144 for (i = 1; i <= schedule->num_entries; ++i) { 1145 u16 data; 1146 u8 gates; 1147 1148 cur++; 1149 next++; 1150 1151 if (i == schedule->num_entries) 1152 gates = initial->gate_mask ^ 1153 cur->gate_mask; 1154 else 1155 gates = next->gate_mask ^ 1156 cur->gate_mask; 1157 1158 data = gates; 1159 1160 if (i == schedule->num_entries) 1161 data |= TR_GCLDAT_GCLWRLAST; 1162 1163 /* Gates states */ 1164 hellcreek_write(hellcreek, data, TR_GCLDAT); 1165 1166 /* Time interval */ 1167 hellcreek_write(hellcreek, 1168 cur->interval & 0x0000ffff, 1169 TR_GCLTIL); 1170 hellcreek_write(hellcreek, 1171 (cur->interval & 0xffff0000) >> 16, 1172 TR_GCLTIH); 1173 1174 /* Commit entry */ 1175 data = ((i - 1) << TR_GCLCMD_GCLWRADR_SHIFT) | 1176 (initial->gate_mask << 1177 TR_GCLCMD_INIT_GATE_STATES_SHIFT); 1178 hellcreek_write(hellcreek, data, TR_GCLCMD); 1179 } 1180 } 1181 1182 static void hellcreek_set_cycle_time(struct hellcreek *hellcreek, 1183 const struct tc_taprio_qopt_offload *schedule) 1184 { 1185 u32 cycle_time = schedule->cycle_time; 1186 1187 hellcreek_write(hellcreek, cycle_time & 0x0000ffff, TR_CTWRL); 1188 hellcreek_write(hellcreek, (cycle_time & 0xffff0000) >> 16, TR_CTWRH); 1189 } 1190 1191 static void hellcreek_switch_schedule(struct hellcreek *hellcreek, 1192 ktime_t start_time) 1193 { 1194 struct timespec64 ts = ktime_to_timespec64(start_time); 1195 1196 /* Start schedule at this point of time */ 1197 hellcreek_write(hellcreek, ts.tv_nsec & 0x0000ffff, TR_ESTWRL); 1198 hellcreek_write(hellcreek, (ts.tv_nsec & 0xffff0000) >> 16, TR_ESTWRH); 1199 1200 /* Arm timer, set seconds and switch schedule */ 1201 hellcreek_write(hellcreek, TR_ESTCMD_ESTARM | TR_ESTCMD_ESTSWCFG | 1202 ((ts.tv_sec & TR_ESTCMD_ESTSEC_MASK) << 1203 TR_ESTCMD_ESTSEC_SHIFT), TR_ESTCMD); 1204 } 1205 1206 static bool hellcreek_schedule_startable(struct hellcreek *hellcreek, int port) 1207 { 1208 struct hellcreek_port *hellcreek_port = &hellcreek->ports[port]; 1209 s64 base_time_ns, current_ns; 1210 1211 /* The switch allows a schedule to be started only eight seconds within 1212 * the future. Therefore, check the current PTP time if the schedule is 1213 * startable or not. 1214 */ 1215 1216 /* Use the "cached" time. That should be alright, as it's updated quite 1217 * frequently in the PTP code. 1218 */ 1219 mutex_lock(&hellcreek->ptp_lock); 1220 current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts; 1221 mutex_unlock(&hellcreek->ptp_lock); 1222 1223 /* Calculate difference to admin base time */ 1224 base_time_ns = ktime_to_ns(hellcreek_port->current_schedule->base_time); 1225 1226 return base_time_ns - current_ns < (s64)8 * NSEC_PER_SEC; 1227 } 1228 1229 static void hellcreek_start_schedule(struct hellcreek *hellcreek, int port) 1230 { 1231 struct hellcreek_port *hellcreek_port = &hellcreek->ports[port]; 1232 ktime_t base_time, current_time; 1233 s64 current_ns; 1234 u32 cycle_time; 1235 1236 /* First select port */ 1237 hellcreek_select_tgd(hellcreek, port); 1238 1239 /* Forward base time into the future if needed */ 1240 mutex_lock(&hellcreek->ptp_lock); 1241 current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts; 1242 mutex_unlock(&hellcreek->ptp_lock); 1243 1244 current_time = ns_to_ktime(current_ns); 1245 base_time = hellcreek_port->current_schedule->base_time; 1246 cycle_time = hellcreek_port->current_schedule->cycle_time; 1247 1248 if (ktime_compare(current_time, base_time) > 0) { 1249 s64 n; 1250 1251 n = div64_s64(ktime_sub_ns(current_time, base_time), 1252 cycle_time); 1253 base_time = ktime_add_ns(base_time, (n + 1) * cycle_time); 1254 } 1255 1256 /* Set admin base time and switch schedule */ 1257 hellcreek_switch_schedule(hellcreek, base_time); 1258 1259 taprio_offload_free(hellcreek_port->current_schedule); 1260 hellcreek_port->current_schedule = NULL; 1261 1262 dev_dbg(hellcreek->dev, "Armed EST timer for port %d\n", 1263 hellcreek_port->port); 1264 } 1265 1266 static void hellcreek_check_schedule(struct work_struct *work) 1267 { 1268 struct delayed_work *dw = to_delayed_work(work); 1269 struct hellcreek_port *hellcreek_port; 1270 struct hellcreek *hellcreek; 1271 bool startable; 1272 1273 hellcreek_port = dw_to_hellcreek_port(dw); 1274 hellcreek = hellcreek_port->hellcreek; 1275 1276 mutex_lock(&hellcreek->reg_lock); 1277 1278 /* Check starting time */ 1279 startable = hellcreek_schedule_startable(hellcreek, 1280 hellcreek_port->port); 1281 if (startable) { 1282 hellcreek_start_schedule(hellcreek, hellcreek_port->port); 1283 mutex_unlock(&hellcreek->reg_lock); 1284 return; 1285 } 1286 1287 mutex_unlock(&hellcreek->reg_lock); 1288 1289 /* Reschedule */ 1290 schedule_delayed_work(&hellcreek_port->schedule_work, 1291 HELLCREEK_SCHEDULE_PERIOD); 1292 } 1293 1294 static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port, 1295 struct tc_taprio_qopt_offload *taprio) 1296 { 1297 struct hellcreek *hellcreek = ds->priv; 1298 struct hellcreek_port *hellcreek_port; 1299 bool startable; 1300 u16 ctrl; 1301 1302 hellcreek_port = &hellcreek->ports[port]; 1303 1304 dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n", 1305 port); 1306 1307 /* First cancel delayed work */ 1308 cancel_delayed_work_sync(&hellcreek_port->schedule_work); 1309 1310 mutex_lock(&hellcreek->reg_lock); 1311 1312 if (hellcreek_port->current_schedule) { 1313 taprio_offload_free(hellcreek_port->current_schedule); 1314 hellcreek_port->current_schedule = NULL; 1315 } 1316 hellcreek_port->current_schedule = taprio_offload_get(taprio); 1317 1318 /* Then select port */ 1319 hellcreek_select_tgd(hellcreek, port); 1320 1321 /* Enable gating and keep defaults */ 1322 ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN; 1323 hellcreek_write(hellcreek, ctrl, TR_TGDCTRL); 1324 1325 /* Cancel pending schedule */ 1326 hellcreek_write(hellcreek, 0x00, TR_ESTCMD); 1327 1328 /* Setup a new schedule */ 1329 hellcreek_setup_gcl(hellcreek, port, hellcreek_port->current_schedule); 1330 1331 /* Configure cycle time */ 1332 hellcreek_set_cycle_time(hellcreek, hellcreek_port->current_schedule); 1333 1334 /* Check starting time */ 1335 startable = hellcreek_schedule_startable(hellcreek, port); 1336 if (startable) { 1337 hellcreek_start_schedule(hellcreek, port); 1338 mutex_unlock(&hellcreek->reg_lock); 1339 return 0; 1340 } 1341 1342 mutex_unlock(&hellcreek->reg_lock); 1343 1344 /* Schedule periodic schedule check */ 1345 schedule_delayed_work(&hellcreek_port->schedule_work, 1346 HELLCREEK_SCHEDULE_PERIOD); 1347 1348 return 0; 1349 } 1350 1351 static int hellcreek_port_del_schedule(struct dsa_switch *ds, int port) 1352 { 1353 struct hellcreek *hellcreek = ds->priv; 1354 struct hellcreek_port *hellcreek_port; 1355 1356 hellcreek_port = &hellcreek->ports[port]; 1357 1358 dev_dbg(hellcreek->dev, "Remove traffic schedule on port %d\n", port); 1359 1360 /* First cancel delayed work */ 1361 cancel_delayed_work_sync(&hellcreek_port->schedule_work); 1362 1363 mutex_lock(&hellcreek->reg_lock); 1364 1365 if (hellcreek_port->current_schedule) { 1366 taprio_offload_free(hellcreek_port->current_schedule); 1367 hellcreek_port->current_schedule = NULL; 1368 } 1369 1370 /* Then select port */ 1371 hellcreek_select_tgd(hellcreek, port); 1372 1373 /* Disable gating and return to regular switching flow */ 1374 hellcreek_write(hellcreek, 0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT, 1375 TR_TGDCTRL); 1376 1377 mutex_unlock(&hellcreek->reg_lock); 1378 1379 return 0; 1380 } 1381 1382 static bool hellcreek_validate_schedule(struct hellcreek *hellcreek, 1383 struct tc_taprio_qopt_offload *schedule) 1384 { 1385 size_t i; 1386 1387 /* Does this hellcreek version support Qbv in hardware? */ 1388 if (!hellcreek->pdata->qbv_support) 1389 return false; 1390 1391 /* cycle time can only be 32bit */ 1392 if (schedule->cycle_time > (u32)-1) 1393 return false; 1394 1395 /* cycle time extension is not supported */ 1396 if (schedule->cycle_time_extension) 1397 return false; 1398 1399 /* Only set command is supported */ 1400 for (i = 0; i < schedule->num_entries; ++i) 1401 if (schedule->entries[i].command != TC_TAPRIO_CMD_SET_GATES) 1402 return false; 1403 1404 return true; 1405 } 1406 1407 static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port, 1408 enum tc_setup_type type, void *type_data) 1409 { 1410 struct tc_taprio_qopt_offload *taprio = type_data; 1411 struct hellcreek *hellcreek = ds->priv; 1412 1413 if (type != TC_SETUP_QDISC_TAPRIO) 1414 return -EOPNOTSUPP; 1415 1416 if (!hellcreek_validate_schedule(hellcreek, taprio)) 1417 return -EOPNOTSUPP; 1418 1419 if (taprio->enable) 1420 return hellcreek_port_set_schedule(ds, port, taprio); 1421 1422 return hellcreek_port_del_schedule(ds, port); 1423 } 1424 1425 static const struct dsa_switch_ops hellcreek_ds_ops = { 1426 .get_ethtool_stats = hellcreek_get_ethtool_stats, 1427 .get_sset_count = hellcreek_get_sset_count, 1428 .get_strings = hellcreek_get_strings, 1429 .get_tag_protocol = hellcreek_get_tag_protocol, 1430 .get_ts_info = hellcreek_get_ts_info, 1431 .phylink_validate = hellcreek_phylink_validate, 1432 .port_bridge_join = hellcreek_port_bridge_join, 1433 .port_bridge_leave = hellcreek_port_bridge_leave, 1434 .port_disable = hellcreek_port_disable, 1435 .port_enable = hellcreek_port_enable, 1436 .port_fdb_add = hellcreek_fdb_add, 1437 .port_fdb_del = hellcreek_fdb_del, 1438 .port_fdb_dump = hellcreek_fdb_dump, 1439 .port_hwtstamp_set = hellcreek_port_hwtstamp_set, 1440 .port_hwtstamp_get = hellcreek_port_hwtstamp_get, 1441 .port_prechangeupper = hellcreek_port_prechangeupper, 1442 .port_rxtstamp = hellcreek_port_rxtstamp, 1443 .port_setup_tc = hellcreek_port_setup_tc, 1444 .port_stp_state_set = hellcreek_port_stp_state_set, 1445 .port_txtstamp = hellcreek_port_txtstamp, 1446 .port_vlan_add = hellcreek_vlan_add, 1447 .port_vlan_del = hellcreek_vlan_del, 1448 .port_vlan_filtering = hellcreek_vlan_filtering, 1449 .setup = hellcreek_setup, 1450 }; 1451 1452 static int hellcreek_probe(struct platform_device *pdev) 1453 { 1454 struct device *dev = &pdev->dev; 1455 struct hellcreek *hellcreek; 1456 struct resource *res; 1457 int ret, i; 1458 1459 hellcreek = devm_kzalloc(dev, sizeof(*hellcreek), GFP_KERNEL); 1460 if (!hellcreek) 1461 return -ENOMEM; 1462 1463 hellcreek->vidmbrcfg = devm_kcalloc(dev, VLAN_N_VID, 1464 sizeof(*hellcreek->vidmbrcfg), 1465 GFP_KERNEL); 1466 if (!hellcreek->vidmbrcfg) 1467 return -ENOMEM; 1468 1469 hellcreek->pdata = of_device_get_match_data(dev); 1470 1471 hellcreek->ports = devm_kcalloc(dev, hellcreek->pdata->num_ports, 1472 sizeof(*hellcreek->ports), 1473 GFP_KERNEL); 1474 if (!hellcreek->ports) 1475 return -ENOMEM; 1476 1477 for (i = 0; i < hellcreek->pdata->num_ports; ++i) { 1478 struct hellcreek_port *port = &hellcreek->ports[i]; 1479 1480 port->counter_values = 1481 devm_kcalloc(dev, 1482 ARRAY_SIZE(hellcreek_counter), 1483 sizeof(*port->counter_values), 1484 GFP_KERNEL); 1485 if (!port->counter_values) 1486 return -ENOMEM; 1487 1488 port->vlan_dev_bitmap = 1489 devm_kcalloc(dev, 1490 BITS_TO_LONGS(VLAN_N_VID), 1491 sizeof(unsigned long), 1492 GFP_KERNEL); 1493 if (!port->vlan_dev_bitmap) 1494 return -ENOMEM; 1495 1496 port->hellcreek = hellcreek; 1497 port->port = i; 1498 1499 INIT_DELAYED_WORK(&port->schedule_work, 1500 hellcreek_check_schedule); 1501 } 1502 1503 mutex_init(&hellcreek->reg_lock); 1504 mutex_init(&hellcreek->vlan_lock); 1505 mutex_init(&hellcreek->ptp_lock); 1506 1507 hellcreek->dev = dev; 1508 1509 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tsn"); 1510 if (!res) { 1511 dev_err(dev, "No memory region provided!\n"); 1512 return -ENODEV; 1513 } 1514 1515 hellcreek->base = devm_ioremap_resource(dev, res); 1516 if (IS_ERR(hellcreek->base)) { 1517 dev_err(dev, "No memory available!\n"); 1518 return PTR_ERR(hellcreek->base); 1519 } 1520 1521 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ptp"); 1522 if (!res) { 1523 dev_err(dev, "No PTP memory region provided!\n"); 1524 return -ENODEV; 1525 } 1526 1527 hellcreek->ptp_base = devm_ioremap_resource(dev, res); 1528 if (IS_ERR(hellcreek->ptp_base)) { 1529 dev_err(dev, "No memory available!\n"); 1530 return PTR_ERR(hellcreek->ptp_base); 1531 } 1532 1533 ret = hellcreek_detect(hellcreek); 1534 if (ret) { 1535 dev_err(dev, "No (known) chip found!\n"); 1536 return ret; 1537 } 1538 1539 ret = hellcreek_wait_until_ready(hellcreek); 1540 if (ret) { 1541 dev_err(dev, "Switch didn't become ready!\n"); 1542 return ret; 1543 } 1544 1545 hellcreek_feature_detect(hellcreek); 1546 1547 hellcreek->ds = devm_kzalloc(dev, sizeof(*hellcreek->ds), GFP_KERNEL); 1548 if (!hellcreek->ds) 1549 return -ENOMEM; 1550 1551 hellcreek->ds->dev = dev; 1552 hellcreek->ds->priv = hellcreek; 1553 hellcreek->ds->ops = &hellcreek_ds_ops; 1554 hellcreek->ds->num_ports = hellcreek->pdata->num_ports; 1555 hellcreek->ds->num_tx_queues = HELLCREEK_NUM_EGRESS_QUEUES; 1556 1557 ret = dsa_register_switch(hellcreek->ds); 1558 if (ret) { 1559 dev_err_probe(dev, ret, "Unable to register switch\n"); 1560 return ret; 1561 } 1562 1563 ret = hellcreek_ptp_setup(hellcreek); 1564 if (ret) { 1565 dev_err(dev, "Failed to setup PTP!\n"); 1566 goto err_ptp_setup; 1567 } 1568 1569 ret = hellcreek_hwtstamp_setup(hellcreek); 1570 if (ret) { 1571 dev_err(dev, "Failed to setup hardware timestamping!\n"); 1572 goto err_tstamp_setup; 1573 } 1574 1575 platform_set_drvdata(pdev, hellcreek); 1576 1577 return 0; 1578 1579 err_tstamp_setup: 1580 hellcreek_ptp_free(hellcreek); 1581 err_ptp_setup: 1582 dsa_unregister_switch(hellcreek->ds); 1583 1584 return ret; 1585 } 1586 1587 static int hellcreek_remove(struct platform_device *pdev) 1588 { 1589 struct hellcreek *hellcreek = platform_get_drvdata(pdev); 1590 1591 hellcreek_hwtstamp_free(hellcreek); 1592 hellcreek_ptp_free(hellcreek); 1593 dsa_unregister_switch(hellcreek->ds); 1594 platform_set_drvdata(pdev, NULL); 1595 1596 return 0; 1597 } 1598 1599 static const struct hellcreek_platform_data de1soc_r1_pdata = { 1600 .num_ports = 4, 1601 .is_100_mbits = 1, 1602 .qbv_support = 1, 1603 .qbv_on_cpu_port = 1, 1604 .qbu_support = 0, 1605 .module_id = 0x4c30, 1606 }; 1607 1608 static const struct of_device_id hellcreek_of_match[] = { 1609 { 1610 .compatible = "hirschmann,hellcreek-de1soc-r1", 1611 .data = &de1soc_r1_pdata, 1612 }, 1613 { /* sentinel */ }, 1614 }; 1615 MODULE_DEVICE_TABLE(of, hellcreek_of_match); 1616 1617 static struct platform_driver hellcreek_driver = { 1618 .probe = hellcreek_probe, 1619 .remove = hellcreek_remove, 1620 .driver = { 1621 .name = "hellcreek", 1622 .of_match_table = hellcreek_of_match, 1623 }, 1624 }; 1625 module_platform_driver(hellcreek_driver); 1626 1627 MODULE_AUTHOR("Kurt Kanzenbach <kurt@linutronix.de>"); 1628 MODULE_DESCRIPTION("Hirschmann Hellcreek driver"); 1629 MODULE_LICENSE("Dual MIT/GPL"); 1630