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        int ret = 0;
 916        size_t i;
 917
 918        mutex_lock(&hellcreek->reg_lock);
 919
 920        /* Set read pointer to zero: The read of HR_FDBMAX (read-only register)
 921         * should reset the internal pointer. But, that doesn't work. The vendor
 922         * suggested a subsequent write as workaround. Same for HR_FDBRDH below.
 923         */
 924        entries = hellcreek_read(hellcreek, HR_FDBMAX);
 925        hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
 926
 927        dev_dbg(hellcreek->dev, "FDB dump for port %d, entries=%d!\n", port, entries);
 928
 929        /* Read table */
 930        for (i = 0; i < hellcreek->fdb_entries; ++i) {
 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 (is_zero_ether_addr(entry.mac))
 941                        continue;
 942
 943                /* Check port mask */
 944                if (!(entry.portmask & BIT(port)))
 945                        continue;
 946
 947                ret = cb(entry.mac, 0, entry.is_static, data);
 948                if (ret)
 949                        break;
 950        }
 951
 952        mutex_unlock(&hellcreek->reg_lock);
 953
 954        return ret;
 955}
 956
 957static int hellcreek_vlan_filtering(struct dsa_switch *ds, int port,
 958                                    bool vlan_filtering,
 959                                    struct netlink_ext_ack *extack)
 960{
 961        struct hellcreek *hellcreek = ds->priv;
 962
 963        dev_dbg(hellcreek->dev, "%s VLAN filtering on port %d\n",
 964                vlan_filtering ? "Enable" : "Disable", port);
 965
 966        /* Configure port to drop packages with not known vids */
 967        hellcreek_setup_ingressflt(hellcreek, port, vlan_filtering);
 968
 969        /* Enable VLAN awareness on the switch. This save due to
 970         * ds->vlan_filtering_is_global.
 971         */
 972        hellcreek_setup_vlan_awareness(hellcreek, vlan_filtering);
 973
 974        return 0;
 975}
 976
 977static int hellcreek_enable_ip_core(struct hellcreek *hellcreek)
 978{
 979        int ret;
 980        u16 val;
 981
 982        mutex_lock(&hellcreek->reg_lock);
 983
 984        val = hellcreek_read(hellcreek, HR_CTRL_C);
 985        val |= HR_CTRL_C_ENABLE;
 986        hellcreek_write(hellcreek, val, HR_CTRL_C);
 987        ret = hellcreek_wait_until_transitioned(hellcreek);
 988
 989        mutex_unlock(&hellcreek->reg_lock);
 990
 991        return ret;
 992}
 993
 994static void hellcreek_setup_cpu_and_tunnel_port(struct hellcreek *hellcreek)
 995{
 996        struct hellcreek_port *tunnel_port = &hellcreek->ports[TUNNEL_PORT];
 997        struct hellcreek_port *cpu_port = &hellcreek->ports[CPU_PORT];
 998        u16 ptcfg = 0;
 999
1000        ptcfg |= HR_PTCFG_LEARNING_EN | HR_PTCFG_ADMIN_EN;
1001
1002        mutex_lock(&hellcreek->reg_lock);
1003
1004        hellcreek_select_port(hellcreek, CPU_PORT);
1005        hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
1006
1007        hellcreek_select_port(hellcreek, TUNNEL_PORT);
1008        hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
1009
1010        cpu_port->ptcfg    = ptcfg;
1011        tunnel_port->ptcfg = ptcfg;
1012
1013        mutex_unlock(&hellcreek->reg_lock);
1014}
1015
1016static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
1017{
1018        int i;
1019
1020        /* The switch has multiple egress queues per port. The queue is selected
1021         * via the PCP field in the VLAN header. The switch internally deals
1022         * with traffic classes instead of PCP values and this mapping is
1023         * configurable.
1024         *
1025         * The default mapping is (PCP - TC):
1026         *  7 - 7
1027         *  6 - 6
1028         *  5 - 5
1029         *  4 - 4
1030         *  3 - 3
1031         *  2 - 1
1032         *  1 - 0
1033         *  0 - 2
1034         *
1035         * The default should be an identity mapping.
1036         */
1037
1038        for (i = 0; i < 8; ++i) {
1039                mutex_lock(&hellcreek->reg_lock);
1040
1041                hellcreek_select_prio(hellcreek, i);
1042                hellcreek_write(hellcreek,
1043                                i << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
1044                                HR_PRTCCFG);
1045
1046                mutex_unlock(&hellcreek->reg_lock);
1047        }
1048}
1049
1050static int hellcreek_setup_fdb(struct hellcreek *hellcreek)
1051{
1052        static struct hellcreek_fdb_entry ptp = {
1053                /* MAC: 01-1B-19-00-00-00 */
1054                .mac          = { 0x01, 0x1b, 0x19, 0x00, 0x00, 0x00 },
1055                .portmask     = 0x03,   /* Management ports */
1056                .age          = 0,
1057                .is_obt       = 0,
1058                .pass_blocked = 0,
1059                .is_static    = 1,
1060                .reprio_tc    = 6,      /* TC: 6 as per IEEE 802.1AS */
1061                .reprio_en    = 1,
1062        };
1063        static struct hellcreek_fdb_entry p2p = {
1064                /* MAC: 01-80-C2-00-00-0E */
1065                .mac          = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e },
1066                .portmask     = 0x03,   /* Management ports */
1067                .age          = 0,
1068                .is_obt       = 0,
1069                .pass_blocked = 0,
1070                .is_static    = 1,
1071                .reprio_tc    = 6,      /* TC: 6 as per IEEE 802.1AS */
1072                .reprio_en    = 1,
1073        };
1074        int ret;
1075
1076        mutex_lock(&hellcreek->reg_lock);
1077        ret = __hellcreek_fdb_add(hellcreek, &ptp);
1078        if (ret)
1079                goto out;
1080        ret = __hellcreek_fdb_add(hellcreek, &p2p);
1081out:
1082        mutex_unlock(&hellcreek->reg_lock);
1083
1084        return ret;
1085}
1086
1087static int hellcreek_devlink_info_get(struct dsa_switch *ds,
1088                                      struct devlink_info_req *req,
1089                                      struct netlink_ext_ack *extack)
1090{
1091        struct hellcreek *hellcreek = ds->priv;
1092        int ret;
1093
1094        ret = devlink_info_driver_name_put(req, "hellcreek");
1095        if (ret)
1096                return ret;
1097
1098        return devlink_info_version_fixed_put(req,
1099                                              DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
1100                                              hellcreek->pdata->name);
1101}
1102
1103static u64 hellcreek_devlink_vlan_table_get(void *priv)
1104{
1105        struct hellcreek *hellcreek = priv;
1106        u64 count = 0;
1107        int i;
1108
1109        mutex_lock(&hellcreek->reg_lock);
1110        for (i = 0; i < VLAN_N_VID; ++i)
1111                if (hellcreek->vidmbrcfg[i])
1112                        count++;
1113        mutex_unlock(&hellcreek->reg_lock);
1114
1115        return count;
1116}
1117
1118static u64 hellcreek_devlink_fdb_table_get(void *priv)
1119{
1120        struct hellcreek *hellcreek = priv;
1121        u64 count = 0;
1122
1123        /* Reading this register has side effects. Synchronize against the other
1124         * FDB operations.
1125         */
1126        mutex_lock(&hellcreek->reg_lock);
1127        count = hellcreek_read(hellcreek, HR_FDBMAX);
1128        mutex_unlock(&hellcreek->reg_lock);
1129
1130        return count;
1131}
1132
1133static int hellcreek_setup_devlink_resources(struct dsa_switch *ds)
1134{
1135        struct devlink_resource_size_params size_vlan_params;
1136        struct devlink_resource_size_params size_fdb_params;
1137        struct hellcreek *hellcreek = ds->priv;
1138        int err;
1139
1140        devlink_resource_size_params_init(&size_vlan_params, VLAN_N_VID,
1141                                          VLAN_N_VID,
1142                                          1, DEVLINK_RESOURCE_UNIT_ENTRY);
1143
1144        devlink_resource_size_params_init(&size_fdb_params,
1145                                          hellcreek->fdb_entries,
1146                                          hellcreek->fdb_entries,
1147                                          1, DEVLINK_RESOURCE_UNIT_ENTRY);
1148
1149        err = dsa_devlink_resource_register(ds, "VLAN", VLAN_N_VID,
1150                                            HELLCREEK_DEVLINK_PARAM_ID_VLAN_TABLE,
1151                                            DEVLINK_RESOURCE_ID_PARENT_TOP,
1152                                            &size_vlan_params);
1153        if (err)
1154                goto out;
1155
1156        err = dsa_devlink_resource_register(ds, "FDB", hellcreek->fdb_entries,
1157                                            HELLCREEK_DEVLINK_PARAM_ID_FDB_TABLE,
1158                                            DEVLINK_RESOURCE_ID_PARENT_TOP,
1159                                            &size_fdb_params);
1160        if (err)
1161                goto out;
1162
1163        dsa_devlink_resource_occ_get_register(ds,
1164                                              HELLCREEK_DEVLINK_PARAM_ID_VLAN_TABLE,
1165                                              hellcreek_devlink_vlan_table_get,
1166                                              hellcreek);
1167
1168        dsa_devlink_resource_occ_get_register(ds,
1169                                              HELLCREEK_DEVLINK_PARAM_ID_FDB_TABLE,
1170                                              hellcreek_devlink_fdb_table_get,
1171                                              hellcreek);
1172
1173        return 0;
1174
1175out:
1176        dsa_devlink_resources_unregister(ds);
1177
1178        return err;
1179}
1180
1181static int hellcreek_devlink_region_vlan_snapshot(struct devlink *dl,
1182                                                  const struct devlink_region_ops *ops,
1183                                                  struct netlink_ext_ack *extack,
1184                                                  u8 **data)
1185{
1186        struct hellcreek_devlink_vlan_entry *table, *entry;
1187        struct dsa_switch *ds = dsa_devlink_to_ds(dl);
1188        struct hellcreek *hellcreek = ds->priv;
1189        int i;
1190
1191        table = kcalloc(VLAN_N_VID, sizeof(*entry), GFP_KERNEL);
1192        if (!table)
1193                return -ENOMEM;
1194
1195        entry = table;
1196
1197        mutex_lock(&hellcreek->reg_lock);
1198        for (i = 0; i < VLAN_N_VID; ++i, ++entry) {
1199                entry->member = hellcreek->vidmbrcfg[i];
1200                entry->vid    = i;
1201        }
1202        mutex_unlock(&hellcreek->reg_lock);
1203
1204        *data = (u8 *)table;
1205
1206        return 0;
1207}
1208
1209static int hellcreek_devlink_region_fdb_snapshot(struct devlink *dl,
1210                                                 const struct devlink_region_ops *ops,
1211                                                 struct netlink_ext_ack *extack,
1212                                                 u8 **data)
1213{
1214        struct dsa_switch *ds = dsa_devlink_to_ds(dl);
1215        struct hellcreek_fdb_entry *table, *entry;
1216        struct hellcreek *hellcreek = ds->priv;
1217        size_t i;
1218
1219        table = kcalloc(hellcreek->fdb_entries, sizeof(*entry), GFP_KERNEL);
1220        if (!table)
1221                return -ENOMEM;
1222
1223        entry = table;
1224
1225        mutex_lock(&hellcreek->reg_lock);
1226
1227        /* Start table read */
1228        hellcreek_read(hellcreek, HR_FDBMAX);
1229        hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
1230
1231        for (i = 0; i < hellcreek->fdb_entries; ++i, ++entry) {
1232                /* Read current entry */
1233                hellcreek_populate_fdb_entry(hellcreek, entry, i);
1234
1235                /* Advance read pointer */
1236                hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
1237        }
1238
1239        mutex_unlock(&hellcreek->reg_lock);
1240
1241        *data = (u8 *)table;
1242
1243        return 0;
1244}
1245
1246static struct devlink_region_ops hellcreek_region_vlan_ops = {
1247        .name       = "vlan",
1248        .snapshot   = hellcreek_devlink_region_vlan_snapshot,
1249        .destructor = kfree,
1250};
1251
1252static struct devlink_region_ops hellcreek_region_fdb_ops = {
1253        .name       = "fdb",
1254        .snapshot   = hellcreek_devlink_region_fdb_snapshot,
1255        .destructor = kfree,
1256};
1257
1258static int hellcreek_setup_devlink_regions(struct dsa_switch *ds)
1259{
1260        struct hellcreek *hellcreek = ds->priv;
1261        struct devlink_region_ops *ops;
1262        struct devlink_region *region;
1263        u64 size;
1264        int ret;
1265
1266        /* VLAN table */
1267        size = VLAN_N_VID * sizeof(struct hellcreek_devlink_vlan_entry);
1268        ops  = &hellcreek_region_vlan_ops;
1269
1270        region = dsa_devlink_region_create(ds, ops, 1, size);
1271        if (IS_ERR(region))
1272                return PTR_ERR(region);
1273
1274        hellcreek->vlan_region = region;
1275
1276        /* FDB table */
1277        size = hellcreek->fdb_entries * sizeof(struct hellcreek_fdb_entry);
1278        ops  = &hellcreek_region_fdb_ops;
1279
1280        region = dsa_devlink_region_create(ds, ops, 1, size);
1281        if (IS_ERR(region)) {
1282                ret = PTR_ERR(region);
1283                goto err_fdb;
1284        }
1285
1286        hellcreek->fdb_region = region;
1287
1288        return 0;
1289
1290err_fdb:
1291        dsa_devlink_region_destroy(hellcreek->vlan_region);
1292
1293        return ret;
1294}
1295
1296static void hellcreek_teardown_devlink_regions(struct dsa_switch *ds)
1297{
1298        struct hellcreek *hellcreek = ds->priv;
1299
1300        dsa_devlink_region_destroy(hellcreek->fdb_region);
1301        dsa_devlink_region_destroy(hellcreek->vlan_region);
1302}
1303
1304static int hellcreek_setup(struct dsa_switch *ds)
1305{
1306        struct hellcreek *hellcreek = ds->priv;
1307        u16 swcfg = 0;
1308        int ret, i;
1309
1310        dev_dbg(hellcreek->dev, "Set up the switch\n");
1311
1312        /* Let's go */
1313        ret = hellcreek_enable_ip_core(hellcreek);
1314        if (ret) {
1315                dev_err(hellcreek->dev, "Failed to enable IP core!\n");
1316                return ret;
1317        }
1318
1319        /* Enable CPU/Tunnel ports */
1320        hellcreek_setup_cpu_and_tunnel_port(hellcreek);
1321
1322        /* Switch config: Keep defaults, enable FDB aging and learning and tag
1323         * each frame from/to cpu port for DSA tagging.  Also enable the length
1324         * aware shaping mode. This eliminates the need for Qbv guard bands.
1325         */
1326        swcfg |= HR_SWCFG_FDBAGE_EN |
1327                HR_SWCFG_FDBLRN_EN  |
1328                HR_SWCFG_ALWAYS_OBT |
1329                (HR_SWCFG_LAS_ON << HR_SWCFG_LAS_MODE_SHIFT);
1330        hellcreek->swcfg = swcfg;
1331        hellcreek_write(hellcreek, swcfg, HR_SWCFG);
1332
1333        /* Initial vlan membership to reflect port separation */
1334        for (i = 0; i < ds->num_ports; ++i) {
1335                if (!dsa_is_user_port(ds, i))
1336                        continue;
1337
1338                hellcreek_setup_vlan_membership(ds, i, true);
1339        }
1340
1341        /* Configure PCP <-> TC mapping */
1342        hellcreek_setup_tc_identity_mapping(hellcreek);
1343
1344        /* The VLAN awareness is a global switch setting. Therefore, mixed vlan
1345         * filtering setups are not supported.
1346         */
1347        ds->vlan_filtering_is_global = true;
1348        ds->needs_standalone_vlan_filtering = true;
1349
1350        /* Intercept _all_ PTP multicast traffic */
1351        ret = hellcreek_setup_fdb(hellcreek);
1352        if (ret) {
1353                dev_err(hellcreek->dev,
1354                        "Failed to insert static PTP FDB entries\n");
1355                return ret;
1356        }
1357
1358        /* Register devlink resources with DSA */
1359        ret = hellcreek_setup_devlink_resources(ds);
1360        if (ret) {
1361                dev_err(hellcreek->dev,
1362                        "Failed to setup devlink resources!\n");
1363                return ret;
1364        }
1365
1366        ret = hellcreek_setup_devlink_regions(ds);
1367        if (ret) {
1368                dev_err(hellcreek->dev,
1369                        "Failed to setup devlink regions!\n");
1370                goto err_regions;
1371        }
1372
1373        return 0;
1374
1375err_regions:
1376        dsa_devlink_resources_unregister(ds);
1377
1378        return ret;
1379}
1380
1381static void hellcreek_teardown(struct dsa_switch *ds)
1382{
1383        hellcreek_teardown_devlink_regions(ds);
1384        dsa_devlink_resources_unregister(ds);
1385}
1386
1387static void hellcreek_phylink_validate(struct dsa_switch *ds, int port,
1388                                       unsigned long *supported,
1389                                       struct phylink_link_state *state)
1390{
1391        __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
1392        struct hellcreek *hellcreek = ds->priv;
1393
1394        dev_dbg(hellcreek->dev, "Phylink validate for port %d\n", port);
1395
1396        /* The MAC settings are a hardware configuration option and cannot be
1397         * changed at run time or by strapping. Therefore the attached PHYs
1398         * should be programmed to only advertise settings which are supported
1399         * by the hardware.
1400         */
1401        if (hellcreek->pdata->is_100_mbits)
1402                phylink_set(mask, 100baseT_Full);
1403        else
1404                phylink_set(mask, 1000baseT_Full);
1405
1406        bitmap_and(supported, supported, mask,
1407                   __ETHTOOL_LINK_MODE_MASK_NBITS);
1408        bitmap_and(state->advertising, state->advertising, mask,
1409                   __ETHTOOL_LINK_MODE_MASK_NBITS);
1410}
1411
1412static int
1413hellcreek_port_prechangeupper(struct dsa_switch *ds, int port,
1414                              struct netdev_notifier_changeupper_info *info)
1415{
1416        struct hellcreek *hellcreek = ds->priv;
1417        bool used = true;
1418        int ret = -EBUSY;
1419        u16 vid;
1420        int i;
1421
1422        dev_dbg(hellcreek->dev, "Pre change upper for port %d\n", port);
1423
1424        /*
1425         * Deny VLAN devices on top of lan ports with the same VLAN ids, because
1426         * it breaks the port separation due to the private VLANs. Example:
1427         *
1428         * lan0.100 *and* lan1.100 cannot be used in parallel. However, lan0.99
1429         * and lan1.100 works.
1430         */
1431
1432        if (!is_vlan_dev(info->upper_dev))
1433                return 0;
1434
1435        vid = vlan_dev_vlan_id(info->upper_dev);
1436
1437        /* For all ports, check bitmaps */
1438        mutex_lock(&hellcreek->vlan_lock);
1439        for (i = 0; i < hellcreek->pdata->num_ports; ++i) {
1440                if (!dsa_is_user_port(ds, i))
1441                        continue;
1442
1443                if (port == i)
1444                        continue;
1445
1446                used = used && test_bit(vid, hellcreek->ports[i].vlan_dev_bitmap);
1447        }
1448
1449        if (used)
1450                goto out;
1451
1452        /* Update bitmap */
1453        set_bit(vid, hellcreek->ports[port].vlan_dev_bitmap);
1454
1455        ret = 0;
1456
1457out:
1458        mutex_unlock(&hellcreek->vlan_lock);
1459
1460        return ret;
1461}
1462
1463static void hellcreek_setup_gcl(struct hellcreek *hellcreek, int port,
1464                                const struct tc_taprio_qopt_offload *schedule)
1465{
1466        const struct tc_taprio_sched_entry *cur, *initial, *next;
1467        size_t i;
1468
1469        cur = initial = &schedule->entries[0];
1470        next = cur + 1;
1471
1472        for (i = 1; i <= schedule->num_entries; ++i) {
1473                u16 data;
1474                u8 gates;
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                cur++;
1506                next++;
1507        }
1508}
1509
1510static void hellcreek_set_cycle_time(struct hellcreek *hellcreek,
1511                                     const struct tc_taprio_qopt_offload *schedule)
1512{
1513        u32 cycle_time = schedule->cycle_time;
1514
1515        hellcreek_write(hellcreek, cycle_time & 0x0000ffff, TR_CTWRL);
1516        hellcreek_write(hellcreek, (cycle_time & 0xffff0000) >> 16, TR_CTWRH);
1517}
1518
1519static void hellcreek_switch_schedule(struct hellcreek *hellcreek,
1520                                      ktime_t start_time)
1521{
1522        struct timespec64 ts = ktime_to_timespec64(start_time);
1523
1524        /* Start schedule at this point of time */
1525        hellcreek_write(hellcreek, ts.tv_nsec & 0x0000ffff, TR_ESTWRL);
1526        hellcreek_write(hellcreek, (ts.tv_nsec & 0xffff0000) >> 16, TR_ESTWRH);
1527
1528        /* Arm timer, set seconds and switch schedule */
1529        hellcreek_write(hellcreek, TR_ESTCMD_ESTARM | TR_ESTCMD_ESTSWCFG |
1530                        ((ts.tv_sec & TR_ESTCMD_ESTSEC_MASK) <<
1531                         TR_ESTCMD_ESTSEC_SHIFT), TR_ESTCMD);
1532}
1533
1534static bool hellcreek_schedule_startable(struct hellcreek *hellcreek, int port)
1535{
1536        struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
1537        s64 base_time_ns, current_ns;
1538
1539        /* The switch allows a schedule to be started only eight seconds within
1540         * the future. Therefore, check the current PTP time if the schedule is
1541         * startable or not.
1542         */
1543
1544        /* Use the "cached" time. That should be alright, as it's updated quite
1545         * frequently in the PTP code.
1546         */
1547        mutex_lock(&hellcreek->ptp_lock);
1548        current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts;
1549        mutex_unlock(&hellcreek->ptp_lock);
1550
1551        /* Calculate difference to admin base time */
1552        base_time_ns = ktime_to_ns(hellcreek_port->current_schedule->base_time);
1553
1554        return base_time_ns - current_ns < (s64)4 * NSEC_PER_SEC;
1555}
1556
1557static void hellcreek_start_schedule(struct hellcreek *hellcreek, int port)
1558{
1559        struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
1560        ktime_t base_time, current_time;
1561        s64 current_ns;
1562        u32 cycle_time;
1563
1564        /* First select port */
1565        hellcreek_select_tgd(hellcreek, port);
1566
1567        /* Forward base time into the future if needed */
1568        mutex_lock(&hellcreek->ptp_lock);
1569        current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts;
1570        mutex_unlock(&hellcreek->ptp_lock);
1571
1572        current_time = ns_to_ktime(current_ns);
1573        base_time    = hellcreek_port->current_schedule->base_time;
1574        cycle_time   = hellcreek_port->current_schedule->cycle_time;
1575
1576        if (ktime_compare(current_time, base_time) > 0) {
1577                s64 n;
1578
1579                n = div64_s64(ktime_sub_ns(current_time, base_time),
1580                              cycle_time);
1581                base_time = ktime_add_ns(base_time, (n + 1) * cycle_time);
1582        }
1583
1584        /* Set admin base time and switch schedule */
1585        hellcreek_switch_schedule(hellcreek, base_time);
1586
1587        taprio_offload_free(hellcreek_port->current_schedule);
1588        hellcreek_port->current_schedule = NULL;
1589
1590        dev_dbg(hellcreek->dev, "Armed EST timer for port %d\n",
1591                hellcreek_port->port);
1592}
1593
1594static void hellcreek_check_schedule(struct work_struct *work)
1595{
1596        struct delayed_work *dw = to_delayed_work(work);
1597        struct hellcreek_port *hellcreek_port;
1598        struct hellcreek *hellcreek;
1599        bool startable;
1600
1601        hellcreek_port = dw_to_hellcreek_port(dw);
1602        hellcreek = hellcreek_port->hellcreek;
1603
1604        mutex_lock(&hellcreek->reg_lock);
1605
1606        /* Check starting time */
1607        startable = hellcreek_schedule_startable(hellcreek,
1608                                                 hellcreek_port->port);
1609        if (startable) {
1610                hellcreek_start_schedule(hellcreek, hellcreek_port->port);
1611                mutex_unlock(&hellcreek->reg_lock);
1612                return;
1613        }
1614
1615        mutex_unlock(&hellcreek->reg_lock);
1616
1617        /* Reschedule */
1618        schedule_delayed_work(&hellcreek_port->schedule_work,
1619                              HELLCREEK_SCHEDULE_PERIOD);
1620}
1621
1622static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port,
1623                                       struct tc_taprio_qopt_offload *taprio)
1624{
1625        struct hellcreek *hellcreek = ds->priv;
1626        struct hellcreek_port *hellcreek_port;
1627        bool startable;
1628        u16 ctrl;
1629
1630        hellcreek_port = &hellcreek->ports[port];
1631
1632        dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n",
1633                port);
1634
1635        /* First cancel delayed work */
1636        cancel_delayed_work_sync(&hellcreek_port->schedule_work);
1637
1638        mutex_lock(&hellcreek->reg_lock);
1639
1640        if (hellcreek_port->current_schedule) {
1641                taprio_offload_free(hellcreek_port->current_schedule);
1642                hellcreek_port->current_schedule = NULL;
1643        }
1644        hellcreek_port->current_schedule = taprio_offload_get(taprio);
1645
1646        /* Then select port */
1647        hellcreek_select_tgd(hellcreek, port);
1648
1649        /* Enable gating and keep defaults */
1650        ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN;
1651        hellcreek_write(hellcreek, ctrl, TR_TGDCTRL);
1652
1653        /* Cancel pending schedule */
1654        hellcreek_write(hellcreek, 0x00, TR_ESTCMD);
1655
1656        /* Setup a new schedule */
1657        hellcreek_setup_gcl(hellcreek, port, hellcreek_port->current_schedule);
1658
1659        /* Configure cycle time */
1660        hellcreek_set_cycle_time(hellcreek, hellcreek_port->current_schedule);
1661
1662        /* Check starting time */
1663        startable = hellcreek_schedule_startable(hellcreek, port);
1664        if (startable) {
1665                hellcreek_start_schedule(hellcreek, port);
1666                mutex_unlock(&hellcreek->reg_lock);
1667                return 0;
1668        }
1669
1670        mutex_unlock(&hellcreek->reg_lock);
1671
1672        /* Schedule periodic schedule check */
1673        schedule_delayed_work(&hellcreek_port->schedule_work,
1674                              HELLCREEK_SCHEDULE_PERIOD);
1675
1676        return 0;
1677}
1678
1679static int hellcreek_port_del_schedule(struct dsa_switch *ds, int port)
1680{
1681        struct hellcreek *hellcreek = ds->priv;
1682        struct hellcreek_port *hellcreek_port;
1683
1684        hellcreek_port = &hellcreek->ports[port];
1685
1686        dev_dbg(hellcreek->dev, "Remove traffic schedule on port %d\n", port);
1687
1688        /* First cancel delayed work */
1689        cancel_delayed_work_sync(&hellcreek_port->schedule_work);
1690
1691        mutex_lock(&hellcreek->reg_lock);
1692
1693        if (hellcreek_port->current_schedule) {
1694                taprio_offload_free(hellcreek_port->current_schedule);
1695                hellcreek_port->current_schedule = NULL;
1696        }
1697
1698        /* Then select port */
1699        hellcreek_select_tgd(hellcreek, port);
1700
1701        /* Disable gating and return to regular switching flow */
1702        hellcreek_write(hellcreek, 0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT,
1703                        TR_TGDCTRL);
1704
1705        mutex_unlock(&hellcreek->reg_lock);
1706
1707        return 0;
1708}
1709
1710static bool hellcreek_validate_schedule(struct hellcreek *hellcreek,
1711                                        struct tc_taprio_qopt_offload *schedule)
1712{
1713        size_t i;
1714
1715        /* Does this hellcreek version support Qbv in hardware? */
1716        if (!hellcreek->pdata->qbv_support)
1717                return false;
1718
1719        /* cycle time can only be 32bit */
1720        if (schedule->cycle_time > (u32)-1)
1721                return false;
1722
1723        /* cycle time extension is not supported */
1724        if (schedule->cycle_time_extension)
1725                return false;
1726
1727        /* Only set command is supported */
1728        for (i = 0; i < schedule->num_entries; ++i)
1729                if (schedule->entries[i].command != TC_TAPRIO_CMD_SET_GATES)
1730                        return false;
1731
1732        return true;
1733}
1734
1735static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port,
1736                                   enum tc_setup_type type, void *type_data)
1737{
1738        struct tc_taprio_qopt_offload *taprio = type_data;
1739        struct hellcreek *hellcreek = ds->priv;
1740
1741        if (type != TC_SETUP_QDISC_TAPRIO)
1742                return -EOPNOTSUPP;
1743
1744        if (!hellcreek_validate_schedule(hellcreek, taprio))
1745                return -EOPNOTSUPP;
1746
1747        if (taprio->enable)
1748                return hellcreek_port_set_schedule(ds, port, taprio);
1749
1750        return hellcreek_port_del_schedule(ds, port);
1751}
1752
1753static const struct dsa_switch_ops hellcreek_ds_ops = {
1754        .devlink_info_get      = hellcreek_devlink_info_get,
1755        .get_ethtool_stats     = hellcreek_get_ethtool_stats,
1756        .get_sset_count        = hellcreek_get_sset_count,
1757        .get_strings           = hellcreek_get_strings,
1758        .get_tag_protocol      = hellcreek_get_tag_protocol,
1759        .get_ts_info           = hellcreek_get_ts_info,
1760        .phylink_validate      = hellcreek_phylink_validate,
1761        .port_bridge_flags     = hellcreek_bridge_flags,
1762        .port_bridge_join      = hellcreek_port_bridge_join,
1763        .port_bridge_leave     = hellcreek_port_bridge_leave,
1764        .port_disable          = hellcreek_port_disable,
1765        .port_enable           = hellcreek_port_enable,
1766        .port_fdb_add          = hellcreek_fdb_add,
1767        .port_fdb_del          = hellcreek_fdb_del,
1768        .port_fdb_dump         = hellcreek_fdb_dump,
1769        .port_hwtstamp_set     = hellcreek_port_hwtstamp_set,
1770        .port_hwtstamp_get     = hellcreek_port_hwtstamp_get,
1771        .port_pre_bridge_flags = hellcreek_pre_bridge_flags,
1772        .port_prechangeupper   = hellcreek_port_prechangeupper,
1773        .port_rxtstamp         = hellcreek_port_rxtstamp,
1774        .port_setup_tc         = hellcreek_port_setup_tc,
1775        .port_stp_state_set    = hellcreek_port_stp_state_set,
1776        .port_txtstamp         = hellcreek_port_txtstamp,
1777        .port_vlan_add         = hellcreek_vlan_add,
1778        .port_vlan_del         = hellcreek_vlan_del,
1779        .port_vlan_filtering   = hellcreek_vlan_filtering,
1780        .setup                 = hellcreek_setup,
1781        .teardown              = hellcreek_teardown,
1782};
1783
1784static int hellcreek_probe(struct platform_device *pdev)
1785{
1786        struct device *dev = &pdev->dev;
1787        struct hellcreek *hellcreek;
1788        struct resource *res;
1789        int ret, i;
1790
1791        hellcreek = devm_kzalloc(dev, sizeof(*hellcreek), GFP_KERNEL);
1792        if (!hellcreek)
1793                return -ENOMEM;
1794
1795        hellcreek->vidmbrcfg = devm_kcalloc(dev, VLAN_N_VID,
1796                                            sizeof(*hellcreek->vidmbrcfg),
1797                                            GFP_KERNEL);
1798        if (!hellcreek->vidmbrcfg)
1799                return -ENOMEM;
1800
1801        hellcreek->pdata = of_device_get_match_data(dev);
1802
1803        hellcreek->ports = devm_kcalloc(dev, hellcreek->pdata->num_ports,
1804                                        sizeof(*hellcreek->ports),
1805                                        GFP_KERNEL);
1806        if (!hellcreek->ports)
1807                return -ENOMEM;
1808
1809        for (i = 0; i < hellcreek->pdata->num_ports; ++i) {
1810                struct hellcreek_port *port = &hellcreek->ports[i];
1811
1812                port->counter_values =
1813                        devm_kcalloc(dev,
1814                                     ARRAY_SIZE(hellcreek_counter),
1815                                     sizeof(*port->counter_values),
1816                                     GFP_KERNEL);
1817                if (!port->counter_values)
1818                        return -ENOMEM;
1819
1820                port->vlan_dev_bitmap =
1821                        devm_kcalloc(dev,
1822                                     BITS_TO_LONGS(VLAN_N_VID),
1823                                     sizeof(unsigned long),
1824                                     GFP_KERNEL);
1825                if (!port->vlan_dev_bitmap)
1826                        return -ENOMEM;
1827
1828                port->hellcreek = hellcreek;
1829                port->port      = i;
1830
1831                INIT_DELAYED_WORK(&port->schedule_work,
1832                                  hellcreek_check_schedule);
1833        }
1834
1835        mutex_init(&hellcreek->reg_lock);
1836        mutex_init(&hellcreek->vlan_lock);
1837        mutex_init(&hellcreek->ptp_lock);
1838
1839        hellcreek->dev = dev;
1840
1841        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tsn");
1842        if (!res) {
1843                dev_err(dev, "No memory region provided!\n");
1844                return -ENODEV;
1845        }
1846
1847        hellcreek->base = devm_ioremap_resource(dev, res);
1848        if (IS_ERR(hellcreek->base))
1849                return PTR_ERR(hellcreek->base);
1850
1851        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ptp");
1852        if (!res) {
1853                dev_err(dev, "No PTP memory region provided!\n");
1854                return -ENODEV;
1855        }
1856
1857        hellcreek->ptp_base = devm_ioremap_resource(dev, res);
1858        if (IS_ERR(hellcreek->ptp_base))
1859                return PTR_ERR(hellcreek->ptp_base);
1860
1861        ret = hellcreek_detect(hellcreek);
1862        if (ret) {
1863                dev_err(dev, "No (known) chip found!\n");
1864                return ret;
1865        }
1866
1867        ret = hellcreek_wait_until_ready(hellcreek);
1868        if (ret) {
1869                dev_err(dev, "Switch didn't become ready!\n");
1870                return ret;
1871        }
1872
1873        hellcreek_feature_detect(hellcreek);
1874
1875        hellcreek->ds = devm_kzalloc(dev, sizeof(*hellcreek->ds), GFP_KERNEL);
1876        if (!hellcreek->ds)
1877                return -ENOMEM;
1878
1879        hellcreek->ds->dev           = dev;
1880        hellcreek->ds->priv          = hellcreek;
1881        hellcreek->ds->ops           = &hellcreek_ds_ops;
1882        hellcreek->ds->num_ports     = hellcreek->pdata->num_ports;
1883        hellcreek->ds->num_tx_queues = HELLCREEK_NUM_EGRESS_QUEUES;
1884
1885        ret = dsa_register_switch(hellcreek->ds);
1886        if (ret) {
1887                dev_err_probe(dev, ret, "Unable to register switch\n");
1888                return ret;
1889        }
1890
1891        ret = hellcreek_ptp_setup(hellcreek);
1892        if (ret) {
1893                dev_err(dev, "Failed to setup PTP!\n");
1894                goto err_ptp_setup;
1895        }
1896
1897        ret = hellcreek_hwtstamp_setup(hellcreek);
1898        if (ret) {
1899                dev_err(dev, "Failed to setup hardware timestamping!\n");
1900                goto err_tstamp_setup;
1901        }
1902
1903        platform_set_drvdata(pdev, hellcreek);
1904
1905        return 0;
1906
1907err_tstamp_setup:
1908        hellcreek_ptp_free(hellcreek);
1909err_ptp_setup:
1910        dsa_unregister_switch(hellcreek->ds);
1911
1912        return ret;
1913}
1914
1915static int hellcreek_remove(struct platform_device *pdev)
1916{
1917        struct hellcreek *hellcreek = platform_get_drvdata(pdev);
1918
1919        if (!hellcreek)
1920                return 0;
1921
1922        hellcreek_hwtstamp_free(hellcreek);
1923        hellcreek_ptp_free(hellcreek);
1924        dsa_unregister_switch(hellcreek->ds);
1925        platform_set_drvdata(pdev, NULL);
1926
1927        return 0;
1928}
1929
1930static void hellcreek_shutdown(struct platform_device *pdev)
1931{
1932        struct hellcreek *hellcreek = platform_get_drvdata(pdev);
1933
1934        if (!hellcreek)
1935                return;
1936
1937        dsa_switch_shutdown(hellcreek->ds);
1938
1939        platform_set_drvdata(pdev, NULL);
1940}
1941
1942static const struct hellcreek_platform_data de1soc_r1_pdata = {
1943        .name            = "r4c30",
1944        .num_ports       = 4,
1945        .is_100_mbits    = 1,
1946        .qbv_support     = 1,
1947        .qbv_on_cpu_port = 1,
1948        .qbu_support     = 0,
1949        .module_id       = 0x4c30,
1950};
1951
1952static const struct of_device_id hellcreek_of_match[] = {
1953        {
1954                .compatible = "hirschmann,hellcreek-de1soc-r1",
1955                .data       = &de1soc_r1_pdata,
1956        },
1957        { /* sentinel */ },
1958};
1959MODULE_DEVICE_TABLE(of, hellcreek_of_match);
1960
1961static struct platform_driver hellcreek_driver = {
1962        .probe  = hellcreek_probe,
1963        .remove = hellcreek_remove,
1964        .shutdown = hellcreek_shutdown,
1965        .driver = {
1966                .name = "hellcreek",
1967                .of_match_table = hellcreek_of_match,
1968        },
1969};
1970module_platform_driver(hellcreek_driver);
1971
1972MODULE_AUTHOR("Kurt Kanzenbach <kurt@linutronix.de>");
1973MODULE_DESCRIPTION("Hirschmann Hellcreek driver");
1974MODULE_LICENSE("Dual MIT/GPL");
1975