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 dsa_bridge bridge,
 678                                      bool *tx_fwd_offload,
 679                                      struct netlink_ext_ack *extack)
 680{
 681        struct hellcreek *hellcreek = ds->priv;
 682
 683        dev_dbg(hellcreek->dev, "Port %d joins a bridge\n", port);
 684
 685        /* When joining a vlan_filtering bridge, keep the switch VLAN aware */
 686        if (!ds->vlan_filtering)
 687                hellcreek_setup_vlan_awareness(hellcreek, false);
 688
 689        /* Drop private vlans */
 690        hellcreek_setup_vlan_membership(ds, port, false);
 691
 692        return 0;
 693}
 694
 695static void hellcreek_port_bridge_leave(struct dsa_switch *ds, int port,
 696                                        struct dsa_bridge bridge)
 697{
 698        struct hellcreek *hellcreek = ds->priv;
 699
 700        dev_dbg(hellcreek->dev, "Port %d leaves a bridge\n", port);
 701
 702        /* Enable VLAN awareness */
 703        hellcreek_setup_vlan_awareness(hellcreek, true);
 704
 705        /* Enable private vlans */
 706        hellcreek_setup_vlan_membership(ds, port, true);
 707}
 708
 709static int __hellcreek_fdb_add(struct hellcreek *hellcreek,
 710                               const struct hellcreek_fdb_entry *entry)
 711{
 712        u16 meta = 0;
 713
 714        dev_dbg(hellcreek->dev, "Add static FDB entry: MAC=%pM, MASK=0x%02x, "
 715                "OBT=%d, PASS_BLOCKED=%d, REPRIO_EN=%d, PRIO=%d\n", entry->mac,
 716                entry->portmask, entry->is_obt, entry->pass_blocked,
 717                entry->reprio_en, entry->reprio_tc);
 718
 719        /* Add mac address */
 720        hellcreek_write(hellcreek, entry->mac[1] | (entry->mac[0] << 8), HR_FDBWDH);
 721        hellcreek_write(hellcreek, entry->mac[3] | (entry->mac[2] << 8), HR_FDBWDM);
 722        hellcreek_write(hellcreek, entry->mac[5] | (entry->mac[4] << 8), HR_FDBWDL);
 723
 724        /* Meta data */
 725        meta |= entry->portmask << HR_FDBWRM0_PORTMASK_SHIFT;
 726        if (entry->is_obt)
 727                meta |= HR_FDBWRM0_OBT;
 728        if (entry->pass_blocked)
 729                meta |= HR_FDBWRM0_PASS_BLOCKED;
 730        if (entry->reprio_en) {
 731                meta |= HR_FDBWRM0_REPRIO_EN;
 732                meta |= entry->reprio_tc << HR_FDBWRM0_REPRIO_TC_SHIFT;
 733        }
 734        hellcreek_write(hellcreek, meta, HR_FDBWRM0);
 735
 736        /* Commit */
 737        hellcreek_write(hellcreek, 0x00, HR_FDBWRCMD);
 738
 739        /* Wait until done */
 740        return hellcreek_wait_fdb_ready(hellcreek);
 741}
 742
 743static int __hellcreek_fdb_del(struct hellcreek *hellcreek,
 744                               const struct hellcreek_fdb_entry *entry)
 745{
 746        dev_dbg(hellcreek->dev, "Delete FDB entry: MAC=%pM!\n", entry->mac);
 747
 748        /* Delete by matching idx */
 749        hellcreek_write(hellcreek, entry->idx | HR_FDBWRCMD_FDBDEL, HR_FDBWRCMD);
 750
 751        /* Wait until done */
 752        return hellcreek_wait_fdb_ready(hellcreek);
 753}
 754
 755static void hellcreek_populate_fdb_entry(struct hellcreek *hellcreek,
 756                                         struct hellcreek_fdb_entry *entry,
 757                                         size_t idx)
 758{
 759        unsigned char addr[ETH_ALEN];
 760        u16 meta, mac;
 761
 762        /* Read values */
 763        meta    = hellcreek_read(hellcreek, HR_FDBMDRD);
 764        mac     = hellcreek_read(hellcreek, HR_FDBRDL);
 765        addr[5] = mac & 0xff;
 766        addr[4] = (mac & 0xff00) >> 8;
 767        mac     = hellcreek_read(hellcreek, HR_FDBRDM);
 768        addr[3] = mac & 0xff;
 769        addr[2] = (mac & 0xff00) >> 8;
 770        mac     = hellcreek_read(hellcreek, HR_FDBRDH);
 771        addr[1] = mac & 0xff;
 772        addr[0] = (mac & 0xff00) >> 8;
 773
 774        /* Populate @entry */
 775        memcpy(entry->mac, addr, sizeof(addr));
 776        entry->idx          = idx;
 777        entry->portmask     = (meta & HR_FDBMDRD_PORTMASK_MASK) >>
 778                HR_FDBMDRD_PORTMASK_SHIFT;
 779        entry->age          = (meta & HR_FDBMDRD_AGE_MASK) >>
 780                HR_FDBMDRD_AGE_SHIFT;
 781        entry->is_obt       = !!(meta & HR_FDBMDRD_OBT);
 782        entry->pass_blocked = !!(meta & HR_FDBMDRD_PASS_BLOCKED);
 783        entry->is_static    = !!(meta & HR_FDBMDRD_STATIC);
 784        entry->reprio_tc    = (meta & HR_FDBMDRD_REPRIO_TC_MASK) >>
 785                HR_FDBMDRD_REPRIO_TC_SHIFT;
 786        entry->reprio_en    = !!(meta & HR_FDBMDRD_REPRIO_EN);
 787}
 788
 789/* Retrieve the index of a FDB entry by mac address. Currently we search through
 790 * the complete table in hardware. If that's too slow, we might have to cache
 791 * the complete FDB table in software.
 792 */
 793static int hellcreek_fdb_get(struct hellcreek *hellcreek,
 794                             const unsigned char *dest,
 795                             struct hellcreek_fdb_entry *entry)
 796{
 797        size_t i;
 798
 799        /* Set read pointer to zero: The read of HR_FDBMAX (read-only register)
 800         * should reset the internal pointer. But, that doesn't work. The vendor
 801         * suggested a subsequent write as workaround. Same for HR_FDBRDH below.
 802         */
 803        hellcreek_read(hellcreek, HR_FDBMAX);
 804        hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
 805
 806        /* We have to read the complete table, because the switch/driver might
 807         * enter new entries anywhere.
 808         */
 809        for (i = 0; i < hellcreek->fdb_entries; ++i) {
 810                struct hellcreek_fdb_entry tmp = { 0 };
 811
 812                /* Read entry */
 813                hellcreek_populate_fdb_entry(hellcreek, &tmp, i);
 814
 815                /* Force next entry */
 816                hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
 817
 818                if (memcmp(tmp.mac, dest, ETH_ALEN))
 819                        continue;
 820
 821                /* Match found */
 822                memcpy(entry, &tmp, sizeof(*entry));
 823
 824                return 0;
 825        }
 826
 827        return -ENOENT;
 828}
 829
 830static int hellcreek_fdb_add(struct dsa_switch *ds, int port,
 831                             const unsigned char *addr, u16 vid,
 832                             struct dsa_db db)
 833{
 834        struct hellcreek_fdb_entry entry = { 0 };
 835        struct hellcreek *hellcreek = ds->priv;
 836        int ret;
 837
 838        dev_dbg(hellcreek->dev, "Add FDB entry for MAC=%pM\n", addr);
 839
 840        mutex_lock(&hellcreek->reg_lock);
 841
 842        ret = hellcreek_fdb_get(hellcreek, addr, &entry);
 843        if (ret) {
 844                /* Not found */
 845                memcpy(entry.mac, addr, sizeof(entry.mac));
 846                entry.portmask = BIT(port);
 847
 848                ret = __hellcreek_fdb_add(hellcreek, &entry);
 849                if (ret) {
 850                        dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
 851                        goto out;
 852                }
 853        } else {
 854                /* Found */
 855                ret = __hellcreek_fdb_del(hellcreek, &entry);
 856                if (ret) {
 857                        dev_err(hellcreek->dev, "Failed to delete FDB entry!\n");
 858                        goto out;
 859                }
 860
 861                entry.portmask |= BIT(port);
 862
 863                ret = __hellcreek_fdb_add(hellcreek, &entry);
 864                if (ret) {
 865                        dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
 866                        goto out;
 867                }
 868        }
 869
 870out:
 871        mutex_unlock(&hellcreek->reg_lock);
 872
 873        return ret;
 874}
 875
 876static int hellcreek_fdb_del(struct dsa_switch *ds, int port,
 877                             const unsigned char *addr, u16 vid,
 878                             struct dsa_db db)
 879{
 880        struct hellcreek_fdb_entry entry = { 0 };
 881        struct hellcreek *hellcreek = ds->priv;
 882        int ret;
 883
 884        dev_dbg(hellcreek->dev, "Delete FDB entry for MAC=%pM\n", addr);
 885
 886        mutex_lock(&hellcreek->reg_lock);
 887
 888        ret = hellcreek_fdb_get(hellcreek, addr, &entry);
 889        if (ret) {
 890                /* Not found */
 891                dev_err(hellcreek->dev, "FDB entry for deletion not found!\n");
 892        } else {
 893                /* Found */
 894                ret = __hellcreek_fdb_del(hellcreek, &entry);
 895                if (ret) {
 896                        dev_err(hellcreek->dev, "Failed to delete FDB entry!\n");
 897                        goto out;
 898                }
 899
 900                entry.portmask &= ~BIT(port);
 901
 902                if (entry.portmask != 0x00) {
 903                        ret = __hellcreek_fdb_add(hellcreek, &entry);
 904                        if (ret) {
 905                                dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
 906                                goto out;
 907                        }
 908                }
 909        }
 910
 911out:
 912        mutex_unlock(&hellcreek->reg_lock);
 913
 914        return ret;
 915}
 916
 917static int hellcreek_fdb_dump(struct dsa_switch *ds, int port,
 918                              dsa_fdb_dump_cb_t *cb, void *data)
 919{
 920        struct hellcreek *hellcreek = ds->priv;
 921        u16 entries;
 922        int ret = 0;
 923        size_t i;
 924
 925        mutex_lock(&hellcreek->reg_lock);
 926
 927        /* Set read pointer to zero: The read of HR_FDBMAX (read-only register)
 928         * should reset the internal pointer. But, that doesn't work. The vendor
 929         * suggested a subsequent write as workaround. Same for HR_FDBRDH below.
 930         */
 931        entries = hellcreek_read(hellcreek, HR_FDBMAX);
 932        hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
 933
 934        dev_dbg(hellcreek->dev, "FDB dump for port %d, entries=%d!\n", port, entries);
 935
 936        /* Read table */
 937        for (i = 0; i < hellcreek->fdb_entries; ++i) {
 938                struct hellcreek_fdb_entry entry = { 0 };
 939
 940                /* Read entry */
 941                hellcreek_populate_fdb_entry(hellcreek, &entry, i);
 942
 943                /* Force next entry */
 944                hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
 945
 946                /* Check valid */
 947                if (is_zero_ether_addr(entry.mac))
 948                        continue;
 949
 950                /* Check port mask */
 951                if (!(entry.portmask & BIT(port)))
 952                        continue;
 953
 954                ret = cb(entry.mac, 0, entry.is_static, data);
 955                if (ret)
 956                        break;
 957        }
 958
 959        mutex_unlock(&hellcreek->reg_lock);
 960
 961        return ret;
 962}
 963
 964static int hellcreek_vlan_filtering(struct dsa_switch *ds, int port,
 965                                    bool vlan_filtering,
 966                                    struct netlink_ext_ack *extack)
 967{
 968        struct hellcreek *hellcreek = ds->priv;
 969
 970        dev_dbg(hellcreek->dev, "%s VLAN filtering on port %d\n",
 971                vlan_filtering ? "Enable" : "Disable", port);
 972
 973        /* Configure port to drop packages with not known vids */
 974        hellcreek_setup_ingressflt(hellcreek, port, vlan_filtering);
 975
 976        /* Enable VLAN awareness on the switch. This save due to
 977         * ds->vlan_filtering_is_global.
 978         */
 979        hellcreek_setup_vlan_awareness(hellcreek, vlan_filtering);
 980
 981        return 0;
 982}
 983
 984static int hellcreek_enable_ip_core(struct hellcreek *hellcreek)
 985{
 986        int ret;
 987        u16 val;
 988
 989        mutex_lock(&hellcreek->reg_lock);
 990
 991        val = hellcreek_read(hellcreek, HR_CTRL_C);
 992        val |= HR_CTRL_C_ENABLE;
 993        hellcreek_write(hellcreek, val, HR_CTRL_C);
 994        ret = hellcreek_wait_until_transitioned(hellcreek);
 995
 996        mutex_unlock(&hellcreek->reg_lock);
 997
 998        return ret;
 999}
