linux/drivers/net/dsa/hirschmann/hellcreek.c
<<
>>
Prefs
   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
  31static 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
  96static u16 hellcreek_read(struct hellcreek *hellcreek, unsigned int offset)
  97{
  98        return readw(hellcreek->base + offset);
  99}
 100
 101static u16 hellcreek_read_ctrl(struct hellcreek *hellcreek)
 102{
 103        return readw(hellcreek->base + HR_CTRL_C);
 104}
 105
 106static u16 hellcreek_read_stat(struct hellcreek *hellcreek)
 107{
 108        return readw(hellcreek->base + HR_SWSTAT);
 109}
 110
 111static void hellcreek_write(struct hellcreek *hellcreek, u16 data,
 112                            unsigned int offset)
 113{
 114        writew(data, hellcreek->base + offset);
 115}
 116
 117static 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
 124static 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
 131static 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
 141static 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
 156static 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
 163static 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
 173static 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
 182static 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
 191static 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
 218static void hellcreek_feature_detect(struct hellcreek *hellcreek)
 219{
 220        u16 features;
 221
 222        features = hellcreek_read(hellcreek, HR_FEABITS0);
 223
 224        /* Only detect the size of the FDB table. The size and current
 225         * utilization can be queried via devlink.
 226         */
 227        hellcreek->fdb_entries = ((features & HR_FEABITS0_FDBBINS_MASK) >>
 228                               HR_FEABITS0_FDBBINS_SHIFT) * 32;
 229}
 230
 231static enum dsa_tag_protocol hellcreek_get_tag_protocol(struct dsa_switch *ds,
 232                                                        int port,
 233                                                        enum dsa_tag_protocol mp)
 234{
 235        return DSA_TAG_PROTO_HELLCREEK;
 236}
 237
 238static int hellcreek_port_enable(struct dsa_switch *ds, int port,
 239                                 struct phy_device *phy)
 240{
 241        struct hellcreek *hellcreek = ds->priv;
 242        struct hellcreek_port *hellcreek_port;
 243        u16 val;
 244
 245        hellcreek_port = &hellcreek->ports[port];
 246
 247        dev_dbg(hellcreek->dev, "Enable port %d\n", port);
 248
 249        mutex_lock(&hellcreek->reg_lock);
 250
 251        hellcreek_select_port(hellcreek, port);
 252        val = hellcreek_port->ptcfg;
 253        val |= HR_PTCFG_ADMIN_EN;
 254        hellcreek_write(hellcreek, val, HR_PTCFG);
 255        hellcreek_port->ptcfg = val;
 256
 257        mutex_unlock(&hellcreek->reg_lock);
 258
 259        return 0;
 260}
 261
 262static void hellcreek_port_disable(struct dsa_switch *ds, int port)
 263{
 264        struct hellcreek *hellcreek = ds->priv;
 265        struct hellcreek_port *hellcreek_port;
 266        u16 val;
 267
 268        hellcreek_port = &hellcreek->ports[port];
 269
 270        dev_dbg(hellcreek->dev, "Disable port %d\n", port);
 271
 272        mutex_lock(&hellcreek->reg_lock);
 273
 274        hellcreek_select_port(hellcreek, port);
 275        val = hellcreek_port->ptcfg;
 276        val &= ~HR_PTCFG_ADMIN_EN;
 277        hellcreek_write(hellcreek, val, HR_PTCFG);
 278        hellcreek_port->ptcfg = val;
 279
 280        mutex_unlock(&hellcreek->reg_lock);
 281}
 282
 283static void hellcreek_get_strings(struct dsa_switch *ds, int port,
 284                                  u32 stringset, uint8_t *data)
 285{
 286        int i;
 287
 288        for (i = 0; i < ARRAY_SIZE(hellcreek_counter); ++i) {
 289                const struct hellcreek_counter *counter = &hellcreek_counter[i];
 290
 291                strlcpy(data + i * ETH_GSTRING_LEN,
 292                        counter->name, ETH_GSTRING_LEN);
 293        }
 294}
 295
 296static int hellcreek_get_sset_count(struct dsa_switch *ds, int port, int sset)
 297{
 298        if (sset != ETH_SS_STATS)
 299                return 0;
 300
 301        return ARRAY_SIZE(hellcreek_counter);
 302}
 303
 304static void hellcreek_get_ethtool_stats(struct dsa_switch *ds, int port,
 305                                        uint64_t *data)
 306{
 307        struct hellcreek *hellcreek = ds->priv;
 308        struct hellcreek_port *hellcreek_port;
 309        int i;
 310
 311        hellcreek_port = &hellcreek->ports[port];
 312
 313        for (i = 0; i < ARRAY_SIZE(hellcreek_counter); ++i) {
 314                const struct hellcreek_counter *counter = &hellcreek_counter[i];
 315                u8 offset = counter->offset + port * 64;
 316                u16 high, low;
 317                u64 value;
 318
 319                mutex_lock(&hellcreek->reg_lock);
 320
 321                hellcreek_select_counter(hellcreek, offset);
 322
 323                /* The registers are locked internally by selecting the
 324                 * counter. So low and high can be read without reading high
 325                 * again.
 326                 */
 327                high  = hellcreek_read(hellcreek, HR_CRDH);
 328                low   = hellcreek_read(hellcreek, HR_CRDL);
 329                value = ((u64)high << 16) | low;
 330
 331                hellcreek_port->counter_values[i] += value;
 332                data[i] = hellcreek_port->counter_values[i];
 333
 334                mutex_unlock(&hellcreek->reg_lock);
 335        }
 336}
 337
 338static u16 hellcreek_private_vid(int port)
 339{
 340        return VLAN_N_VID - port + 1;
 341}
 342
 343static int hellcreek_vlan_prepare(struct dsa_switch *ds, int port,
 344                                  const struct switchdev_obj_port_vlan *vlan,
 345                                  struct netlink_ext_ack *extack)
 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                        NL_SET_ERR_MSG_MOD(extack, "VID restricted by driver");
 364                        return -EBUSY;
 365                }
 366        }
 367
 368        return 0;
 369}
 370
 371static void hellcreek_select_vlan_params(struct hellcreek *hellcreek, int port,
 372                                         int *shift, int *mask)
 373{
 374        switch (port) {
 375        case 0:
 376                *shift = HR_VIDMBRCFG_P0MBR_SHIFT;
 377                *mask  = HR_VIDMBRCFG_P0MBR_MASK;
 378                break;
 379        case 1:
 380                *shift = HR_VIDMBRCFG_P1MBR_SHIFT;
 381                *mask  = HR_VIDMBRCFG_P1MBR_MASK;
 382                break;
 383        case 2:
 384                *shift = HR_VIDMBRCFG_P2MBR_SHIFT;
 385                *mask  = HR_VIDMBRCFG_P2MBR_MASK;
 386                break;
 387        case 3:
 388                *shift = HR_VIDMBRCFG_P3MBR_SHIFT;
 389                *mask  = HR_VIDMBRCFG_P3MBR_MASK;
 390                break;
 391        default:
 392                *shift = *mask = 0;
 393                dev_err(hellcreek->dev, "Unknown port %d selected!\n", port);
 394        }
 395}
 396
 397static void hellcreek_apply_vlan(struct hellcreek *hellcreek, int port, u16 vid,
 398                                 bool pvid, bool untagged)
 399{
 400        int shift, mask;
 401        u16 val;
 402
 403        dev_dbg(hellcreek->dev, "Apply VLAN: port=%d vid=%u pvid=%d untagged=%d",
 404                port, vid, pvid, untagged);
 405
 406        mutex_lock(&hellcreek->reg_lock);
 407
 408        hellcreek_select_port(hellcreek, port);
 409        hellcreek_select_vlan(hellcreek, vid, pvid);
 410
 411        /* Setup port vlan membership */
 412        hellcreek_select_vlan_params(hellcreek, port, &shift, &mask);
 413        val = hellcreek->vidmbrcfg[vid];
 414        val &= ~mask;
 415        if (untagged)
 416                val |= HELLCREEK_VLAN_UNTAGGED_MEMBER << shift;
 417        else
 418                val |= HELLCREEK_VLAN_TAGGED_MEMBER << shift;
 419
 420        hellcreek_write(hellcreek, val, HR_VIDMBRCFG);
 421        hellcreek->vidmbrcfg[vid] = val;
 422
 423        mutex_unlock(&hellcreek->reg_lock);
 424}
 425
 426static void hellcreek_unapply_vlan(struct hellcreek *hellcreek, int port,
 427                                   u16 vid)
 428{
 429        int shift, mask;
 430        u16 val;
 431
 432        dev_dbg(hellcreek->dev, "Unapply VLAN: port=%d vid=%u\n", port, vid);
 433
 434        mutex_lock(&hellcreek->reg_lock);
 435
 436        hellcreek_select_vlan(hellcreek, vid, false);
 437
 438        /* Setup port vlan membership */
 439        hellcreek_select_vlan_params(hellcreek, port, &shift, &mask);
 440        val = hellcreek->vidmbrcfg[vid];
 441        val &= ~mask;
 442        val |= HELLCREEK_VLAN_NO_MEMBER << shift;
 443
 444        hellcreek_write(hellcreek, val, HR_VIDMBRCFG);
 445        hellcreek->vidmbrcfg[vid] = val;
 446
 447        mutex_unlock(&hellcreek->reg_lock);
 448}
 449
 450static int hellcreek_vlan_add(struct dsa_switch *ds, int port,
 451                              const struct switchdev_obj_port_vlan *vlan,
 452                              struct netlink_ext_ack *extack)
 453{
 454        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
 455        bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
 456        struct hellcreek *hellcreek = ds->priv;
 457        int err;
 458
 459        err = hellcreek_vlan_prepare(ds, port, vlan, extack);
 460        if (err)
 461                return err;
 462
 463        dev_dbg(hellcreek->dev, "Add VLAN %d on port %d, %s, %s\n",
 464                vlan->vid, port, untagged ? "untagged" : "tagged",
 465                pvid ? "PVID" : "no PVID");
 466
 467        hellcreek_apply_vlan(hellcreek, port, vlan->vid, pvid, untagged);
 468
 469        return 0;
 470}
 471
 472static int hellcreek_vlan_del(struct dsa_switch *ds, int port,
 473                              const struct switchdev_obj_port_vlan *vlan)
 474{
 475        struct hellcreek *hellcreek = ds->priv;
 476
 477        dev_dbg(hellcreek->dev, "Remove VLAN %d on port %d\n", vlan->vid, port);
 478
 479        hellcreek_unapply_vlan(hellcreek, port, vlan->vid);
 480
 481        return 0;
 482}
 483
 484static void hellcreek_port_stp_state_set(struct dsa_switch *ds, int port,
 485                                         u8 state)
 486{
 487        struct hellcreek *hellcreek = ds->priv;
 488        struct hellcreek_port *hellcreek_port;
 489        const char *new_state;
 490        u16 val;
 491
 492        mutex_lock(&hellcreek->reg_lock);
 493
 494        hellcreek_port = &hellcreek->ports[port];
 495        val = hellcreek_port->ptcfg;
 496
 497        switch (state) {
 498        case BR_STATE_DISABLED:
 499                new_state = "DISABLED";
 500                val |= HR_PTCFG_BLOCKED;
 501                val &= ~HR_PTCFG_LEARNING_EN;
 502                break;
 503        case BR_STATE_BLOCKING:
 504                new_state = "BLOCKING";
 505                val |= HR_PTCFG_BLOCKED;
 506                val &= ~HR_PTCFG_LEARNING_EN;
 507                break;
 508        case BR_STATE_LISTENING:
 509                new_state = "LISTENING";
 510                val |= HR_PTCFG_BLOCKED;
 511                val &= ~HR_PTCFG_LEARNING_EN;
 512                break;
 513        case BR_STATE_LEARNING:
 514                new_state = "LEARNING";
 515                val |= HR_PTCFG_BLOCKED;
 516                val |= HR_PTCFG_LEARNING_EN;
 517                break;
 518        case BR_STATE_FORWARDING:
 519                new_state = "FORWARDING";
 520                val &= ~HR_PTCFG_BLOCKED;
 521                val |= HR_PTCFG_LEARNING_EN;
 522                break;
 523        default:
 524                new_state = "UNKNOWN";
 525        }
 526
 527        hellcreek_select_port(hellcreek, port);
 528        hellcreek_write(hellcreek, val, HR_PTCFG);
 529        hellcreek_port->ptcfg = val;
 530
 531        mutex_unlock(&hellcreek->reg_lock);
 532
 533        dev_dbg(hellcreek->dev, "Configured STP state for port %d: %s\n",
 534                port, new_state);
 535}
 536
 537static void hellcreek_setup_ingressflt(struct hellcreek *hellcreek, int port,
 538                                       bool enable)
 539{
 540        struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
 541        u16 ptcfg;
 542
 543        mutex_lock(&hellcreek->reg_lock);
 544
 545        ptcfg = hellcreek_port->ptcfg;
 546
 547        if (enable)
 548                ptcfg |= HR_PTCFG_INGRESSFLT;
 549        else
 550                ptcfg &= ~HR_PTCFG_INGRESSFLT;
 551
 552        hellcreek_select_port(hellcreek, port);
 553        hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
 554        hellcreek_port->ptcfg = ptcfg;
 555
 556        mutex_unlock(&hellcreek->reg_lock);
 557}
 558
 559static void hellcreek_setup_vlan_awareness(struct hellcreek *hellcreek,
 560                                           bool enable)
 561{
 562        u16 swcfg;
 563
 564        mutex_lock(&hellcreek->reg_lock);
 565
 566        swcfg = hellcreek->swcfg;
 567
 568        if (enable)
 569                swcfg |= HR_SWCFG_VLAN_UNAWARE;
 570        else
 571                swcfg &= ~HR_SWCFG_VLAN_UNAWARE;
 572
 573        hellcreek_write(hellcreek, swcfg, HR_SWCFG);
 574
 575        mutex_unlock(&hellcreek->reg_lock);
 576}
 577
 578/* Default setup for DSA: VLAN <X>: CPU and Port <X> egress untagged. */
 579static void hellcreek_setup_vlan_membership(struct dsa_switch *ds, int port,
 580                                            bool enabled)
 581{
 582        const u16 vid = hellcreek_private_vid(port);
 583        int upstream = dsa_upstream_port(ds, port);
 584        struct hellcreek *hellcreek = ds->priv;
 585
 586        /* Apply vid to port as egress untagged and port vlan id */
 587        if (enabled)
 588                hellcreek_apply_vlan(hellcreek, port, vid, true, true);
 589        else
 590                hellcreek_unapply_vlan(hellcreek, port, vid);
 591
 592        /* Apply vid to cpu port as well */
 593        if (enabled)
 594                hellcreek_apply_vlan(hellcreek, upstream, vid, false, true);
 595        else
 596                hellcreek_unapply_vlan(hellcreek, upstream, vid);
 597}
 598
 599static void hellcreek_port_set_ucast_flood(struct hellcreek *hellcreek,
 600                                           int port, bool enable)
 601{
 602        struct hellcreek_port *hellcreek_port;
 603        u16 val;
 604
 605        hellcreek_port = &hellcreek->ports[port];
 606
 607        dev_dbg(hellcreek->dev, "%s unicast flooding on port %d\n",
 608                enable ? "Enable" : "Disable", port);
 609
 610        mutex_lock(&hellcreek->reg_lock);
 611
 612        hellcreek_select_port(hellcreek, port);
 613        val = hellcreek_port->ptcfg;
 614        if (enable)
 615                val &= ~HR_PTCFG_UUC_FLT;
 616        else
 617                val |= HR_PTCFG_UUC_FLT;
 618        hellcreek_write(hellcreek, val, HR_PTCFG);
 619        hellcreek_port->ptcfg = val;
 620
 621        mutex_unlock(&hellcreek->reg_lock);
 622}
 623
 624static void hellcreek_port_set_mcast_flood(struct hellcreek *hellcreek,
 625                                           int port, bool enable)
 626{
 627        struct hellcreek_port *hellcreek_port;
 628        u16 val;
 629
 630        hellcreek_port = &hellcreek->ports[port];
 631
 632        dev_dbg(hellcreek->dev, "%s multicast flooding on port %d\n",
 633                enable ? "Enable" : "Disable", port);
 634
 635        mutex_lock(&hellcreek->reg_lock);
 636
 637        hellcreek_select_port(hellcreek, port);
 638        val = hellcreek_port->ptcfg;
 639        if (enable)
 640                val &= ~HR_PTCFG_UMC_FLT;
 641        else
 642                val |= HR_PTCFG_UMC_FLT;
 643        hellcreek_write(hellcreek, val, HR_PTCFG);
 644        hellcreek_port->ptcfg = val;
 645
 646        mutex_unlock(&hellcreek->reg_lock);
 647}
 648
 649static int hellcreek_pre_bridge_flags(struct dsa_switch *ds, int port,
 650                                      struct switchdev_brport_flags flags,
 651                                      struct netlink_ext_ack *extack)
 652{
 653        if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD))
 654                return -EINVAL;
 655
 656        return 0;
 657}
 658
 659static int hellcreek_bridge_flags(struct dsa_switch *ds, int port,
 660                                  struct switchdev_brport_flags flags,
 661                                  struct netlink_ext_ack *extack)
 662{
 663        struct hellcreek *hellcreek = ds->priv;
 664
 665        if (flags.mask & BR_FLOOD)
 666                hellcreek_port_set_ucast_flood(hellcreek, port,
 667                                               !!(flags.val & BR_FLOOD));
 668
 669        if (flags.mask & BR_MCAST_FLOOD)
 670                hellcreek_port_set_mcast_flood(hellcreek, port,
 671                                               !!(flags.val & BR_MCAST_FLOOD));
 672
 673        return 0;
 674}
 675
 676static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port,
 677                                      struct net_device *br)
 678{
 679        struct hellcreek *hellcreek = ds->priv;
 680
 681        dev_dbg(hellcreek->dev, "Port %d joins a bridge\n", port);
 682
 683        /* When joining a vlan_filtering bridge, keep the switch VLAN aware */
 684        if (!ds->vlan_filtering)
 685                hellcreek_setup_vlan_awareness(hellcreek, false);
 686
 687        /* Drop private vlans */
 688        hellcreek_setup_vlan_membership(ds, port, false);
 689
 690        return 0;
 691}
 692
 693static void hellcreek_port_bridge_leave(struct dsa_switch *ds, int port,
 694                                        struct net_device *br)
 695{
 696        struct hellcreek *hellcreek = ds->priv;
 697
 698        dev_dbg(hellcreek->dev, "Port %d leaves a bridge\n", port);
 699
 700        /* Enable VLAN awareness */
 701        hellcreek_setup_vlan_awareness(hellcreek, true);
 702
 703        /* Enable private vlans */
 704        hellcreek_setup_vlan_membership(ds, port, true);
 705}
 706
 707static int __hellcreek_fdb_add(struct hellcreek *hellcreek,
 708                               const struct hellcreek_fdb_entry *entry)
 709{
 710        u16 meta = 0;
 711
 712        dev_dbg(hellcreek->dev, "Add static FDB entry: MAC=%pM, MASK=0x%02x, "
 713                "OBT=%d, REPRIO_EN=%d, PRIO=%d\n", entry->mac, entry->portmask,
 714                entry->is_obt, entry->reprio_en, entry->reprio_tc);
 715
 716        /* Add mac address */
 717        hellcreek_write(hellcreek, entry->mac[1] | (entry->mac[0] << 8), HR_FDBWDH);
 718        hellcreek_write(hellcreek, entry->mac[3] | (entry->mac[2] << 8), HR_FDBWDM);
 719        hellcreek_write(hellcreek, entry->mac[5] | (entry->mac[4] << 8), HR_FDBWDL);
 720
 721        /* Meta data */
 722        meta |= entry->portmask << HR_FDBWRM0_PORTMASK_SHIFT;
 723        if (entry->is_obt)
 724                meta |= HR_FDBWRM0_OBT;
 725        if (entry->reprio_en) {
 726                meta |= HR_FDBWRM0_REPRIO_EN;
 727                meta |= entry->reprio_tc << HR_FDBWRM0_REPRIO_TC_SHIFT;
 728        }
 729        hellcreek_write(hellcreek, meta, HR_FDBWRM0);
 730
 731        /* Commit */
 732        hellcreek_write(hellcreek, 0x00, HR_FDBWRCMD);
 733
 734        /* Wait until done */
 735        return hellcreek_wait_fdb_ready(hellcreek);
 736}
 737
 738static int __hellcreek_fdb_del(struct hellcreek *hellcreek,
 739                               const struct hellcreek_fdb_entry *entry)
 740{
 741        dev_dbg(hellcreek->dev, "Delete FDB entry: MAC=%pM!\n", entry->mac);
 742
 743        /* Delete by matching idx */
 744        hellcreek_write(hellcreek, entry->idx | HR_FDBWRCMD_FDBDEL, HR_FDBWRCMD);
 745
 746        /* Wait until done */
 747        return hellcreek_wait_fdb_ready(hellcreek);
 748}
 749
 750static void hellcreek_populate_fdb_entry(struct hellcreek *hellcreek,
 751                                         struct hellcreek_fdb_entry *entry,
 752                                         size_t idx)
 753{
 754        unsigned char addr[ETH_ALEN];
 755        u16 meta, mac;
 756
 757        /* Read values */
 758        meta    = hellcreek_read(hellcreek, HR_FDBMDRD);
 759        mac     = hellcreek_read(hellcreek, HR_FDBRDL);
 760        addr[5] = mac & 0xff;
 761        addr[4] = (mac & 0xff00) >> 8;
 762        mac     = hellcreek_read(hellcreek, HR_FDBRDM);
 763        addr[3] = mac & 0xff;
 764        addr[2] = (mac & 0xff00) >> 8;
 765        mac     = hellcreek_read(hellcreek, HR_FDBRDH);
 766        addr[1] = mac & 0xff;
 767        addr[0] = (mac & 0xff00) >> 8;
 768
 769        /* Populate @entry */
 770        memcpy(entry->mac, addr, sizeof(addr));
 771        entry->idx          = idx;
 772        entry->portmask     = (meta & HR_FDBMDRD_PORTMASK_MASK) >>
 773                HR_FDBMDRD_PORTMASK_SHIFT;
 774        entry->age          = (meta & HR_FDBMDRD_AGE_MASK) >>
 775                HR_FDBMDRD_AGE_SHIFT;
 776        entry->is_obt       = !!(meta & HR_FDBMDRD_OBT);
 777        entry->pass_blocked = !!(meta & HR_FDBMDRD_PASS_BLOCKED);
 778        entry->is_static    = !!(meta & HR_FDBMDRD_STATIC);
 779        entry->reprio_tc    = (meta & HR_FDBMDRD_REPRIO_TC_MASK) >>
 780                HR_FDBMDRD_REPRIO_TC_SHIFT;
 781        entry->reprio_en    = !!(meta & HR_FDBMDRD_REPRIO_EN);
 782}
 783
 784/* Retrieve the index of a FDB entry by mac address. Currently we search through
 785 * the complete table in hardware. If that's too slow, we might have to cache
 786 * the complete FDB table in software.
 787 */
 788static int hellcreek_fdb_get(struct hellcreek *hellcreek,
 789                             const unsigned char *dest,
 790                             struct hellcreek_fdb_entry *entry)
 791{
 792        size_t i;
 793
 794        /* Set read pointer to zero: The read of HR_FDBMAX (read-only register)
 795         * should reset the internal pointer. But, that doesn't work. The vendor
 796         * suggested a subsequent write as workaround. Same for HR_FDBRDH below.
 797         */
 798        hellcreek_read(hellcreek, HR_FDBMAX);
 799        hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
 800
 801        /* We have to read the complete table, because the switch/driver might
 802         * enter new entries anywhere.
 803         */
 804        for (i = 0; i < hellcreek->fdb_entries; ++i) {
 805                struct hellcreek_fdb_entry tmp = { 0 };
 806
 807                /* Read entry */
 808                hellcreek_populate_fdb_entry(hellcreek, &tmp, i);
 809
 810                /* Force next entry */
 811                hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
 812
 813                if (memcmp(tmp.mac, dest, ETH_ALEN))
 814                        continue;
 815
 816                /* Match found */
 817                memcpy(entry, &tmp, sizeof(*entry));
 818
 819                return 0;
 820        }
 821
 822        return -ENOENT;
 823}
 824
 825static int hellcreek_fdb_add(struct dsa_switch *ds, int port,
 826                             const unsigned char *addr, u16 vid)
 827{
 828        struct hellcreek_fdb_entry entry = { 0 };
 829        struct hellcreek *hellcreek = ds->priv;
 830        int ret;
 831
 832        dev_dbg(hellcreek->dev, "Add FDB entry for MAC=%pM\n", addr);
 833
 834        mutex_lock(&hellcreek->reg_lock);
 835
 836        ret = hellcreek_fdb_get(hellcreek, addr, &entry);
 837        if (ret) {
 838                /* Not found */
 839                memcpy(entry.mac, addr, sizeof(entry.mac));
 840                entry.portmask = BIT(port);
 841
 842                ret = __hellcreek_fdb_add(hellcreek, &entry);
 843                if (ret) {
 844                        dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
 845                        goto out;
 846                }
 847        } else {
 848                /* Found */
 849                ret = __hellcreek_fdb_del(hellcreek, &entry);
 850                if (ret) {
 851                        dev_err(hellcreek->dev, "Failed to delete FDB entry!\n");
 852                        goto out;
 853                }
 854
 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        }
 863
 864out:
 865        mutex_unlock(&hellcreek->reg_lock);
 866
 867        return ret;
 868}
 869
 870static int hellcreek_fdb_del(struct dsa_switch *ds, int port,
 871                             const unsigned char *addr, u16 vid)
 872{
 873        struct hellcreek_fdb_entry entry = { 0 };
 874        struct hellcreek *hellcreek = ds->priv;
 875        int ret;
 876
 877        dev_dbg(hellcreek->dev, "Delete FDB entry for MAC=%pM\n", addr);
 878
 879        mutex_lock(&hellcreek->reg_lock);
 880
 881        ret = hellcreek_fdb_get(hellcreek, addr, &entry);
 882        if (ret) {
 883                /* Not found */
 884                dev_err(hellcreek->dev, "FDB entry for deletion not found!\n");
 885        } else {
 886                /* Found */
 887                ret = __hellcreek_fdb_del(hellcreek, &entry);
 888                if (ret) {
 889                        dev_err(hellcreek->dev, "Failed to delete FDB entry!\n");
 890                        goto out;
 891                }
 892
 893                entry.portmask &= ~BIT(port);
 894
 895                if (entry.portmask != 0x00) {
 896                        ret = __hellcreek_fdb_add(hellcreek, &entry);
 897                        if (ret) {
 898                                dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
 899                                goto out;
 900                        }
 901                }
 902        }
 903
 904out:
 905        mutex_unlock(&hellcreek->reg_lock);
 906
 907        return ret;
 908}
 909
 910static int hellcreek_fdb_dump(struct dsa_switch *ds, int port,
 911                              dsa_fdb_dump_cb_t *cb, void *data)
 912{
 913        struct hellcreek *hellcreek = ds->priv;
 914        u16 entries;
 915        size_t i;
 916
 917        mutex_lock(&hellcreek->reg_lock);
 918
 919        /* Set read pointer to zero: The read of HR_FDBMAX (read-only register)
 920         * should reset the internal pointer. But, that doesn't work. The vendor
 921         * suggested a subsequent write as workaround. Same for HR_FDBRDH below.
 922         */
 923        entries = hellcreek_read(hellcreek, HR_FDBMAX);
 924        hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
 925
 926        dev_dbg(hellcreek->dev, "FDB dump for port %d, entries=%d!\n", port, entries);
 927
 928        /* Read table */
 929        for (i = 0; i < hellcreek->fdb_entries; ++i) {
 930                unsigned char null_addr[ETH_ALEN] = { 0 };
 931                struct hellcreek_fdb_entry entry = { 0 };
 932
 933                /* Read entry */
 934                hellcreek_populate_fdb_entry(hellcreek, &entry, i);
 935
 936                /* Force next entry */
 937                hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
 938
 939                /* Check valid */
 940                if (!memcmp(entry.mac, null_addr, ETH_ALEN))
 941                        continue;
 942
 943                /* Check port mask */
 944                if (!(entry.portmask & BIT(port)))
 945                        continue;
 946
 947                cb(entry.mac, 0, entry.is_static, data);
 948        }
 949
 950        mutex_unlock(&hellcreek->reg_lock);
 951
 952        return 0;
 953}
 954
 955static int hellcreek_vlan_filtering(struct dsa_switch *ds, int port,
 956                                    bool vlan_filtering,
 957                                    struct netlink_ext_ack *extack)
 958{
 959        struct hellcreek *hellcreek = ds->priv;
 960
 961        dev_dbg(hellcreek->dev, "%s VLAN filtering on port %d\n",
 962                vlan_filtering ? "Enable" : "Disable", port);
 963
 964        /* Configure port to drop packages with not known vids */
 965        hellcreek_setup_ingressflt(hellcreek, port, vlan_filtering);
 966
 967        /* Enable VLAN awareness on the switch. This save due to
 968         * ds->vlan_filtering_is_global.
 969         */
 970        hellcreek_setup_vlan_awareness(hellcreek, vlan_filtering);
 971
 972        return 0;
 973}
 974
 975static int hellcreek_enable_ip_core(struct hellcreek *hellcreek)
 976{
 977        int ret;
 978        u16 val;
 979
 980        mutex_lock(&hellcreek->reg_lock);
 981
 982        val = hellcreek_read(hellcreek, HR_CTRL_C);
 983        val |= HR_CTRL_C_ENABLE;
 984        hellcreek_write(hellcreek, val, HR_CTRL_C);
 985        ret = hellcreek_wait_until_transitioned(hellcreek);
 986
 987        mutex_unlock(&hellcreek->reg_lock);
 988
 989        return ret;
 990}
 991
 992static void hellcreek_setup_cpu_and_tunnel_port(struct hellcreek *hellcreek)
 993{
 994        struct hellcreek_port *tunnel_port = &hellcreek->ports[TUNNEL_PORT];
 995        struct hellcreek_port *cpu_port = &hellcreek->ports[CPU_PORT];
 996        u16 ptcfg = 0;
 997
 998        ptcfg |= HR_PTCFG_LEARNING_EN | HR_PTCFG_ADMIN_EN;
 999
1000        mutex_lock(&hellcreek->reg_lock);
1001
1002        hellcreek_select_port(hellcreek, CPU_PORT);
1003        hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
1004
1005        hellcreek_select_port(hellcreek, TUNNEL_PORT);
1006        hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
1007
1008        cpu_port->ptcfg    = ptcfg;
1009        tunnel_port->ptcfg = ptcfg;
1010
1011        mutex_unlock(&hellcreek->reg_lock);
1012}
1013
1014static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
1015{
1016        int i;
1017
1018        /* The switch has multiple egress queues per port. The queue is selected
1019         * via the PCP field in the VLAN header. The switch internally deals
1020         * with traffic classes instead of PCP values and this mapping is
1021         * configurable.
1022         *
1023         * The default mapping is (PCP - TC):
1024         *  7 - 7
1025         *  6 - 6
1026         *  5 - 5
1027         *  4 - 4
1028         *  3 - 3
1029         *  2 - 1
1030         *  1 - 0
1031         *  0 - 2
1032         *
1033         * The default should be an identity mapping.
1034         */
1035
1036        for (i = 0; i < 8; ++i) {
1037                mutex_lock(&hellcreek->reg_lock);
1038
1039                hellcreek_select_prio(hellcreek, i);
1040                hellcreek_write(hellcreek,
1041                                i << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
1042                                HR_PRTCCFG);
1043
1044                mutex_unlock(&hellcreek->reg_lock);
1045        }
1046}
1047
1048static int hellcreek_setup_fdb(struct hellcreek *hellcreek)
1049{
1050        static struct hellcreek_fdb_entry ptp = {
1051                /* MAC: 01-1B-19-00-00-00 */
1052                .mac          = { 0x01, 0x1b, 0x19, 0x00, 0x00, 0x00 },
1053                .portmask     = 0x03,   /* Management ports */
1054                .age          = 0,
1055                .is_obt       = 0,
1056                .pass_blocked = 0,
1057                .is_static    = 1,
1058                .reprio_tc    = 6,      /* TC: 6 as per IEEE 802.1AS */
1059                .reprio_en    = 1,
1060        };
1061        static struct hellcreek_fdb_entry p2p = {
1062                /* MAC: 01-80-C2-00-00-0E */
1063                .mac          = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e },
1064                .portmask     = 0x03,   /* Management ports */
1065                .age          = 0,
1066                .is_obt       = 0,
1067                .pass_blocked = 0,
1068                .is_static    = 1,
1069                .reprio_tc    = 6,      /* TC: 6 as per IEEE 802.1AS */
1070                .reprio_en    = 1,
1071        };
1072        int ret;
1073
1074        mutex_lock(&hellcreek->reg_lock);
1075        ret = __hellcreek_fdb_add(hellcreek, &ptp);
1076        if (ret)
1077                goto out;
1078        ret = __hellcreek_fdb_add(hellcreek, &p2p);
1079out:
1080        mutex_unlock(&hellcreek->reg_lock);
1081
1082        return ret;
1083}
1084
1085static int hellcreek_devlink_info_get(struct dsa_switch *ds,
1086                                      struct devlink_info_req *req,
1087                                      struct netlink_ext_ack *extack)
1088{
1089        struct hellcreek *hellcreek = ds->priv;
1090        int ret;
1091
1092        ret = devlink_info_driver_name_put(req, "hellcreek");
1093        if (ret)
1094                return ret;
1095
1096        return devlink_info_version_fixed_put(req,
1097                                              DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
1098                                              hellcreek->pdata->name);
1099}
1100
1101static u64 hellcreek_devlink_vlan_table_get(void *priv)
1102{
1103        struct hellcreek *hellcreek = priv;
1104        u64 count = 0;
1105        int i;
1106
1107        mutex_lock(&hellcreek->reg_lock);
1108        for (i = 0; i < VLAN_N_VID; ++i)
1109                if (hellcreek->vidmbrcfg[i])
1110                        count++;
1111        mutex_unlock(&hellcreek->reg_lock);
1112
1113        return count;
1114}
1115
1116static u64 hellcreek_devlink_fdb_table_get(void *priv)
1117{
1118        struct hellcreek *hellcreek = priv;
1119        u64 count = 0;
1120
1121        /* Reading this register has side effects. Synchronize against the other
1122         * FDB operations.
1123         */
1124        mutex_lock(&hellcreek->reg_lock);
1125        count = hellcreek_read(hellcreek, HR_FDBMAX);
1126        mutex_unlock(&hellcreek->reg_lock);
1127
1128        return count;
1129}
1130
1131static int hellcreek_setup_devlink_resources(struct dsa_switch *ds)
1132{
1133        struct devlink_resource_size_params size_vlan_params;
1134        struct devlink_resource_size_params size_fdb_params;
1135        struct hellcreek *hellcreek = ds->priv;
1136        int err;
1137
1138        devlink_resource_size_params_init(&size_vlan_params, VLAN_N_VID,
1139                                          VLAN_N_VID,
1140                                          1, DEVLINK_RESOURCE_UNIT_ENTRY);
1141
1142        devlink_resource_size_params_init(&size_fdb_params,
1143                                          hellcreek->fdb_entries,
1144                                          hellcreek->fdb_entries,
1145                                          1, DEVLINK_RESOURCE_UNIT_ENTRY);
1146
1147        err = dsa_devlink_resource_register(ds, "VLAN", VLAN_N_VID,
1148                                            HELLCREEK_DEVLINK_PARAM_ID_VLAN_TABLE,
1149                                            DEVLINK_RESOURCE_ID_PARENT_TOP,
1150                                            &size_vlan_params);
1151        if (err)
1152                goto out;
1153
1154        err = dsa_devlink_resource_register(ds, "FDB", hellcreek->fdb_entries,
1155                                            HELLCREEK_DEVLINK_PARAM_ID_FDB_TABLE,
1156                                            DEVLINK_RESOURCE_ID_PARENT_TOP,
1157                                            &size_fdb_params);
1158        if (err)
1159                goto out;
1160
1161        dsa_devlink_resource_occ_get_register(ds,
1162                                              HELLCREEK_DEVLINK_PARAM_ID_VLAN_TABLE,
1163                                              hellcreek_devlink_vlan_table_get,
1164                                              hellcreek);
1165
1166        dsa_devlink_resource_occ_get_register(ds,
1167                                              HELLCREEK_DEVLINK_PARAM_ID_FDB_TABLE,
1168                                              hellcreek_devlink_fdb_table_get,
1169                                              hellcreek);
1170
1171        return 0;
1172
1173out:
1174        dsa_devlink_resources_unregister(ds);
1175
1176        return err;
1177}
1178
1179static int hellcreek_devlink_region_vlan_snapshot(struct devlink *dl,
1180                                                  const struct devlink_region_ops *ops,
1181                                                  struct netlink_ext_ack *extack,
1182                                                  u8 **data)
1183{
1184        struct hellcreek_devlink_vlan_entry *table, *entry;
1185        struct dsa_switch *ds = dsa_devlink_to_ds(dl);
1186        struct hellcreek *hellcreek = ds->priv;
1187        int i;
1188
1189        table = kcalloc(VLAN_N_VID, sizeof(*entry), GFP_KERNEL);
1190        if (!table)
1191                return -ENOMEM;
1192
1193        entry = table;
1194
1195        mutex_lock(&hellcreek->reg_lock);
1196        for (i = 0; i < VLAN_N_VID; ++i, ++entry) {
1197                entry->member = hellcreek->vidmbrcfg[i];
1198                entry->vid    = i;
1199        }
1200        mutex_unlock(&hellcreek->reg_lock);
1201
1202        *data = (u8 *)table;
1203
1204        return 0;
1205}
1206
1207static int hellcreek_devlink_region_fdb_snapshot(struct devlink *dl,
1208                                                 const struct devlink_region_ops *ops,
1209                                                 struct netlink_ext_ack *extack,
1210                                                 u8 **data)
1211{
1212        struct dsa_switch *ds = dsa_devlink_to_ds(dl);
1213        struct hellcreek_fdb_entry *table, *entry;
1214        struct hellcreek *hellcreek = ds->priv;
1215        size_t i;
1216
1217        table = kcalloc(hellcreek->fdb_entries, sizeof(*entry), GFP_KERNEL);
1218        if (!table)
1219                return -ENOMEM;
1220
1221        entry = table;
1222
1223        mutex_lock(&hellcreek->reg_lock);
1224
1225        /* Start table read */
1226        hellcreek_read(hellcreek, HR_FDBMAX);
1227        hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
1228
1229        for (i = 0; i < hellcreek->fdb_entries; ++i, ++entry) {
1230                /* Read current entry */
1231                hellcreek_populate_fdb_entry(hellcreek, entry, i);
1232
1233                /* Advance read pointer */
1234                hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
1235        }
1236
1237        mutex_unlock(&hellcreek->reg_lock);
1238
1239        *data = (u8 *)table;
1240
1241        return 0;
1242}
1243
1244static struct devlink_region_ops hellcreek_region_vlan_ops = {
1245        .name       = "vlan",
1246        .snapshot   = hellcreek_devlink_region_vlan_snapshot,
1247        .destructor = kfree,
1248};
1249
1250static struct devlink_region_ops hellcreek_region_fdb_ops = {
1251        .name       = "fdb",
1252        .snapshot   = hellcreek_devlink_region_fdb_snapshot,
1253        .destructor = kfree,
1254};
1255
1256static int hellcreek_setup_devlink_regions(struct dsa_switch *ds)
1257{
1258        struct hellcreek *hellcreek = ds->priv;
1259        struct devlink_region_ops *ops;
1260        struct devlink_region *region;
1261        u64 size;
1262        int ret;
1263
1264        /* VLAN table */
1265        size = VLAN_N_VID * sizeof(struct hellcreek_devlink_vlan_entry);
1266        ops  = &hellcreek_region_vlan_ops;
1267
1268        region = dsa_devlink_region_create(ds, ops, 1, size);
1269        if (IS_ERR(region))
1270                return PTR_ERR(region);
1271
1272        hellcreek->vlan_region = region;
1273
1274        /* FDB table */
1275        size = hellcreek->fdb_entries * sizeof(struct hellcreek_fdb_entry);
1276        ops  = &hellcreek_region_fdb_ops;
1277
1278        region = dsa_devlink_region_create(ds, ops, 1, size);
1279        if (IS_ERR(region)) {
1280                ret = PTR_ERR(region);
1281                goto err_fdb;
1282        }
1283
1284        hellcreek->fdb_region = region;
1285
1286        return 0;
1287
1288err_fdb:
1289        dsa_devlink_region_destroy(hellcreek->vlan_region);
1290
1291        return ret;
1292}
1293
1294static void hellcreek_teardown_devlink_regions(struct dsa_switch *ds)
1295{
1296        struct hellcreek *hellcreek = ds->priv;
1297
1298        dsa_devlink_region_destroy(hellcreek->fdb_region);
1299        dsa_devlink_region_destroy(hellcreek->vlan_region);
1300}
1301
1302static int hellcreek_setup(struct dsa_switch *ds)
1303{
1304        struct hellcreek *hellcreek = ds->priv;
1305        u16 swcfg = 0;
1306        int ret, i;
1307
1308        dev_dbg(hellcreek->dev, "Set up the switch\n");
1309
1310        /* Let's go */
1311        ret = hellcreek_enable_ip_core(hellcreek);
1312        if (ret) {
1313                dev_err(hellcreek->dev, "Failed to enable IP core!\n");
1314                return ret;
1315        }
1316
1317        /* Enable CPU/Tunnel ports */
1318        hellcreek_setup_cpu_and_tunnel_port(hellcreek);
1319
1320        /* Switch config: Keep defaults, enable FDB aging and learning and tag
1321         * each frame from/to cpu port for DSA tagging.  Also enable the length
1322         * aware shaping mode. This eliminates the need for Qbv guard bands.
1323         */
1324        swcfg |= HR_SWCFG_FDBAGE_EN |
1325                HR_SWCFG_FDBLRN_EN  |
1326                HR_SWCFG_ALWAYS_OBT |
1327                (HR_SWCFG_LAS_ON << HR_SWCFG_LAS_MODE_SHIFT);
1328        hellcreek->swcfg = swcfg;
1329        hellcreek_write(hellcreek, swcfg, HR_SWCFG);
1330
1331        /* Initial vlan membership to reflect port separation */
1332        for (i = 0; i < ds->num_ports; ++i) {
1333                if (!dsa_is_user_port(ds, i))
1334                        continue;
1335
1336                hellcreek_setup_vlan_membership(ds, i, true);
1337        }
1338
1339        /* Configure PCP <-> TC mapping */
1340        hellcreek_setup_tc_identity_mapping(hellcreek);
1341
1342        /* The VLAN awareness is a global switch setting. Therefore, mixed vlan
1343         * filtering setups are not supported.
1344         */
1345        ds->vlan_filtering_is_global = true;
1346
1347        /* Intercept _all_ PTP multicast traffic */
1348        ret = hellcreek_setup_fdb(hellcreek);
1349        if (ret) {
1350                dev_err(hellcreek->dev,
1351                        "Failed to insert static PTP FDB entries\n");
1352                return ret;
1353        }
1354
1355        /* Register devlink resources with DSA */
1356        ret = hellcreek_setup_devlink_resources(ds);
1357        if (ret) {
1358                dev_err(hellcreek->dev,
1359                        "Failed to setup devlink resources!\n");
1360                return ret;
1361        }
1362
1363        ret = hellcreek_setup_devlink_regions(ds);
1364        if (ret) {
1365                dev_err(hellcreek->dev,
1366                        "Failed to setup devlink regions!\n");
1367                goto err_regions;
1368        }
1369
1370        return 0;
1371
1372err_regions:
1373        dsa_devlink_resources_unregister(ds);
1374
1375        return ret;
1376}
1377
1378static void hellcreek_teardown(struct dsa_switch *ds)
1379{
1380        hellcreek_teardown_devlink_regions(ds);
1381        dsa_devlink_resources_unregister(ds);
1382}
1383
1384static void hellcreek_phylink_validate(struct dsa_switch *ds, int port,
1385                                       unsigned long *supported,
1386                                       struct phylink_link_state *state)
1387{
1388        __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
1389        struct hellcreek *hellcreek = ds->priv;
1390
1391        dev_dbg(hellcreek->dev, "Phylink validate for port %d\n", port);
1392
1393        /* The MAC settings are a hardware configuration option and cannot be
1394         * changed at run time or by strapping. Therefore the attached PHYs
1395         * should be programmed to only advertise settings which are supported
1396         * by the hardware.
1397         */
1398        if (hellcreek->pdata->is_100_mbits)
1399                phylink_set(mask, 100baseT_Full);
1400        else
1401                phylink_set(mask, 1000baseT_Full);
1402
1403        bitmap_and(supported, supported, mask,
1404                   __ETHTOOL_LINK_MODE_MASK_NBITS);
1405        bitmap_and(state->advertising, state->advertising, mask,
1406                   __ETHTOOL_LINK_MODE_MASK_NBITS);
1407}
1408
1409static int
1410hellcreek_port_prechangeupper(struct dsa_switch *ds, int port,
1411                              struct netdev_notifier_changeupper_info *info)
1412{
1413        struct hellcreek *hellcreek = ds->priv;
1414        bool used = true;
1415        int ret = -EBUSY;
1416        u16 vid;
1417        int i;
1418
1419        dev_dbg(hellcreek->dev, "Pre change upper for port %d\n", port);
1420
1421        /*
1422         * Deny VLAN devices on top of lan ports with the same VLAN ids, because
1423         * it breaks the port separation due to the private VLANs. Example:
1424         *
1425         * lan0.100 *and* lan1.100 cannot be used in parallel. However, lan0.99
1426         * and lan1.100 works.
1427         */
1428
1429        if (!is_vlan_dev(info->upper_dev))
1430                return 0;
1431
1432        vid = vlan_dev_vlan_id(info->upper_dev);
1433
1434        /* For all ports, check bitmaps */
1435        mutex_lock(&hellcreek->vlan_lock);
1436        for (i = 0; i < hellcreek->pdata->num_ports; ++i) {
1437                if (!dsa_is_user_port(ds, i))
1438                        continue;
1439
1440                if (port == i)
1441                        continue;
1442
1443                used = used && test_bit(vid, hellcreek->ports[i].vlan_dev_bitmap);
1444        }
1445
1446        if (used)
1447                goto out;
1448
1449        /* Update bitmap */
1450        set_bit(vid, hellcreek->ports[port].vlan_dev_bitmap);
1451
1452        ret = 0;
1453
1454out:
1455        mutex_unlock(&hellcreek->vlan_lock);
1456
1457        return ret;
1458}
1459
1460static void hellcreek_setup_gcl(struct hellcreek *hellcreek, int port,
1461                                const struct tc_taprio_qopt_offload *schedule)
1462{
1463        const struct tc_taprio_sched_entry *cur, *initial, *next;
1464        size_t i;
1465
1466        cur = initial = &schedule->entries[0];
1467        next = cur + 1;
1468
1469        for (i = 1; i <= schedule->num_entries; ++i) {
1470                u16 data;
1471                u8 gates;
1472
1473                cur++;
1474                next++;
1475
1476                if (i == schedule->num_entries)
1477                        gates = initial->gate_mask ^
1478                                cur->gate_mask;
1479                else
1480                        gates = next->gate_mask ^
1481                                cur->gate_mask;
1482
1483                data = gates;
1484
1485                if (i == schedule->num_entries)
1486                        data |= TR_GCLDAT_GCLWRLAST;
1487
1488                /* Gates states */
1489                hellcreek_write(hellcreek, data, TR_GCLDAT);
1490
1491                /* Time interval */
1492                hellcreek_write(hellcreek,
1493                                cur->interval & 0x0000ffff,
1494                                TR_GCLTIL);
1495                hellcreek_write(hellcreek,
1496                                (cur->interval & 0xffff0000) >> 16,
1497                                TR_GCLTIH);
1498
1499                /* Commit entry */
1500                data = ((i - 1) << TR_GCLCMD_GCLWRADR_SHIFT) |
1501                        (initial->gate_mask <<
1502                         TR_GCLCMD_INIT_GATE_STATES_SHIFT);
1503                hellcreek_write(hellcreek, data, TR_GCLCMD);
1504        }
1505}
1506
1507static void hellcreek_set_cycle_time(struct hellcreek *hellcreek,
1508                                     const struct tc_taprio_qopt_offload *schedule)
1509{
1510        u32 cycle_time = schedule->cycle_time;
1511
1512        hellcreek_write(hellcreek, cycle_time & 0x0000ffff, TR_CTWRL);
1513        hellcreek_write(hellcreek, (cycle_time & 0xffff0000) >> 16, TR_CTWRH);
1514}
1515
1516static void hellcreek_switch_schedule(struct hellcreek *hellcreek,
1517                                      ktime_t start_time)
1518{
1519        struct timespec64 ts = ktime_to_timespec64(start_time);
1520
1521        /* Start schedule at this point of time */
1522        hellcreek_write(hellcreek, ts.tv_nsec & 0x0000ffff, TR_ESTWRL);
1523        hellcreek_write(hellcreek, (ts.tv_nsec & 0xffff0000) >> 16, TR_ESTWRH);
1524
1525        /* Arm timer, set seconds and switch schedule */
1526        hellcreek_write(hellcreek, TR_ESTCMD_ESTARM | TR_ESTCMD_ESTSWCFG |
1527                        ((ts.tv_sec & TR_ESTCMD_ESTSEC_MASK) <<
1528                         TR_ESTCMD_ESTSEC_SHIFT), TR_ESTCMD);
1529}
1530
1531static bool hellcreek_schedule_startable(struct hellcreek *hellcreek, int port)
1532{
1533        struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
1534        s64 base_time_ns, current_ns;
1535
1536        /* The switch allows a schedule to be started only eight seconds within
1537         * the future. Therefore, check the current PTP time if the schedule is
1538         * startable or not.
1539         */
1540
1541        /* Use the "cached" time. That should be alright, as it's updated quite
1542         * frequently in the PTP code.
1543         */
1544        mutex_lock(&hellcreek->ptp_lock);
1545        current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts;
1546        mutex_unlock(&hellcreek->ptp_lock);
1547
1548        /* Calculate difference to admin base time */
1549        base_time_ns = ktime_to_ns(hellcreek_port->current_schedule->base_time);
1550
1551        return base_time_ns - current_ns < (s64)8 * NSEC_PER_SEC;
1552}
1553
1554static void hellcreek_start_schedule(struct hellcreek *hellcreek, int port)
1555{
1556        struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
1557        ktime_t base_time, current_time;
1558        s64 current_ns;
1559        u32 cycle_time;
1560
1561        /* First select port */
1562        hellcreek_select_tgd(hellcreek, port);
1563
1564        /* Forward base time into the future if needed */
1565        mutex_lock(&hellcreek->ptp_lock);
1566        current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts;
1567        mutex_unlock(&hellcreek->ptp_lock);
1568
1569        current_time = ns_to_ktime(current_ns);
1570        base_time    = hellcreek_port->current_schedule->base_time;
1571        cycle_time   = hellcreek_port->current_schedule->cycle_time;
1572
1573        if (ktime_compare(current_time, base_time) > 0) {
1574                s64 n;
1575
1576                n = div64_s64(ktime_sub_ns(current_time, base_time),
1577                              cycle_time);
1578                base_time = ktime_add_ns(base_time, (n + 1) * cycle_time);
1579        }
1580
1581        /* Set admin base time and switch schedule */
1582        hellcreek_switch_schedule(hellcreek, base_time);
1583
1584        taprio_offload_free(hellcreek_port->current_schedule);
1585        hellcreek_port->current_schedule = NULL;
1586
1587        dev_dbg(hellcreek->dev, "Armed EST timer for port %d\n",
1588                hellcreek_port->port);
1589}
1590
1591static void hellcreek_check_schedule(struct work_struct *work)
1592{
1593        struct delayed_work *dw = to_delayed_work(work);
1594        struct hellcreek_port *hellcreek_port;
1595        struct hellcreek *hellcreek;
1596        bool startable;
1597
1598        hellcreek_port = dw_to_hellcreek_port(dw);
1599        hellcreek = hellcreek_port->hellcreek;
1600
1601        mutex_lock(&hellcreek->reg_lock);
1602
1603        /* Check starting time */
1604        startable = hellcreek_schedule_startable(hellcreek,
1605                                                 hellcreek_port->port);
1606        if (startable) {
1607                hellcreek_start_schedule(hellcreek, hellcreek_port->port);
1608                mutex_unlock(&hellcreek->reg_lock);
1609                return;
1610        }
1611
1612        mutex_unlock(&hellcreek->reg_lock);
1613
1614        /* Reschedule */
1615        schedule_delayed_work(&hellcreek_port->schedule_work,
1616                              HELLCREEK_SCHEDULE_PERIOD);
1617}
1618
1619static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port,
1620                                       struct tc_taprio_qopt_offload *taprio)
1621{
1622        struct hellcreek *hellcreek = ds->priv;
1623        struct hellcreek_port *hellcreek_port;
1624        bool startable;
1625        u16 ctrl;
1626
1627        hellcreek_port = &hellcreek->ports[port];
1628
1629        dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n",
1630                port);
1631
1632        /* First cancel delayed work */
1633        cancel_delayed_work_sync(&hellcreek_port->schedule_work);
1634
1635        mutex_lock(&hellcreek->reg_lock);
1636
1637        if (hellcreek_port->current_schedule) {
1638                taprio_offload_free(hellcreek_port->current_schedule);
1639                hellcreek_port->current_schedule = NULL;
1640        }
1641        hellcreek_port->current_schedule = taprio_offload_get(taprio);
1642
1643        /* Then select port */
1644        hellcreek_select_tgd(hellcreek, port);
1645
1646        /* Enable gating and keep defaults */
1647        ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN;
1648        hellcreek_write(hellcreek, ctrl, TR_TGDCTRL);
1649
1650        /* Cancel pending schedule */
1651        hellcreek_write(hellcreek, 0x00, TR_ESTCMD);
1652
1653        /* Setup a new schedule */
1654        hellcreek_setup_gcl(hellcreek, port, hellcreek_port->current_schedule);
1655
1656        /* Configure cycle time */
1657        hellcreek_set_cycle_time(hellcreek, hellcreek_port->current_schedule);
1658
1659        /* Check starting time */
1660        startable = hellcreek_schedule_startable(hellcreek, port);
1661        if (startable) {
1662                hellcreek_start_schedule(hellcreek, port);
1663                mutex_unlock(&hellcreek->reg_lock);
1664                return 0;
1665        }
1666
1667        mutex_unlock(&hellcreek->reg_lock);
1668
1669        /* Schedule periodic schedule check */
1670        schedule_delayed_work(&hellcreek_port->schedule_work,
1671                              HELLCREEK_SCHEDULE_PERIOD);
1672
1673        return 0;
1674}
1675
1676static int hellcreek_port_del_schedule(struct dsa_switch *ds, int port)
1677{
1678        struct hellcreek *hellcreek = ds->priv;
1679        struct hellcreek_port *hellcreek_port;
1680
1681        hellcreek_port = &hellcreek->ports[port];
1682
1683        dev_dbg(hellcreek->dev, "Remove traffic schedule on port %d\n", port);
1684
1685        /* First cancel delayed work */
1686        cancel_delayed_work_sync(&hellcreek_port->schedule_work);
1687
1688        mutex_lock(&hellcreek->reg_lock);
1689
1690        if (hellcreek_port->current_schedule) {
1691                taprio_offload_free(hellcreek_port->current_schedule);
1692                hellcreek_port->current_schedule = NULL;
1693        }
1694
1695        /* Then select port */
1696        hellcreek_select_tgd(hellcreek, port);
1697
1698        /* Disable gating and return to regular switching flow */
1699        hellcreek_write(hellcreek, 0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT,
1700                        TR_TGDCTRL);
1701
1702        mutex_unlock(&hellcreek->reg_lock);
1703
1704        return 0;
1705}
1706
1707static bool hellcreek_validate_schedule(struct hellcreek *hellcreek,
1708                                        struct tc_taprio_qopt_offload *schedule)
1709{
1710        size_t i;
1711
1712        /* Does this hellcreek version support Qbv in hardware? */
1713        if (!hellcreek->pdata->qbv_support)
1714                return false;
1715
1716        /* cycle time can only be 32bit */
1717        if (schedule->cycle_time > (u32)-1)
1718                return false;
1719
1720        /* cycle time extension is not supported */
1721        if (schedule->cycle_time_extension)
1722                return false;
1723
1724        /* Only set command is supported */
1725        for (i = 0; i < schedule->num_entries; ++i)
1726                if (schedule->entries[i].command != TC_TAPRIO_CMD_SET_GATES)
1727                        return false;
1728
1729        return true;
1730}
1731
1732static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port,
1733                                   enum tc_setup_type type, void *type_data)
1734{
1735        struct tc_taprio_qopt_offload *taprio = type_data;
1736        struct hellcreek *hellcreek = ds->priv;
1737
1738        if (type != TC_SETUP_QDISC_TAPRIO)
1739                return -EOPNOTSUPP;
1740
1741        if (!hellcreek_validate_schedule(hellcreek, taprio))
1742                return -EOPNOTSUPP;
1743
1744        if (taprio->enable)
1745                return hellcreek_port_set_schedule(ds, port, taprio);
1746
1747        return hellcreek_port_del_schedule(ds, port);
1748}
1749
1750static const struct dsa_switch_ops hellcreek_ds_ops = {
1751        .devlink_info_get      = hellcreek_devlink_info_get,
1752        .get_ethtool_stats     = hellcreek_get_ethtool_stats,
1753        .get_sset_count        = hellcreek_get_sset_count,
1754        .get_strings           = hellcreek_get_strings,
1755        .get_tag_protocol      = hellcreek_get_tag_protocol,
1756        .get_ts_info           = hellcreek_get_ts_info,
1757        .phylink_validate      = hellcreek_phylink_validate,
1758        .port_bridge_flags     = hellcreek_bridge_flags,
1759        .port_bridge_join      = hellcreek_port_bridge_join,
1760        .port_bridge_leave     = hellcreek_port_bridge_leave,
1761        .port_disable          = hellcreek_port_disable,
1762        .port_enable           = hellcreek_port_enable,
1763        .port_fdb_add          = hellcreek_fdb_add,
1764        .port_fdb_del          = hellcreek_fdb_del,
1765        .port_fdb_dump         = hellcreek_fdb_dump,
1766        .port_hwtstamp_set     = hellcreek_port_hwtstamp_set,
1767        .port_hwtstamp_get     = hellcreek_port_hwtstamp_get,
1768        .port_pre_bridge_flags = hellcreek_pre_bridge_flags,
1769        .port_prechangeupper   = hellcreek_port_prechangeupper,
1770        .port_rxtstamp         = hellcreek_port_rxtstamp,
1771        .port_setup_tc         = hellcreek_port_setup_tc,
1772        .port_stp_state_set    = hellcreek_port_stp_state_set,
1773        .port_txtstamp         = hellcreek_port_txtstamp,
1774        .port_vlan_add         = hellcreek_vlan_add,
1775        .port_vlan_del         = hellcreek_vlan_del,
1776        .port_vlan_filtering   = hellcreek_vlan_filtering,
1777        .setup                 = hellcreek_setup,
1778        .teardown              = hellcreek_teardown,
1779};
1780
1781static int hellcreek_probe(struct platform_device *pdev)
1782{
1783        struct device *dev = &pdev->dev;
1784        struct hellcreek *hellcreek;
1785        struct resource *res;
1786        int ret, i;
1787
1788        hellcreek = devm_kzalloc(dev, sizeof(*hellcreek), GFP_KERNEL);
1789        if (!hellcreek)
1790                return -ENOMEM;
1791
1792        hellcreek->vidmbrcfg = devm_kcalloc(dev, VLAN_N_VID,
1793                                            sizeof(*hellcreek->vidmbrcfg),
1794                                            GFP_KERNEL);
1795        if (!hellcreek->vidmbrcfg)
1796                return -ENOMEM;
1797
1798        hellcreek->pdata = of_device_get_match_data(dev);
1799
1800        hellcreek->ports = devm_kcalloc(dev, hellcreek->pdata->num_ports,
1801                                        sizeof(*hellcreek->ports),
1802                                        GFP_KERNEL);
1803        if (!hellcreek->ports)
1804                return -ENOMEM;
1805
1806        for (i = 0; i < hellcreek->pdata->num_ports; ++i) {
1807                struct hellcreek_port *port = &hellcreek->ports[i];
1808
1809                port->counter_values =
1810                        devm_kcalloc(dev,
1811                                     ARRAY_SIZE(hellcreek_counter),
1812                                     sizeof(*port->counter_values),
1813                                     GFP_KERNEL);
1814                if (!port->counter_values)
1815                        return -ENOMEM;
1816
1817                port->vlan_dev_bitmap =
1818                        devm_kcalloc(dev,
1819                                     BITS_TO_LONGS(VLAN_N_VID),
1820                                     sizeof(unsigned long),
1821                                     GFP_KERNEL);
1822                if (!port->vlan_dev_bitmap)
1823                        return -ENOMEM;
1824
1825                port->hellcreek = hellcreek;
1826                port->port      = i;
1827
1828                INIT_DELAYED_WORK(&port->schedule_work,
1829                                  hellcreek_check_schedule);
1830        }
1831
1832        mutex_init(&hellcreek->reg_lock);
1833        mutex_init(&hellcreek->vlan_lock);
1834        mutex_init(&hellcreek->ptp_lock);
1835
1836        hellcreek->dev = dev;
1837
1838        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tsn");
1839        if (!res) {
1840                dev_err(dev, "No memory region provided!\n");
1841                return -ENODEV;
1842        }
1843
1844        hellcreek->base = devm_ioremap_resource(dev, res);
1845        if (IS_ERR(hellcreek->base))
1846                return PTR_ERR(hellcreek->base);
1847
1848        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ptp");
1849        if (!res) {
1850                dev_err(dev, "No PTP memory region provided!\n");
1851                return -ENODEV;
1852        }
1853
1854        hellcreek->ptp_base = devm_ioremap_resource(dev, res);
1855        if (IS_ERR(hellcreek->ptp_base))
1856                return PTR_ERR(hellcreek->ptp_base);
1857
1858        ret = hellcreek_detect(hellcreek);
1859        if (ret) {
1860                dev_err(dev, "No (known) chip found!\n");
1861                return ret;
1862        }
1863
1864        ret = hellcreek_wait_until_ready(hellcreek);
1865        if (ret) {
1866                dev_err(dev, "Switch didn't become ready!\n");
1867                return ret;
1868        }
1869
1870        hellcreek_feature_detect(hellcreek);
1871
1872        hellcreek->ds = devm_kzalloc(dev, sizeof(*hellcreek->ds), GFP_KERNEL);
1873        if (!hellcreek->ds)
1874                return -ENOMEM;
1875
1876        hellcreek->ds->dev           = dev;
1877        hellcreek->ds->priv          = hellcreek;
1878        hellcreek->ds->ops           = &hellcreek_ds_ops;
1879        hellcreek->ds->num_ports     = hellcreek->pdata->num_ports;
1880        hellcreek->ds->num_tx_queues = HELLCREEK_NUM_EGRESS_QUEUES;
1881
1882        ret = dsa_register_switch(hellcreek->ds);
1883        if (ret) {
1884                dev_err_probe(dev, ret, "Unable to register switch\n");
1885                return ret;
1886        }
1887
1888        ret = hellcreek_ptp_setup(hellcreek);
1889        if (ret) {
1890                dev_err(dev, "Failed to setup PTP!\n");
1891                goto err_ptp_setup;
1892        }
1893
1894        ret = hellcreek_hwtstamp_setup(hellcreek);
1895        if (ret) {
1896                dev_err(dev, "Failed to setup hardware timestamping!\n");
1897                goto err_tstamp_setup;
1898        }
1899
1900        platform_set_drvdata(pdev, hellcreek);
1901
1902        return 0;
1903
1904err_tstamp_setup:
1905        hellcreek_ptp_free(hellcreek);
1906err_ptp_setup:
1907        dsa_unregister_switch(hellcreek->ds);
1908
1909        return ret;
1910}
1911
1912static int hellcreek_remove(struct platform_device *pdev)
1913{
1914        struct hellcreek *hellcreek = platform_get_drvdata(pdev);
1915
1916        hellcreek_hwtstamp_free(hellcreek);
1917        hellcreek_ptp_free(hellcreek);
1918        dsa_unregister_switch(hellcreek->ds);
1919        platform_set_drvdata(pdev, NULL);
1920
1921        return 0;
1922}
1923
1924static const struct hellcreek_platform_data de1soc_r1_pdata = {
1925        .name            = "r4c30",
1926        .num_ports       = 4,
1927        .is_100_mbits    = 1,
1928        .qbv_support     = 1,
1929        .qbv_on_cpu_port = 1,
1930        .qbu_support     = 0,
1931        .module_id       = 0x4c30,
1932};
1933
1934static const struct of_device_id hellcreek_of_match[] = {
1935        {
1936                .compatible = "hirschmann,hellcreek-de1soc-r1",
1937                .data       = &de1soc_r1_pdata,
1938        },
1939        { /* sentinel */ },
1940};
1941MODULE_DEVICE_TABLE(of, hellcreek_of_match);
1942
1943static struct platform_driver hellcreek_driver = {
1944        .probe  = hellcreek_probe,
1945        .remove = hellcreek_remove,
1946        .driver = {
1947                .name = "hellcreek",
1948                .of_match_table = hellcreek_of_match,
1949        },
1950};
1951module_platform_driver(hellcreek_driver);
1952
1953MODULE_AUTHOR("Kurt Kanzenbach <kurt@linutronix.de>");
1954MODULE_DESCRIPTION("Hirschmann Hellcreek driver");
1955MODULE_LICENSE("Dual MIT/GPL");
1956