linux/drivers/net/dsa/realtek/rtl8366-core.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.h"
  15
  16int rtl8366_mc_is_used(struct realtek_priv *priv, int mc_index, int *used)
  17{
  18        int ret;
  19        int i;
  20
  21        *used = 0;
  22        for (i = 0; i < priv->num_ports; i++) {
  23                int index = 0;
  24
  25                ret = priv->ops->get_mc_index(priv, 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 * @priv: 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_priv *priv, 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 < priv->num_vlan_mc; i++) {
  56                ret = priv->ops->get_vlan_mc(priv, i, vlanmc);
  57                if (ret) {
  58                        dev_err(priv->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 < priv->num_vlan_mc; i++) {
  69                ret = priv->ops->get_vlan_mc(priv, i, vlanmc);
  70                if (ret) {
  71                        dev_err(priv->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 = priv->ops->get_vlan_4k(priv, vid, &vlan4k);
  79                        if (ret) {
  80                                dev_err(priv->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 = priv->ops->set_vlan_mc(priv, i, vlanmc);
  90                        if (ret) {
  91                                dev_err(priv->dev, "unable to set/update VLAN MC %d for VID %d\n",
  92                                        i, vid);
  93                                return ret;
  94                        }
  95
  96                        dev_dbg(priv->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 < priv->num_vlan_mc; i++) {
 104                int used;
 105
 106                ret = rtl8366_mc_is_used(priv, i, &used);
 107                if (ret)
 108                        return ret;
 109
 110                if (!used) {
 111                        /* Update the entry from the 4K table */
 112                        ret = priv->ops->get_vlan_4k(priv, 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 = priv->ops->set_vlan_mc(priv, i, vlanmc);
 121                        if (ret) {
 122                                dev_err(priv->dev, "unable to set/update VLAN MC %d for VID %d\n",
 123                                        i, vid);
 124                                return ret;
 125                        }
 126                        dev_dbg(priv->dev, "recycled MC at index %i for VID %d\n",
 127                                i, vid);
 128                        return i;
 129                }
 130        }
 131
 132        dev_err(priv->dev, "all VLAN member configurations are in use\n");
 133        return -ENOSPC;
 134}
 135
 136int rtl8366_set_vlan(struct realtek_priv *priv, 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 (!priv->ops->is_vlan_valid(priv, vid))
 145                return -EINVAL;
 146
 147        dev_dbg(priv->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 = priv->ops->get_vlan_4k(priv, vid, &vlan4k);
 153        if (ret)
 154                return ret;
 155
 156        vlan4k.member |= member;
 157        vlan4k.untag |= untag;
 158        vlan4k.fid = fid;
 159        ret = priv->ops->set_vlan_4k(priv, &vlan4k);
 160        if (ret)
 161                return ret;
 162
 163        dev_dbg(priv->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(priv, 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 = priv->ops->set_vlan_mc(priv, mc, &vlanmc);
 180        if (ret)
 181                dev_err(priv->dev, "failed to commit changes to VLAN MC index %d for VID %d\n",
 182                        mc, vid);
 183        else
 184                dev_dbg(priv->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_priv *priv, unsigned int port,
 193                     unsigned int vid)
 194{
 195        struct rtl8366_vlan_mc vlanmc;
 196        int mc;
 197        int ret;
 198
 199        if (!priv->ops->is_vlan_valid(priv, vid))
 200                return -EINVAL;
 201
 202        /* Find or allocate a member config for this VID */
 203        ret = rtl8366_obtain_mc(priv, vid, &vlanmc);
 204        if (ret < 0)
 205                return ret;
 206        mc = ret;
 207
 208        ret = priv->ops->set_mc_index(priv, port, mc);
 209        if (ret) {
 210                dev_err(priv->dev, "set PVID: failed to set MC index %d for port %d\n",
 211                        mc, port);
 212                return ret;
 213        }
 214
 215        dev_dbg(priv->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_priv *priv, 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 = priv->ops->enable_vlan(priv, true);
 233                if (ret)
 234                        return ret;
 235
 236                priv->vlan_enabled = true;
 237        }
 238
 239        ret = priv->ops->enable_vlan4k(priv, enable);
 240        if (ret)
 241                return ret;
 242
 243        priv->vlan4k_enabled = enable;
 244        return 0;
 245}
 246EXPORT_SYMBOL_GPL(rtl8366_enable_vlan4k);
 247
 248int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable)
 249{
 250        int ret;
 251
 252        ret = priv->ops->enable_vlan(priv, enable);
 253        if (ret)
 254                return ret;
 255
 256        priv->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                priv->vlan4k_enabled = false;
 263                ret = priv->ops->enable_vlan4k(priv, false);
 264        }
 265
 266        return ret;
 267}
 268EXPORT_SYMBOL_GPL(rtl8366_enable_vlan);
 269
 270int rtl8366_reset_vlan(struct realtek_priv *priv)
 271{
 272        struct rtl8366_vlan_mc vlanmc;
 273        int ret;
 274        int i;
 275
 276        rtl8366_enable_vlan(priv, false);
 277        rtl8366_enable_vlan4k(priv, 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 < priv->num_vlan_mc; i++) {
 286                ret = priv->ops->set_vlan_mc(priv, i, &vlanmc);
 287                if (ret)
 288                        return ret;
 289        }
 290
 291        return 0;
 292}
 293EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
 294
 295int rtl8366_vlan_add(struct dsa_switch *ds, int port,
 296                     const struct switchdev_obj_port_vlan *vlan,
 297                     struct netlink_ext_ack *extack)
 298{
 299        bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
 300        bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID);
 301        struct realtek_priv *priv = ds->priv;
 302        u32 member = 0;
 303        u32 untag = 0;
 304        int ret;
 305
 306        if (!priv->ops->is_vlan_valid(priv, vlan->vid)) {
 307                NL_SET_ERR_MSG_MOD(extack, "VLAN ID not valid");
 308                return -EINVAL;
 309        }
 310
 311        /* Enable VLAN in the hardware
 312         * FIXME: what's with this 4k business?
 313         * Just rtl8366_enable_vlan() seems inconclusive.
 314         */
 315        ret = rtl8366_enable_vlan4k(priv, true);
 316        if (ret) {
 317                NL_SET_ERR_MSG_MOD(extack, "Failed to enable VLAN 4K");
 318                return ret;
 319        }
 320
 321        dev_dbg(priv->dev, "add VLAN %d on port %d, %s, %s\n",
 322                vlan->vid, port, untagged ? "untagged" : "tagged",
 323                pvid ? "PVID" : "no PVID");
 324
 325        member |= BIT(port);
 326
 327        if (untagged)
 328                untag |= BIT(port);
 329
 330        ret = rtl8366_set_vlan(priv, vlan->vid, member, untag, 0);
 331        if (ret) {
 332                dev_err(priv->dev, "failed to set up VLAN %04x", vlan->vid);
 333                return ret;
 334        }
 335
 336        if (!pvid)
 337                return 0;
 338
 339        ret = rtl8366_set_pvid(priv, port, vlan->vid);
 340        if (ret) {
 341                dev_err(priv->dev, "failed to set PVID on port %d to VLAN %04x",
 342                        port, vlan->vid);
 343                return ret;
 344        }
 345
 346        return 0;
 347}
 348EXPORT_SYMBOL_GPL(rtl8366_vlan_add);
 349
 350int rtl8366_vlan_del(struct dsa_switch *ds, int port,
 351                     const struct switchdev_obj_port_vlan *vlan)
 352{
 353        struct realtek_priv *priv = ds->priv;
 354        int ret, i;
 355
 356        dev_dbg(priv->dev, "del VLAN %d on port %d\n", vlan->vid, port);
 357
 358        for (i = 0; i < priv->num_vlan_mc; i++) {
 359                struct rtl8366_vlan_mc vlanmc;
 360
 361                ret = priv->ops->get_vlan_mc(priv, i, &vlanmc);
 362                if (ret)
 363                        return ret;
 364
 365                if (vlan->vid == vlanmc.vid) {
 366                        /* Remove this port from the VLAN */
 367                        vlanmc.member &= ~BIT(port);
 368                        vlanmc.untag &= ~BIT(port);
 369                        /*
 370                         * If no ports are members of this VLAN
 371                         * anymore then clear the whole member
 372                         * config so it can be reused.
 373                         */
 374                        if (!vlanmc.member) {
 375                                vlanmc.vid = 0;
 376                                vlanmc.priority = 0;
 377                                vlanmc.fid = 0;
 378                        }
 379                        ret = priv->ops->set_vlan_mc(priv, i, &vlanmc);
 380                        if (ret) {
 381                                dev_err(priv->dev,
 382                                        "failed to remove VLAN %04x\n",
 383                                        vlan->vid);
 384                                return ret;
 385                        }
 386                        break;
 387                }
 388        }
 389
 390        return 0;
 391}
 392EXPORT_SYMBOL_GPL(rtl8366_vlan_del);
 393
 394void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset,
 395                         uint8_t *data)
 396{
 397        struct realtek_priv *priv = ds->priv;
 398        struct rtl8366_mib_counter *mib;
 399        int i;
 400
 401        if (port >= priv->num_ports)
 402                return;
 403
 404        for (i = 0; i < priv->num_mib_counters; i++) {
 405                mib = &priv->mib_counters[i];
 406                strncpy(data + i * ETH_GSTRING_LEN,
 407                        mib->name, ETH_GSTRING_LEN);
 408        }
 409}
 410EXPORT_SYMBOL_GPL(rtl8366_get_strings);
 411
 412int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset)
 413{
 414        struct realtek_priv *priv = ds->priv;
 415
 416        /* We only support SS_STATS */
 417        if (sset != ETH_SS_STATS)
 418                return 0;
 419        if (port >= priv->num_ports)
 420                return -EINVAL;
 421
 422        return priv->num_mib_counters;
 423}
 424EXPORT_SYMBOL_GPL(rtl8366_get_sset_count);
 425
 426void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
 427{
 428        struct realtek_priv *priv = ds->priv;
 429        int i;
 430        int ret;
 431
 432        if (port >= priv->num_ports)
 433                return;
 434
 435        for (i = 0; i < priv->num_mib_counters; i++) {
 436                struct rtl8366_mib_counter *mib;
 437                u64 mibvalue = 0;
 438
 439                mib = &priv->mib_counters[i];
 440                ret = priv->ops->get_mib_counter(priv, port, mib, &mibvalue);
 441                if (ret) {
 442                        dev_err(priv->dev, "error reading MIB counter %s\n",
 443                                mib->name);
 444                }
 445                data[i] = mibvalue;
 446        }
 447}
 448EXPORT_SYMBOL_GPL(rtl8366_get_ethtool_stats);
 449