linux/drivers/net/dsa/rtl8366.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Realtek SMI library helpers for the RTL8366x variants
   3 * RTL8366RB and RTL8366S
   4 *
   5 * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
   6 * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
   7 * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
   8 * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv>
   9 * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com>
  10 */
  11#include <linux/if_bridge.h>
  12#include <net/dsa.h>
  13
  14#include "realtek-smi-core.h"
  15
  16int rtl8366_mc_is_used(struct realtek_smi *smi, int mc_index, int *used)
  17{
  18        int ret;
  19        int i;
  20
  21        *used = 0;
  22        for (i = 0; i < smi->num_ports; i++) {
  23                int index = 0;
  24
  25                ret = smi->ops->get_mc_index(smi, i, &index);
  26                if (ret)
  27                        return ret;
  28
  29                if (mc_index == index) {
  30                        *used = 1;
  31                        break;
  32                }
  33        }
  34
  35        return 0;
  36}
  37EXPORT_SYMBOL_GPL(rtl8366_mc_is_used);
  38
  39/**
  40 * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration
  41 * @smi: the Realtek SMI device instance
  42 * @vid: the VLAN ID to look up or allocate
  43 * @vlanmc: the pointer will be assigned to a pointer to a valid member config
  44 * if successful
  45 * @return: index of a new member config or negative error number
  46 */
  47static int rtl8366_obtain_mc(struct realtek_smi *smi, int vid,
  48                             struct rtl8366_vlan_mc *vlanmc)
  49{
  50        struct rtl8366_vlan_4k vlan4k;
  51        int ret;
  52        int i;
  53
  54        /* Try to find an existing member config entry for this VID */
  55        for (i = 0; i < smi->num_vlan_mc; i++) {
  56                ret = smi->ops->get_vlan_mc(smi, i, vlanmc);
  57                if (ret) {
  58                        dev_err(smi->dev, "error searching for VLAN MC %d for VID %d\n",
  59                                i, vid);
  60                        return ret;
  61                }
  62
  63                if (vid == vlanmc->vid)
  64                        return i;
  65        }
  66
  67        /* We have no MC entry for this VID, try to find an empty one */
  68        for (i = 0; i < smi->num_vlan_mc; i++) {
  69                ret = smi->ops->get_vlan_mc(smi, i, vlanmc);
  70                if (ret) {
  71                        dev_err(smi->dev, "error searching for VLAN MC %d for VID %d\n",
  72                                i, vid);
  73                        return ret;
  74                }
  75
  76                if (vlanmc->vid == 0 && vlanmc->member == 0) {
  77                        /* Update the entry from the 4K table */
  78                        ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
  79                        if (ret) {
  80                                dev_err(smi->dev, "error looking for 4K VLAN MC %d for VID %d\n",
  81                                        i, vid);
  82                                return ret;
  83                        }
  84
  85                        vlanmc->vid = vid;
  86                        vlanmc->member = vlan4k.member;
  87                        vlanmc->untag = vlan4k.untag;
  88                        vlanmc->fid = vlan4k.fid;
  89                        ret = smi->ops->set_vlan_mc(smi, i, vlanmc);
  90                        if (ret) {
  91                                dev_err(smi->dev, "unable to set/update VLAN MC %d for VID %d\n",
  92                                        i, vid);
  93                                return ret;
  94                        }
  95
  96                        dev_dbg(smi->dev, "created new MC at index %d for VID %d\n",
  97                                i, vid);
  98                        return i;
  99                }
 100        }
 101
 102        /* MC table is full, try to find an unused entry and replace it */
 103        for (i = 0; i < smi->num_vlan_mc; i++) {
 104                int used;
 105
 106                ret = rtl8366_mc_is_used(smi, i, &used);
 107                if (ret)
 108                        return ret;
 109
 110                if (!used) {
 111                        /* Update the entry from the 4K table */
 112                        ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
 113                        if (ret)
 114                                return ret;
 115
 116                        vlanmc->vid = vid;
 117                        vlanmc->member = vlan4k.member;
 118                        vlanmc->untag = vlan4k.untag;
 119                        vlanmc->fid = vlan4k.fid;
 120                        ret = smi->ops->set_vlan_mc(smi, i, vlanmc);
 121                        if (ret) {
 122                                dev_err(smi->dev, "unable to set/update VLAN MC %d for VID %d\n",
 123                                        i, vid);
 124                                return ret;
 125                        }
 126                        dev_dbg(smi->dev, "recycled MC at index %i for VID %d\n",
 127                                i, vid);
 128                        return i;
 129                }
 130        }
 131
 132        dev_err(smi->dev, "all VLAN member configurations are in use\n");
 133        return -ENOSPC;
 134}
 135
 136int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member,
 137                     u32 untag, u32 fid)
 138{
 139        struct rtl8366_vlan_mc vlanmc;
 140        struct rtl8366_vlan_4k vlan4k;
 141        int mc;
 142        int ret;
 143
 144        if (!smi->ops->is_vlan_valid(smi, vid))
 145                return -EINVAL;
 146
 147        dev_dbg(smi->dev,
 148                "setting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n",
 149                vid, member, untag);
 150
 151        /* Update the 4K table */
 152        ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
 153        if (ret)
 154                return ret;
 155
 156        vlan4k.member |= member;
 157        vlan4k.untag |= untag;
 158        vlan4k.fid = fid;
 159        ret = smi->ops->set_vlan_4k(smi, &vlan4k);
 160        if (ret)
 161                return ret;
 162
 163        dev_dbg(smi->dev,
 164                "resulting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n",
 165                vid, vlan4k.member, vlan4k.untag);
 166
 167        /* Find or allocate a member config for this VID */
 168        ret = rtl8366_obtain_mc(smi, vid, &vlanmc);
 169        if (ret < 0)
 170                return ret;
 171        mc = ret;
 172
 173        /* Update the MC entry */
 174        vlanmc.member |= member;
 175        vlanmc.untag |= untag;
 176        vlanmc.fid = fid;
 177
 178        /* Commit updates to the MC entry */
 179        ret = smi->ops->set_vlan_mc(smi, mc, &vlanmc);
 180        if (ret)
 181                dev_err(smi->dev, "failed to commit changes to VLAN MC index %d for VID %d\n",
 182                        mc, vid);
 183        else
 184                dev_dbg(smi->dev,
 185                        "resulting VLAN%d MC members: 0x%02x, untagged: 0x%02x\n",
 186                        vid, vlanmc.member, vlanmc.untag);
 187
 188        return ret;
 189}
 190EXPORT_SYMBOL_GPL(rtl8366_set_vlan);
 191
 192int rtl8366_set_pvid(struct realtek_smi *smi, unsigned int port,
 193                     unsigned int vid)
 194{
 195        struct rtl8366_vlan_mc vlanmc;
 196        int mc;
 197        int ret;
 198
 199        if (!smi->ops->is_vlan_valid(smi, vid))
 200                return -EINVAL;
 201
 202        /* Find or allocate a member config for this VID */
 203        ret = rtl8366_obtain_mc(smi, vid, &vlanmc);
 204        if (ret < 0)
 205                return ret;
 206        mc = ret;
 207
 208        ret = smi->ops->set_mc_index(smi, port, mc);
 209        if (ret) {
 210                dev_err(smi->dev, "set PVID: failed to set MC index %d for port %d\n",
 211                        mc, port);
 212                return ret;
 213        }
 214
 215        dev_dbg(smi->dev, "set PVID: the PVID for port %d set to %d using existing MC index %d\n",
 216                port, vid, mc);
 217
 218        return 0;
 219}
 220EXPORT_SYMBOL_GPL(rtl8366_set_pvid);
 221
 222int rtl8366_enable_vlan4k(struct realtek_smi *smi, bool enable)
 223{
 224        int ret;
 225
 226        /* To enable 4k VLAN, ordinary VLAN must be enabled first,
 227         * but if we disable 4k VLAN it is fine to leave ordinary
 228         * VLAN enabled.
 229         */
 230        if (enable) {
 231                /* Make sure VLAN is ON */
 232                ret = smi->ops->enable_vlan(smi, true);
 233                if (ret)
 234                        return ret;
 235
 236                smi->vlan_enabled = true;
 237        }
 238
 239        ret = smi->ops->enable_vlan4k(smi, enable);
 240        if (ret)
 241                return ret;
 242
 243        smi->vlan4k_enabled = enable;
 244        return 0;
 245}
 246EXPORT_SYMBOL_GPL(rtl8366_enable_vlan4k);
 247
 248int rtl8366_enable_vlan(struct realtek_smi *smi, bool enable)
 249{
 250        int ret;
 251
 252        ret = smi->ops->enable_vlan(smi, enable);
 253        if (ret)
 254                return ret;
 255
 256        smi->vlan_enabled = enable;
 257
 258        /* If we turn VLAN off, make sure that we turn off
 259         * 4k VLAN as well, if that happened to be on.
 260         */
 261        if (!enable) {
 262                smi->vlan4k_enabled = false;
 263                ret = smi->ops->enable_vlan4k(smi, false);
 264        }
 265
 266        return ret;
 267}
 268EXPORT_SYMBOL_GPL(rtl8366_enable_vlan);
 269
 270int rtl8366_reset_vlan(struct realtek_smi *smi)
 271{
 272        struct rtl8366_vlan_mc vlanmc;
 273        int ret;
 274        int i;
 275
 276        rtl8366_enable_vlan(smi, false);
 277        rtl8366_enable_vlan4k(smi, false);
 278
 279        /* Clear the 16 VLAN member configurations */
 280        vlanmc.vid = 0;
 281        vlanmc.priority = 0;
 282        vlanmc.member = 0;
 283        vlanmc.untag = 0;
 284        vlanmc.fid = 0;
 285        for (i = 0; i < smi->num_vlan_mc; i++) {
 286                ret = smi->ops->set_vlan_mc(smi, i, &vlanmc);
 287                if (ret)
 288                        return ret;
 289        }
 290
 291        return 0;
 292}
 293EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
 294
 295int rtl8366_init_vlan(struct realtek_smi *smi)
 296{
 297        int port;
 298        int ret;
 299
 300        ret = rtl8366_reset_vlan(smi);
 301        if (ret)
 302                return ret;
 303
 304        /* Loop over the available ports, for each port, associate
 305         * it with the VLAN (port+1)
 306         */
 307        for (port = 0; port < smi->num_ports; port++) {
 308                u32 mask;
 309
 310                if (port == smi->cpu_port)
 311                        /* For the CPU port, make all ports members of this
 312                         * VLAN.
 313                         */
 314                        mask = GENMASK((int)smi->num_ports - 1, 0);
 315                else
 316                        /* For all other ports, enable itself plus the
 317                         * CPU port.
 318                         */
 319                        mask = BIT(port) | BIT(smi->cpu_port);
 320
 321                /* For each port, set the port as member of VLAN (port+1)
 322                 * and untagged, except for the CPU port: the CPU port (5) is
 323                 * member of VLAN 6 and so are ALL the other ports as well.
 324                 * Use filter 0 (no filter).
 325                 */
 326                dev_info(smi->dev, "VLAN%d port mask for port %d, %08x\n",
 327                         (port + 1), port, mask);
 328                ret = rtl8366_set_vlan(smi, (port + 1), mask, mask, 0);
 329                if (ret)
 330                        return ret;
 331
 332                dev_info(smi->dev, "VLAN%d port %d, PVID set to %d\n",
 333                         (port + 1), port, (port + 1));
 334                ret = rtl8366_set_pvid(smi, port, (port + 1));
 335                if (ret)
 336                        return ret;
 337        }
 338
 339        return rtl8366_enable_vlan(smi, true);
 340}
 341EXPORT_SYMBOL_GPL(rtl8366_init_vlan);
 342
 343int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
 344                           struct switchdev_trans *trans)
 345{
 346        struct realtek_smi *smi = ds->priv;
 347        struct rtl8366_vlan_4k vlan4k;
 348        int ret;
 349
 350        /* Use VLAN nr port + 1 since VLAN0 is not valid */
 351        if (switchdev_trans_ph_prepare(trans)) {
 352                if (!smi->ops->is_vlan_valid(smi, port + 1))
 353                        return -EINVAL;
 354
 355                return 0;
 356        }
 357
 358        dev_info(smi->dev, "%s filtering on port %d\n",
 359                 vlan_filtering ? "enable" : "disable",
 360                 port);
 361
 362        /* TODO:
 363         * The hardware support filter ID (FID) 0..7, I have no clue how to
 364         * support this in the driver when the callback only says on/off.
 365         */
 366        ret = smi->ops->get_vlan_4k(smi, port + 1, &vlan4k);
 367        if (ret)
 368                return ret;
 369
 370        /* Just set the filter to FID 1 for now then */
 371        ret = rtl8366_set_vlan(smi, port + 1,
 372                               vlan4k.member,
 373                               vlan4k.untag,
 374                               1);
 375        if (ret)
 376                return ret;
 377
 378        return 0;
 379}
 380EXPORT_SYMBOL_GPL(rtl8366_vlan_filtering);
 381
 382int rtl8366_vlan_prepare(struct dsa_switch *ds, int port,
 383                         const struct switchdev_obj_port_vlan *vlan)
 384{
 385        struct realtek_smi *smi = ds->priv;
 386        u16 vid;
 387        int ret;
 388
 389        for (vid = vlan->vid_begin; vid < vlan->vid_end; vid++)
 390                if (!smi->ops->is_vlan_valid(smi, vid))
 391                        return -EINVAL;
 392
 393        dev_info(smi->dev, "prepare VLANs %04x..%04x\n",
 394                 vlan->vid_begin, vlan->vid_end);
 395
 396        /* Enable VLAN in the hardware
 397         * FIXME: what's with this 4k business?
 398         * Just rtl8366_enable_vlan() seems inconclusive.
 399         */
 400        ret = rtl8366_enable_vlan4k(smi, true);
 401        if (ret)
 402                return ret;
 403
 404        return 0;
 405}
 406EXPORT_SYMBOL_GPL(rtl8366_vlan_prepare);
 407
 408void rtl8366_vlan_add(struct dsa_switch *ds, int port,
 409                      const struct switchdev_obj_port_vlan *vlan)
 410{
 411        bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
 412        bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID);
 413        struct realtek_smi *smi = ds->priv;
 414        u32 member = 0;
 415        u32 untag = 0;
 416        u16 vid;
 417        int ret;
 418
 419        for (vid = vlan->vid_begin; vid < vlan->vid_end; vid++)
 420                if (!smi->ops->is_vlan_valid(smi, vid))
 421                        return;
 422
 423        dev_info(smi->dev, "add VLAN %d on port %d, %s, %s\n",
 424                 vlan->vid_begin,
 425                 port,
 426                 untagged ? "untagged" : "tagged",
 427                 pvid ? " PVID" : "no PVID");
 428
 429        if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
 430                dev_err(smi->dev, "port is DSA or CPU port\n");
 431
 432        for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
 433                member |= BIT(port);
 434
 435                if (untagged)
 436                        untag |= BIT(port);
 437
 438                ret = rtl8366_set_vlan(smi, vid, member, untag, 0);
 439                if (ret)
 440                        dev_err(smi->dev,
 441                                "failed to set up VLAN %04x",
 442                                vid);
 443
 444                if (!pvid)
 445                        continue;
 446
 447                ret = rtl8366_set_pvid(smi, port, vid);
 448                if (ret)
 449                        dev_err(smi->dev,
 450                                "failed to set PVID on port %d to VLAN %04x",
 451                                port, vid);
 452
 453                if (!ret)
 454                        dev_dbg(smi->dev, "VLAN add: added VLAN %d with PVID on port %d\n",
 455                                vid, port);
 456        }
 457}
 458EXPORT_SYMBOL_GPL(rtl8366_vlan_add);
 459
 460int rtl8366_vlan_del(struct dsa_switch *ds, int port,
 461                     const struct switchdev_obj_port_vlan *vlan)
 462{
 463        struct realtek_smi *smi = ds->priv;
 464        u16 vid;
 465        int ret;
 466
 467        dev_info(smi->dev, "del VLAN on port %d\n", port);
 468
 469        for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
 470                int i;
 471
 472                dev_info(smi->dev, "del VLAN %04x\n", vid);
 473
 474                for (i = 0; i < smi->num_vlan_mc; i++) {
 475                        struct rtl8366_vlan_mc vlanmc;
 476
 477                        ret = smi->ops->get_vlan_mc(smi, i, &vlanmc);
 478                        if (ret)
 479                                return ret;
 480
 481                        if (vid == vlanmc.vid) {
 482                                /* Remove this port from the VLAN */
 483                                vlanmc.member &= ~BIT(port);
 484                                vlanmc.untag &= ~BIT(port);
 485                                /*
 486                                 * If no ports are members of this VLAN
 487                                 * anymore then clear the whole member
 488                                 * config so it can be reused.
 489                                 */
 490                                if (!vlanmc.member && vlanmc.untag) {
 491                                        vlanmc.vid = 0;
 492                                        vlanmc.priority = 0;
 493                                        vlanmc.fid = 0;
 494                                }
 495                                ret = smi->ops->set_vlan_mc(smi, i, &vlanmc);
 496                                if (ret) {
 497                                        dev_err(smi->dev,
 498                                                "failed to remove VLAN %04x\n",
 499                                                vid);
 500                                        return ret;
 501                                }
 502                                break;
 503                        }
 504                }
 505        }
 506
 507        return 0;
 508}
 509EXPORT_SYMBOL_GPL(rtl8366_vlan_del);
 510
 511void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset,
 512                         uint8_t *data)
 513{
 514        struct realtek_smi *smi = ds->priv;
 515        struct rtl8366_mib_counter *mib;
 516        int i;
 517
 518        if (port >= smi->num_ports)
 519                return;
 520
 521        for (i = 0; i < smi->num_mib_counters; i++) {
 522                mib = &smi->mib_counters[i];
 523                strncpy(data + i * ETH_GSTRING_LEN,
 524                        mib->name, ETH_GSTRING_LEN);
 525        }
 526}
 527EXPORT_SYMBOL_GPL(rtl8366_get_strings);
 528
 529int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset)
 530{
 531        struct realtek_smi *smi = ds->priv;
 532
 533        /* We only support SS_STATS */
 534        if (sset != ETH_SS_STATS)
 535                return 0;
 536        if (port >= smi->num_ports)
 537                return -EINVAL;
 538
 539        return smi->num_mib_counters;
 540}
 541EXPORT_SYMBOL_GPL(rtl8366_get_sset_count);
 542
 543void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
 544{
 545        struct realtek_smi *smi = ds->priv;
 546        int i;
 547        int ret;
 548
 549        if (port >= smi->num_ports)
 550                return;
 551
 552        for (i = 0; i < smi->num_mib_counters; i++) {
 553                struct rtl8366_mib_counter *mib;
 554                u64 mibvalue = 0;
 555
 556                mib = &smi->mib_counters[i];
 557                ret = smi->ops->get_mib_counter(smi, port, mib, &mibvalue);
 558                if (ret) {
 559                        dev_err(smi->dev, "error reading MIB counter %s\n",
 560                                mib->name);
 561                }
 562                data[i] = mibvalue;
 563        }
 564}
 565EXPORT_SYMBOL_GPL(rtl8366_get_ethtool_stats);
 566