1000
1001static void hellcreek_setup_cpu_and_tunnel_port(struct hellcreek *hellcreek)
1002{
1003        struct hellcreek_port *tunnel_port = &hellcreek->ports[TUNNEL_PORT];
1004        struct hellcreek_port *cpu_port = &hellcreek->ports[CPU_PORT];
1005        u16 ptcfg = 0;
1006
1007        ptcfg |= HR_PTCFG_LEARNING_EN | HR_PTCFG_ADMIN_EN;
1008
1009        mutex_lock(&hellcreek->reg_lock);
1010
1011        hellcreek_select_port(hellcreek, CPU_PORT);
1012        hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
1013
1014        hellcreek_select_port(hellcreek, TUNNEL_PORT);
1015        hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
1016
1017        cpu_port->ptcfg    = ptcfg;
1018        tunnel_port->ptcfg = ptcfg;
1019
1020        mutex_unlock(&hellcreek->reg_lock);
1021}
1022
1023static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
1024{
1025        int i;
1026
1027        /* The switch has multiple egress queues per port. The queue is selected
1028         * via the PCP field in the VLAN header. The switch internally deals
1029         * with traffic classes instead of PCP values and this mapping is
1030         * configurable.
1031         *
1032         * The default mapping is (PCP - TC):
1033         *  7 - 7
1034         *  6 - 6
1035         *  5 - 5
1036         *  4 - 4
1037         *  3 - 3
1038         *  2 - 1
1039         *  1 - 0
1040         *  0 - 2
1041         *
1042         * The default should be an identity mapping.
1043         */
1044
1045        for (i = 0; i < 8; ++i) {
1046                mutex_lock(&hellcreek->reg_lock);
1047
1048                hellcreek_select_prio(hellcreek, i);
1049                hellcreek_write(hellcreek,
1050                                i << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
1051                                HR_PRTCCFG);
1052
1053                mutex_unlock(&hellcreek->reg_lock);
1054        }
1055}
1056
1057static int hellcreek_setup_fdb(struct hellcreek *hellcreek)
1058{
1059        static struct hellcreek_fdb_entry l2_ptp = {
1060                /* MAC: 01-1B-19-00-00-00 */
1061                .mac          = { 0x01, 0x1b, 0x19, 0x00, 0x00, 0x00 },
1062                .portmask     = 0x03,   /* Management ports */
1063                .age          = 0,
1064                .is_obt       = 0,
1065                .pass_blocked = 0,
1066                .is_static    = 1,
1067                .reprio_tc    = 6,      /* TC: 6 as per IEEE 802.1AS */
1068                .reprio_en    = 1,
1069        };
1070        static struct hellcreek_fdb_entry udp4_ptp = {
1071                /* MAC: 01-00-5E-00-01-81 */
1072                .mac          = { 0x01, 0x00, 0x5e, 0x00, 0x01, 0x81 },
1073                .portmask     = 0x03,   /* Management ports */
1074                .age          = 0,
1075                .is_obt       = 0,
1076                .pass_blocked = 0,
1077                .is_static    = 1,
1078                .reprio_tc    = 6,
1079                .reprio_en    = 1,
1080        };
1081        static struct hellcreek_fdb_entry udp6_ptp = {
1082                /* MAC: 33-33-00-00-01-81 */
1083                .mac          = { 0x33, 0x33, 0x00, 0x00, 0x01, 0x81 },
1084                .portmask     = 0x03,   /* Management ports */
1085                .age          = 0,
1086                .is_obt       = 0,
1087                .pass_blocked = 0,
1088                .is_static    = 1,
1089                .reprio_tc    = 6,
1090                .reprio_en    = 1,
1091        };
1092        static struct hellcreek_fdb_entry l2_p2p = {
1093                /* MAC: 01-80-C2-00-00-0E */
1094                .mac          = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e },
1095                .portmask     = 0x03,   /* Management ports */
1096                .age          = 0,
1097                .is_obt       = 0,
1098                .pass_blocked = 1,
1099                .is_static    = 1,
1100                .reprio_tc    = 6,      /* TC: 6 as per IEEE 802.1AS */
1101                .reprio_en    = 1,
1102        };
1103        static struct hellcreek_fdb_entry udp4_p2p = {
1104                /* MAC: 01-00-5E-00-00-6B */
1105                .mac          = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x6b },
1106                .portmask     = 0x03,   /* Management ports */
1107                .age          = 0,
1108                .is_obt       = 0,
1109                .pass_blocked = 1,
1110                .is_static    = 1,
1111                .reprio_tc    = 6,
1112                .reprio_en    = 1,
1113        };
1114        static struct hellcreek_fdb_entry udp6_p2p = {
1115                /* MAC: 33-33-00-00-00-6B */
1116                .mac          = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x6b },
1117                .portmask     = 0x03,   /* Management ports */
1118                .age          = 0,
1119                .is_obt       = 0,
1120                .pass_blocked = 1,
1121                .is_static    = 1,
1122                .reprio_tc    = 6,
1123                .reprio_en    = 1,
1124        };
1125        static struct hellcreek_fdb_entry stp = {
1126                /* MAC: 01-80-C2-00-00-00 */
1127                .mac          = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 },
1128                .portmask     = 0x03,   /* Management ports */
1129                .age          = 0,
1130                .is_obt       = 0,
1131                .pass_blocked = 1,
1132                .is_static    = 1,
1133                .reprio_tc    = 6,
1134                .reprio_en    = 1,
1135        };
1136        int ret;
1137
1138        mutex_lock(&hellcreek->reg_lock);
1139        ret = __hellcreek_fdb_add(hellcreek, &l2_ptp);
1140        if (ret)
1141                goto out;
1142        ret = __hellcreek_fdb_add(hellcreek, &udp4_ptp);
1143        if (ret)
1144                goto out;
1145        ret = __hellcreek_fdb_add(hellcreek, &udp6_ptp);
1146        if (ret)
1147                goto out;
1148        ret = __hellcreek_fdb_add(hellcreek, &l2_p2p);
1149        if (ret)
1150                goto out;
1151        ret = __hellcreek_fdb_add(hellcreek, &udp4_p2p);
1152        if (ret)
1153                goto out;
1154        ret = __hellcreek_fdb_add(hellcreek, &udp6_p2p);
1155        if (ret)
1156                goto out;
1157        ret = __hellcreek_fdb_add(hellcreek, &stp);
1158out:
1159        mutex_unlock(&hellcreek->reg_lock);
1160
1161        return ret;
1162}
1163
1164static int hellcreek_devlink_info_get(struct dsa_switch *ds,
1165                                      struct devlink_info_req *req,
1166                                      struct netlink_ext_ack *extack)
1167{
1168        struct hellcreek *hellcreek = ds->priv;
1169        int ret;
1170
1171        ret = devlink_info_driver_name_put(req, "hellcreek");
1172        if (ret)
1173                return ret;
1174
1175        return devlink_info_version_fixed_put(req,
1176                                              DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
1177                                              hellcreek->pdata->name);
1178}
1179
1180static u64 hellcreek_devlink_vlan_table_get(void *priv)
1181{
1182        struct hellcreek *hellcreek = priv;
1183        u64 count = 0;
1184        int i;
1185
1186        mutex_lock(&hellcreek->reg_lock);
1187        for (i = 0; i < VLAN_N_VID; ++i)
1188                if (hellcreek->vidmbrcfg[i])
1189                        count++;
1190        mutex_unlock(&hellcreek->reg_lock);
1191
1192        return count;
1193}
1194
1195static u64 hellcreek_devlink_fdb_table_get(void *priv)
1196{
1197        struct hellcreek *hellcreek = priv;
1198        u64 count = 0;
1199
1200        /* Reading this register has side effects. Synchronize against the other
1201         * FDB operations.
1202         */
1203        mutex_lock(&hellcreek->reg_lock);
1204        count = hellcreek_read(hellcreek, HR_FDBMAX);
1205        mutex_unlock(&hellcreek->reg_lock);
1206
1207        return count;
1208}
1209
1210static int hellcreek_setup_devlink_resources(struct dsa_switch *ds)
1211{
1212        struct devlink_resource_size_params size_vlan_params;
1213        struct devlink_resource_size_params size_fdb_params;
1214        struct hellcreek *hellcreek = ds->priv;
1215        int err;
1216
1217        devlink_resource_size_params_init(&size_vlan_params, VLAN_N_VID,
1218                                          VLAN_N_VID,
1219                                          1, DEVLINK_RESOURCE_UNIT_ENTRY);
1220
1221        devlink_resource_size_params_init(&size_fdb_params,
1222                                          hellcreek->fdb_entries,
1223                                          hellcreek->fdb_entries,
1224                                          1, DEVLINK_RESOURCE_UNIT_ENTRY);
1225
1226        err = dsa_devlink_resource_register(ds, "VLAN", VLAN_N_VID,
1227                                            HELLCREEK_DEVLINK_PARAM_ID_VLAN_TABLE,
1228                                            DEVLINK_RESOURCE_ID_PARENT_TOP,
1229                                            &size_vlan_params);
1230        if (err)
1231                goto out;
1232
1233        err = dsa_devlink_resource_register(ds, "FDB", hellcreek->fdb_entries,
1234                                            HELLCREEK_DEVLINK_PARAM_ID_FDB_TABLE,
1235                                            DEVLINK_RESOURCE_ID_PARENT_TOP,
1236                                            &size_fdb_params);
1237        if (err)
1238                goto out;
1239
1240        dsa_devlink_resource_occ_get_register(ds,
1241                                              HELLCREEK_DEVLINK_PARAM_ID_VLAN_TABLE,
1242                                              hellcreek_devlink_vlan_table_get,
1243                                              hellcreek);
1244
1245        dsa_devlink_resource_occ_get_register(ds,
1246                                              HELLCREEK_DEVLINK_PARAM_ID_FDB_TABLE,
1247                                              hellcreek_devlink_fdb_table_get,
1248                                              hellcreek);
1249
1250        return 0;
1251
1252out:
1253        dsa_devlink_resources_unregister(ds);
1254
1255        return err;
1256}
1257
1258static int hellcreek_devlink_region_vlan_snapshot(struct devlink *dl,
1259                                                  const struct devlink_region_ops *ops,
1260                                                  struct netlink_ext_ack *extack,
1261                                                  u8 **data)
1262{
1263        struct hellcreek_devlink_vlan_entry *table, *entry;
1264        struct dsa_switch *ds = dsa_devlink_to_ds(dl);
1265        struct hellcreek *hellcreek = ds->priv;
1266        int i;
1267
1268        table = kcalloc(VLAN_N_VID, sizeof(*entry), GFP_KERNEL);
1269        if (!table)
1270                return -ENOMEM;
1271
1272        entry = table;
1273
1274        mutex_lock(&hellcreek->reg_lock);
1275        for (i = 0; i < VLAN_N_VID; ++i, ++entry) {
1276                entry->member = hellcreek->vidmbrcfg[i];
1277                entry->vid    = i;
1278        }
1279        mutex_unlock(&hellcreek->reg_lock);
1280
1281        *data = (u8 *)table;
1282
1283        return 0;
1284}
1285
1286static int hellcreek_devlink_region_fdb_snapshot(struct devlink *dl,
1287                                                 const struct devlink_region_ops *ops,
1288                                                 struct netlink_ext_ack *extack,
1289                                                 u8 **data)
1290{
1291        struct dsa_switch *ds = dsa_devlink_to_ds(dl);
1292        struct hellcreek_fdb_entry *table, *entry;
1293        struct hellcreek *hellcreek = ds->priv;
1294        size_t i;
1295
1296        table = kcalloc(hellcreek->fdb_entries, sizeof(*entry), GFP_KERNEL);
1297        if (!table)
1298                return -ENOMEM;
1299
1300        entry = table;
1301
1302        mutex_lock(&hellcreek->reg_lock);
1303
1304        /* Start table read */
1305        hellcreek_read(hellcreek, HR_FDBMAX);
1306        hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
1307
1308        for (i = 0; i < hellcreek->fdb_entries; ++i, ++entry) {
1309                /* Read current entry */
1310                hellcreek_populate_fdb_entry(hellcreek, entry, i);
1311
1312                /* Advance read pointer */
1313                hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
1314        }
1315
1316        mutex_unlock(&hellcreek->reg_lock);
1317
1318        *data = (u8 *)table;
1319
1320        return 0;
1321}
1322
1323static struct devlink_region_ops hellcreek_region_vlan_ops = {
1324        .name       = "vlan",
1325        .snapshot   = hellcreek_devlink_region_vlan_snapshot,
1326        .destructor = kfree,
1327};
1328
1329static struct devlink_region_ops hellcreek_region_fdb_ops = {
1330        .name       = "fdb",
1331        .snapshot   = hellcreek_devlink_region_fdb_snapshot,
1332        .destructor = kfree,
1333};
1334
1335static int hellcreek_setup_devlink_regions(struct dsa_switch *ds)
1336{
1337        struct hellcreek *hellcreek = ds->priv;
1338        struct devlink_region_ops *ops;
1339        struct devlink_region *region;
1340        u64 size;
1341        int ret;
1342
1343        /* VLAN table */
1344        size = VLAN_N_VID * sizeof(struct hellcreek_devlink_vlan_entry);
1345        ops  = &hellcreek_region_vlan_ops;
1346
1347        region = dsa_devlink_region_create(ds, ops, 1, size);
1348        if (IS_ERR(region))
1349                return PTR_ERR(region);
1350
1351        hellcreek->vlan_region = region;
1352
1353        /* FDB table */
1354        size = hellcreek->fdb_entries * sizeof(struct hellcreek_fdb_entry);
1355        ops  = &hellcreek_region_fdb_ops;
1356
1357        region = dsa_devlink_region_create(ds, ops, 1, size);
1358        if (IS_ERR(region)) {
1359                ret = PTR_ERR(region);
1360                goto err_fdb;
1361        }
1362
1363        hellcreek->fdb_region = region;
1364
1365        return 0;
1366
1367err_fdb:
1368        dsa_devlink_region_destroy(hellcreek->vlan_region);
1369
1370        return ret;
1371}
1372
1373static void hellcreek_teardown_devlink_regions(struct dsa_switch *ds)
1374{
1375        struct hellcreek *hellcreek = ds->priv;
1376
1377        dsa_devlink_region_destroy(hellcreek->fdb_region);
1378        dsa_devlink_region_destroy(hellcreek->vlan_region);
1379}
1380
1381static int hellcreek_setup(struct dsa_switch *ds)
1382{
1383        struct hellcreek *hellcreek = ds->priv;
1384        u16 swcfg = 0;
1385        int ret, i;
1386
1387        dev_dbg(hellcreek->dev, "Set up the switch\n");
1388
1389        /* Let's go */
1390        ret = hellcreek_enable_ip_core(hellcreek);
1391        if (ret) {
1392                dev_err(hellcreek->dev, "Failed to enable IP core!\n");
1393                return ret;
1394        }
1395
1396        /* Enable CPU/Tunnel ports */
1397        hellcreek_setup_cpu_and_tunnel_port(hellcreek);
1398
1399        /* Switch config: Keep defaults, enable FDB aging and learning and tag
1400         * each frame from/to cpu port for DSA tagging.  Also enable the length
1401         * aware shaping mode. This eliminates the need for Qbv guard bands.
1402         */
1403        swcfg |= HR_SWCFG_FDBAGE_EN |
1404                HR_SWCFG_FDBLRN_EN  |
1405                HR_SWCFG_ALWAYS_OBT |
1406                (HR_SWCFG_LAS_ON << HR_SWCFG_LAS_MODE_SHIFT);
1407        hellcreek->swcfg = swcfg;
1408        hellcreek_write(hellcreek, swcfg, HR_SWCFG);
1409
1410        /* Initial vlan membership to reflect port separation */
1411        for (i = 0; i < ds->num_ports; ++i) {
1412                if (!dsa_is_user_port(ds, i))
1413                        continue;
1414
1415                hellcreek_setup_vlan_membership(ds, i, true);
1416        }
1417
1418        /* Configure PCP <-> TC mapping */
1419        hellcreek_setup_tc_identity_mapping(hellcreek);
1420
1421        /* The VLAN awareness is a global switch setting. Therefore, mixed vlan
1422         * filtering setups are not supported.
1423         */
1424        ds->vlan_filtering_is_global = true;
1425        ds->needs_standalone_vlan_filtering = true;
1426
1427        /* Intercept _all_ PTP multicast traffic */
1428        ret = hellcreek_setup_fdb(hellcreek);
1429        if (ret) {
1430                dev_err(hellcreek->dev,
1431                        "Failed to insert static PTP FDB entries\n");
1432                return ret;
1433        }
1434
1435        /* Register devlink resources with DSA */
1436        ret = hellcreek_setup_devlink_resources(ds);
1437        if (ret) {
1438                dev_err(hellcreek->dev,
1439                        "Failed to setup devlink resources!\n");
1440                return ret;
1441        }
1442
1443        ret = hellcreek_setup_devlink_regions(ds);
1444        if (ret) {
1445                dev_err(hellcreek->dev,
1446                        "Failed to setup devlink regions!\n");
1447                goto err_regions;
1448        }
1449
1450        return 0;
1451
1452err_regions:
1453        dsa_devlink_resources_unregister(ds);
1454
1455        return ret;
1456}
1457
1458static void hellcreek_teardown(struct dsa_switch *ds)
1459{
1460        hellcreek_teardown_devlink_regions(ds);
1461        dsa_devlink_resources_unregister(ds);
1462}
1463
1464static void hellcreek_phylink_get_caps(struct dsa_switch *ds, int port,
1465                                       struct phylink_config *config)
1466{
1467        struct hellcreek *hellcreek = ds->priv;
1468
1469        __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces);
1470        __set_bit(PHY_INTERFACE_MODE_RGMII, config->supported_interfaces);
1471
1472        /* Include GMII - the hardware does not support this interface
1473         * mode, but it's the default interface mode for phylib, so we
1474         * need it for compatibility with existing DT.
1475         */
1476        __set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces);
1477
1478        /* The MAC settings are a hardware configuration option and cannot be
1479         * changed at run time or by strapping. Therefore the attached PHYs
1480         * should be programmed to only advertise settings which are supported
1481         * by the hardware.
1482         */
1483        if (hellcreek->pdata->is_100_mbits)
1484                config->mac_capabilities = MAC_100FD;
1485        else
1486                config->mac_capabilities = MAC_1000FD;
1487}
1488
1489static int
1490hellcreek_port_prechangeupper(struct dsa_switch *ds, int port,
1491                              struct netdev_notifier_changeupper_info *info)
1492{
1493        struct hellcreek *hellcreek = ds->priv;
1494        bool used = true;
1495        int ret = -EBUSY;
1496        u16 vid;
1497        int i;
1498
1499        dev_dbg(hellcreek->dev, "Pre change upper for port %d\n", port);
1500
1501        /*
1502         * Deny VLAN devices on top of lan ports with the same VLAN ids, because
1503         * it breaks the port separation due to the private VLANs. Example:
1504         *
1505         * lan0.100 *and* lan1.100 cannot be used in parallel. However, lan0.99
1506         * and lan1.100 works.
1507         */
1508
1509        if (!is_vlan_dev(info->upper_dev))
1510                return 0;
1511
1512        vid = vlan_dev_vlan_id(info->upper_dev);
1513
1514        /* For all ports, check bitmaps */
1515        mutex_lock(&hellcreek->vlan_lock);
1516        for (i = 0; i < hellcreek->pdata->num_ports; ++i) {
1517                if (!dsa_is_user_port(ds, i))
1518                        continue;
1519
1520                if (port == i)
1521                        continue;
1522
1523                used = used && test_bit(vid, hellcreek->ports[i].vlan_dev_bitmap);
1524        }
1525
1526        if (used)
1527                goto out;
1528
1529        /* Update bitmap */
1530        set_bit(vid, hellcreek->ports[port].vlan_dev_bitmap);
1531
1532        ret = 0;
1533
1534out:
1535        mutex_unlock(&hellcreek->vlan_lock);
1536
1537        return ret;
1538}
1539
1540static void hellcreek_setup_gcl(struct hellcreek *hellcreek, int port,
1541                                const struct tc_taprio_qopt_offload *schedule)
1542{
1543        const struct tc_taprio_sched_entry *cur, *initial, *next;
1544        size_t i;
1545
1546        cur = initial = &schedule->entries[0];
1547        next = cur + 1;
1548
1549        for (i = 1; i <= schedule->num_entries; ++i) {
1550                u16 data;
1551                u8 gates;
1552
1553                if (i == schedule->num_entries)
1554                        gates = initial->gate_mask ^
1555                                cur->gate_mask;
1556                else
1557                        gates = next->gate_mask ^
1558                                cur->gate_mask;
1559
1560                data = gates;
1561
1562                if (i == schedule->num_entries)
1563                        data |= TR_GCLDAT_GCLWRLAST;
1564
1565                /* Gates states */
1566                hellcreek_write(hellcreek, data, TR_GCLDAT);
1567
1568                /* Time interval */
1569                hellcreek_write(hellcreek,
1570                                cur->interval & 0x0000ffff,
1571                                TR_GCLTIL);
1572                hellcreek_write(hellcreek,
1573                                (cur->interval & 0xffff0000) >> 16,
1574                                TR_GCLTIH);
1575
1576                /* Commit entry */
1577                data = ((i - 1) << TR_GCLCMD_GCLWRADR_SHIFT) |
1578                        (initial->gate_mask <<
1579                         TR_GCLCMD_INIT_GATE_STATES_SHIFT);
1580                hellcreek_write(hellcreek, data, TR_GCLCMD);
1581
1582                cur++;
1583                next++;
1584        }
1585}
1586
1587static void hellcreek_set_cycle_time(struct hellcreek *hellcreek,
1588                                     const struct tc_taprio_qopt_offload *schedule)
1589{
1590        u32 cycle_time = schedule->cycle_time;
1591
1592        hellcreek_write(hellcreek, cycle_time & 0x0000ffff, TR_CTWRL);
1593        hellcreek_write(hellcreek, (cycle_time & 0xffff0000) >> 16, TR_CTWRH);
1594}
1595
1596static void hellcreek_switch_schedule(struct hellcreek *hellcreek,
1597                                      ktime_t start_time)
1598{
1599        struct timespec64 ts = ktime_to_timespec64(start_time);
1600
1601        /* Start schedule at this point of time */
1602        hellcreek_write(hellcreek, ts.tv_nsec & 0x0000ffff, TR_ESTWRL);
1603        hellcreek_write(hellcreek, (ts.tv_nsec & 0xffff0000) >> 16, TR_ESTWRH);
1604
1605        /* Arm timer, set seconds and switch schedule */
1606        hellcreek_write(hellcreek, TR_ESTCMD_ESTARM | TR_ESTCMD_ESTSWCFG |
1607                        ((ts.tv_sec & TR_ESTCMD_ESTSEC_MASK) <<
1608                         TR_ESTCMD_ESTSEC_SHIFT), TR_ESTCMD);
1609}
1610
1611static bool hellcreek_schedule_startable(struct hellcreek *hellcreek, int port)
1612{
1613        struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
1614        s64 base_time_ns, current_ns;
1615
1616        /* The switch allows a schedule to be started only eight seconds within
1617         * the future. Therefore, check the current PTP time if the schedule is
1618         * startable or not.
1619         */
1620
1621        /* Use the "cached" time. That should be alright, as it's updated quite
1622         * frequently in the PTP code.
1623         */
1624        mutex_lock(&hellcreek->ptp_lock);
1625        current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts;
1626        mutex_unlock(&hellcreek->ptp_lock);
1627
1628        /* Calculate difference to admin base time */
1629        base_time_ns = ktime_to_ns(hellcreek_port->current_schedule->base_time);
1630
1631        return base_time_ns - current_ns < (s64)4 * NSEC_PER_SEC;
1632}
1633
1634static void hellcreek_start_schedule(struct hellcreek *hellcreek, int port)
1635{
1636        struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
1637        ktime_t base_time, current_time;
1638        s64 current_ns;
1639        u32 cycle_time;
1640
1641        /* First select port */
1642        hellcreek_select_tgd(hellcreek, port);
1643
1644        /* Forward base time into the future if needed */
1645        mutex_lock(&hellcreek->ptp_lock);
1646        current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts;
1647        mutex_unlock(&hellcreek->ptp_lock);
1648
1649        current_time = ns_to_ktime(current_ns);
1650        base_time    = hellcreek_port->current_schedule->base_time;
1651        cycle_time   = hellcreek_port->current_schedule->cycle_time;
1652
1653        if (ktime_compare(current_time, base_time) > 0) {
1654                s64 n;
1655
1656                n = div64_s64(ktime_sub_ns(current_time, base_time),
1657                              cycle_time);
1658                base_time = ktime_add_ns(base_time, (n + 1) * cycle_time);
1659        }
1660
1661        /* Set admin base time and switch schedule */
1662        hellcreek_switch_schedule(hellcreek, base_time);
1663
1664        taprio_offload_free(hellcreek_port->current_schedule);
1665        hellcreek_port->current_schedule = NULL;
1666
1667        dev_dbg(hellcreek->dev, "Armed EST timer for port %d\n",
1668                hellcreek_port->port);
1669}
1670
1671static void hellcreek_check_schedule(struct work_struct *work)
1672{
1673        struct delayed_work *dw = to_delayed_work(work);
1674        struct hellcreek_port *hellcreek_port;
1675        struct hellcreek *hellcreek;
1676        bool startable;
1677
1678        hellcreek_port = dw_to_hellcreek_port(dw);
1679        hellcreek = hellcreek_port->hellcreek;
1680
1681        mutex_lock(&hellcreek->reg_lock);
1682
1683        /* Check starting time */
1684        startable = hellcreek_schedule_startable(hellcreek,
1685                                                 hellcreek_port->port);
1686        if (startable) {
1687                hellcreek_start_schedule(hellcreek, hellcreek_port->port);
1688                mutex_unlock(&hellcreek->reg_lock);
1689                return;
1690        }
1691
1692        mutex_unlock(&hellcreek->reg_lock);
1693
1694        /* Reschedule */
1695        schedule_delayed_work(&hellcreek_port->schedule_work,
1696                              HELLCREEK_SCHEDULE_PERIOD);
1697}
1698
1699static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port,
1700                                       struct tc_taprio_qopt_offload *taprio)
1701{
1702        struct hellcreek *hellcreek = ds->priv;
1703        struct hellcreek_port *hellcreek_port;
1704        bool startable;
1705        u16 ctrl;
1706
1707        hellcreek_port = &hellcreek->ports[port];
1708
1709        dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n",
1710                port);
1711
1712        /* First cancel delayed work */
1713        cancel_delayed_work_sync(&hellcreek_port->schedule_work);
1714
1715        mutex_lock(&hellcreek->reg_lock);
1716
1717        if (hellcreek_port->current_schedule) {
1718                taprio_offload_free(hellcreek_port->current_schedule);
1719                hellcreek_port->current_schedule = NULL;
1720        }
1721        hellcreek_port->current_schedule = taprio_offload_get(taprio);
1722
1723        /* Then select port */
1724        hellcreek_select_tgd(hellcreek, port);
1725
1726        /* Enable gating and keep defaults */
1727        ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN;
1728        hellcreek_write(hellcreek, ctrl, TR_TGDCTRL);
1729
1730        /* Cancel pending schedule */
1731        hellcreek_write(hellcreek, 0x00, TR_ESTCMD);
1732
1733        /* Setup a new schedule */
1734        hellcreek_setup_gcl(hellcreek, port, hellcreek_port->current_schedule);
1735
1736        /* Configure cycle time */
1737        hellcreek_set_cycle_time(hellcreek, hellcreek_port->current_schedule);
1738
1739        /* Check starting time */
1740        startable = hellcreek_schedule_startable(hellcreek, port);
1741        if (startable) {
1742                hellcreek_start_schedule(hellcreek, port);
1743                mutex_unlock(&hellcreek->reg_lock);
1744                return 0;
1745        }
1746
1747        mutex_unlock(&hellcreek->reg_lock);
1748
1749        /* Schedule periodic schedule check */
1750        schedule_delayed_work(&hellcreek_port->schedule_work,
1751                              HELLCREEK_SCHEDULE_PERIOD);
1752
1753        return 0;
1754}
1755
1756static int hellcreek_port_del_schedule(struct dsa_switch *ds, int port)
1757{
1758        struct hellcreek *hellcreek = ds->priv;
1759        struct hellcreek_port *hellcreek_port;
1760
1761        hellcreek_port = &hellcreek->ports[port];
1762
1763        dev_dbg(hellcreek->dev, "Remove traffic schedule on port %d\n", port);
1764
1765        /* First cancel delayed work */
1766        cancel_delayed_work_sync(&hellcreek_port->schedule_work);
1767
1768        mutex_lock(&hellcreek->reg_lock);
1769
1770        if (hellcreek_port->current_schedule) {
1771                taprio_offload_free(hellcreek_port->current_schedule);
1772                hellcreek_port->current_schedule = NULL;
1773        }
1774
1775        /* Then select port */
1776        hellcreek_select_tgd(hellcreek, port);
1777
1778        /* Disable gating and return to regular switching flow */
1779        hellcreek_write(hellcreek, 0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT,
1780                        TR_TGDCTRL);
1781
1782        mutex_unlock(&hellcreek->reg_lock);
1783
1784        return 0;
1785}
1786
1787static bool hellcreek_validate_schedule(struct hellcreek *hellcreek,
1788                                        struct tc_taprio_qopt_offload *schedule)
1789{
1790        size_t i;
1791
1792        /* Does this hellcreek version support Qbv in hardware? */
1793        if (!hellcreek->pdata->qbv_support)
1794                return false;
1795
1796        /* cycle time can only be 32bit */
1797        if (schedule->cycle_time > (u32)-1)
1798                return false;
1799
1800        /* cycle time extension is not supported */
1801        if (schedule->cycle_time_extension)
1802                return false;
1803
1804        /* Only set command is supported */
1805        for (i = 0; i < schedule->num_entries; ++i)
1806                if (schedule->entries[i].command != TC_TAPRIO_CMD_SET_GATES)
1807                        return false;
1808
1809        return true;
1810}
1811
1812static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port,
1813                                   enum tc_setup_type type, void *type_data)
1814{
1815        struct tc_taprio_qopt_offload *taprio = type_data;
1816        struct hellcreek *hellcreek = ds->priv;
1817
1818        if (type != TC_SETUP_QDISC_TAPRIO)
1819                return -EOPNOTSUPP;
1820
1821        if (!hellcreek_validate_schedule(hellcreek, taprio))
1822                return -EOPNOTSUPP;
1823
1824        if (taprio->enable)
1825                return hellcreek_port_set_schedule(ds, port, taprio);
1826
1827        return hellcreek_port_del_schedule(ds, port);
1828}
1829
1830static const struct dsa_switch_ops hellcreek_ds_ops = {
1831        .devlink_info_get      = hellcreek_devlink_info_get,
1832        .get_ethtool_stats     = hellcreek_get_ethtool_stats,
1833        .get_sset_count        = hellcreek_get_sset_count,
1834        .get_strings           = hellcreek_get_strings,
1835        .get_tag_protocol      = hellcreek_get_tag_protocol,
1836        .get_ts_info           = hellcreek_get_ts_info,
1837        .phylink_get_caps      = hellcreek_phylink_get_caps,
1838        .port_bridge_flags     = hellcreek_bridge_flags,
1839        .port_bridge_join      = hellcreek_port_bridge_join,
1840        .port_bridge_leave     = hellcreek_port_bridge_leave,
1841        .port_disable          = hellcreek_port_disable,
1842        .port_enable           = hellcreek_port_enable,
1843        .port_fdb_add          = hellcreek_fdb_add,
1844        .port_fdb_del          = hellcreek_fdb_del,
1845        .port_fdb_dump         = hellcreek_fdb_dump,
1846        .port_hwtstamp_set     = hellcreek_port_hwtstamp_set,
1847        .port_hwtstamp_get     = hellcreek_port_hwtstamp_get,
1848        .port_pre_bridge_flags = hellcreek_pre_bridge_flags,
1849        .port_prechangeupper   = hellcreek_port_prechangeupper,
1850        .port_rxtstamp         = hellcreek_port_rxtstamp,
1851        .port_setup_tc         = hellcreek_port_setup_tc,
1852        .port_stp_state_set    = hellcreek_port_stp_state_set,
1853        .port_txtstamp         = hellcreek_port_txtstamp,
1854        .port_vlan_add         = hellcreek_vlan_add,
1855        .port_vlan_del         = hellcreek_vlan_del,
1856        .port_vlan_filtering   = hellcreek_vlan_filtering,
1857        .setup                 = hellcreek_setup,
1858        .teardown              = hellcreek_teardown,
1859};
1860
1861static int hellcreek_probe(struct platform_device *pdev)
1862{
1863        struct device *dev = &pdev->dev;
1864        struct hellcreek *hellcreek;
1865        struct resource *res;
1866        int ret, i;
1867
1868        hellcreek = devm_kzalloc(dev, sizeof(*hellcreek), GFP_KERNEL);
1869        if (!hellcreek)
1870                return -ENOMEM;
1871
1872        hellcreek->vidmbrcfg = devm_kcalloc(dev, VLAN_N_VID,
1873                                            sizeof(*hellcreek->vidmbrcfg),
1874                                            GFP_KERNEL);
1875        if (!hellcreek->vidmbrcfg)
1876                return -ENOMEM;
1877
1878        hellcreek->pdata = of_device_get_match_data(dev);
1879
1880        hellcreek->ports = devm_kcalloc(dev, hellcreek->pdata->num_ports,
1881                                        sizeof(*hellcreek->ports),
1882                                        GFP_KERNEL);
1883        if (!hellcreek->ports)
1884                return -ENOMEM;
1885
1886        for (i = 0; i < hellcreek->pdata->num_ports; ++i) {
1887                struct hellcreek_port *port = &hellcreek->ports[i];
1888
1889                port->counter_values =
1890                        devm_kcalloc(dev,
1891                                     ARRAY_SIZE(hellcreek_counter),
1892                                     sizeof(*port->counter_values),
1893                                     GFP_KERNEL);
1894                if (!port->counter_values)
1895                        return -ENOMEM;
1896
1897                port->vlan_dev_bitmap =
1898                        devm_kcalloc(dev,
1899                                     BITS_TO_LONGS(VLAN_N_VID),
1900                                     sizeof(unsigned long),
1901                                     GFP_KERNEL);
1902                if (!port->vlan_dev_bitmap)
1903                        return -ENOMEM;
1904
1905                port->hellcreek = hellcreek;
1906                port->port      = i;
1907
1908                INIT_DELAYED_WORK(&port->schedule_work,
1909                                  hellcreek_check_schedule);
1910        }
1911
1912        mutex_init(&hellcreek->reg_lock);
1913        mutex_init(&hellcreek->vlan_lock);
1914        mutex_init(&hellcreek->ptp_lock);
1915
1916        hellcreek->dev = dev;
1917
1918        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tsn");
1919        if (!res) {
1920                dev_err(dev, "No memory region provided!\n");
1921                return -ENODEV;
1922        }
1923
1924        hellcreek->base = devm_ioremap_resource(dev, res);
1925        if (IS_ERR(hellcreek->base))
1926                return PTR_ERR(hellcreek->base);
1927
1928        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ptp");
1929        if (!res) {
1930                dev_err(dev, "No PTP memory region provided!\n");
1931                return -ENODEV;
1932        }
1933
1934        hellcreek->ptp_base = devm_ioremap_resource(dev, res);
1935        if (IS_ERR(hellcreek->ptp_base))
1936                return PTR_ERR(hellcreek->ptp_base);
1937
1938        ret = hellcreek_detect(hellcreek);
1939        if (ret) {
1940                dev_err(dev, "No (known) chip found!\n");
1941                return ret;
1942        }
1943
1944        ret = hellcreek_wait_until_ready(hellcreek);
1945        if (ret) {
1946                dev_err(dev, "Switch didn't become ready!\n");
1947                return ret;
1948        }
1949
1950        hellcreek_feature_detect(hellcreek);
1951
1952        hellcreek->ds = devm_kzalloc(dev, sizeof(*hellcreek->ds), GFP_KERNEL);
1953        if (!hellcreek->ds)
1954                return -ENOMEM;
1955
1956        hellcreek->ds->dev           = dev;
1957        hellcreek->ds->priv          = hellcreek;
1958        hellcreek->ds->ops           = &hellcreek_ds_ops;
1959        hellcreek->ds->num_ports     = hellcreek->pdata->num_ports;
1960        hellcreek->ds->num_tx_queues = HELLCREEK_NUM_EGRESS_QUEUES;
1961
1962        ret = dsa_register_switch(hellcreek->ds);
1963        if (ret) {
1964                dev_err_probe(dev, ret, "Unable to register switch\n");
1965                return ret;
1966        }
1967
1968        ret = hellcreek_ptp_setup(hellcreek);
1969        if (ret) {
1970                dev_err(dev, "Failed to setup PTP!\n");
1971                goto err_ptp_setup;
1972        }
1973
1974        ret = hellcreek_hwtstamp_setup(hellcreek);
1975        if (ret) {
1976                dev_err(dev, "Failed to setup hardware timestamping!\n");
1977                goto err_tstamp_setup;
1978        }
1979
1980        platform_set_drvdata(pdev, hellcreek);
1981
1982        return 0;
1983
1984err_tstamp_setup:
1985        hellcreek_ptp_free(hellcreek);
1986err_ptp_setup:
1987        dsa_unregister_switch(hellcreek->ds);
1988
1989        return ret;
1990}
1991
1992static int hellcreek_remove(struct platform_device *pdev)
1993{
1994        struct hellcreek *hellcreek = platform_get_drvdata(pdev);
1995
1996        if (!hellcreek)
1997                return 0;
1998
1999        hellcreek_hwtstamp_free(hellcreek);
2000        hellcreek_ptp_free(hellcreek);
2001        dsa_unregister_switch(hellcreek->ds);
2002        platform_set_drvdata(pdev, NULL);
2003
2004        return 0;
2005}
2006
2007static void hellcreek_shutdown(struct platform_device *pdev)
2008{
2009        struct hellcreek *hellcreek = platform_get_drvdata(pdev);
2010
2011        if (!hellcreek)
2012                return;
2013
2014        dsa_switch_shutdown(hellcreek->ds);
2015
2016        platform_set_drvdata(pdev, NULL);
2017}
2018
2019static const struct hellcreek_platform_data de1soc_r1_pdata = {
2020        .name            = "r4c30",
2021        .num_ports       = 4,
2022        .is_100_mbits    = 1,
2023        .qbv_support     = 1,
2024        .qbv_on_cpu_port = 1,
2025        .qbu_support     = 0,
2026        .module_id       = 0x4c30,
2027};
2028
2029static const struct of_device_id hellcreek_of_match[] = {
2030        {
2031                .compatible = "hirschmann,hellcreek-de1soc-r1",
2032                .data       = &de1soc_r1_pdata,
2033        },
2034        { /* sentinel */ },
2035};
2036MODULE_DEVICE_TABLE(of, hellcreek_of_match);
2037
2038static struct platform_driver hellcreek_driver = {
2039        .probe  = hellcreek_probe,
2040        .remove = hellcreek_remove,
2041        .shutdown = hellcreek_shutdown,
2042        .driver = {
2043                .name = "hellcreek",
2044                .of_match_table = hellcreek_of_match,
2045        },
2046};
2047module_platform_driver(hellcreek_driver);
2048
2049MODULE_AUTHOR("Kurt Kanzenbach <kurt@linutronix.de>");
2050MODULE_DESCRIPTION("Hirschmann Hellcreek driver");
2051MODULE_LICENSE("Dual MIT/GPL");
2052