Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* Realtek SMI library helpers for the RTL8366x variants
0003  * RTL8366RB and RTL8366S
0004  *
0005  * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
0006  * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
0007  * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
0008  * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv>
0009  * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com>
0010  */
0011 #include <linux/if_bridge.h>
0012 #include <net/dsa.h>
0013 
0014 #include "realtek.h"
0015 
0016 int rtl8366_mc_is_used(struct realtek_priv *priv, int mc_index, int *used)
0017 {
0018     int ret;
0019     int i;
0020 
0021     *used = 0;
0022     for (i = 0; i < priv->num_ports; i++) {
0023         int index = 0;
0024 
0025         ret = priv->ops->get_mc_index(priv, i, &index);
0026         if (ret)
0027             return ret;
0028 
0029         if (mc_index == index) {
0030             *used = 1;
0031             break;
0032         }
0033     }
0034 
0035     return 0;
0036 }
0037 EXPORT_SYMBOL_GPL(rtl8366_mc_is_used);
0038 
0039 /**
0040  * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration
0041  * @priv: the Realtek SMI device instance
0042  * @vid: the VLAN ID to look up or allocate
0043  * @vlanmc: the pointer will be assigned to a pointer to a valid member config
0044  * if successful
0045  * @return: index of a new member config or negative error number
0046  */
0047 static int rtl8366_obtain_mc(struct realtek_priv *priv, int vid,
0048                  struct rtl8366_vlan_mc *vlanmc)
0049 {
0050     struct rtl8366_vlan_4k vlan4k;
0051     int ret;
0052     int i;
0053 
0054     /* Try to find an existing member config entry for this VID */
0055     for (i = 0; i < priv->num_vlan_mc; i++) {
0056         ret = priv->ops->get_vlan_mc(priv, i, vlanmc);
0057         if (ret) {
0058             dev_err(priv->dev, "error searching for VLAN MC %d for VID %d\n",
0059                 i, vid);
0060             return ret;
0061         }
0062 
0063         if (vid == vlanmc->vid)
0064             return i;
0065     }
0066 
0067     /* We have no MC entry for this VID, try to find an empty one */
0068     for (i = 0; i < priv->num_vlan_mc; i++) {
0069         ret = priv->ops->get_vlan_mc(priv, i, vlanmc);
0070         if (ret) {
0071             dev_err(priv->dev, "error searching for VLAN MC %d for VID %d\n",
0072                 i, vid);
0073             return ret;
0074         }
0075 
0076         if (vlanmc->vid == 0 && vlanmc->member == 0) {
0077             /* Update the entry from the 4K table */
0078             ret = priv->ops->get_vlan_4k(priv, vid, &vlan4k);
0079             if (ret) {
0080                 dev_err(priv->dev, "error looking for 4K VLAN MC %d for VID %d\n",
0081                     i, vid);
0082                 return ret;
0083             }
0084 
0085             vlanmc->vid = vid;
0086             vlanmc->member = vlan4k.member;
0087             vlanmc->untag = vlan4k.untag;
0088             vlanmc->fid = vlan4k.fid;
0089             ret = priv->ops->set_vlan_mc(priv, i, vlanmc);
0090             if (ret) {
0091                 dev_err(priv->dev, "unable to set/update VLAN MC %d for VID %d\n",
0092                     i, vid);
0093                 return ret;
0094             }
0095 
0096             dev_dbg(priv->dev, "created new MC at index %d for VID %d\n",
0097                 i, vid);
0098             return i;
0099         }
0100     }
0101 
0102     /* MC table is full, try to find an unused entry and replace it */
0103     for (i = 0; i < priv->num_vlan_mc; i++) {
0104         int used;
0105 
0106         ret = rtl8366_mc_is_used(priv, i, &used);
0107         if (ret)
0108             return ret;
0109 
0110         if (!used) {
0111             /* Update the entry from the 4K table */
0112             ret = priv->ops->get_vlan_4k(priv, vid, &vlan4k);
0113             if (ret)
0114                 return ret;
0115 
0116             vlanmc->vid = vid;
0117             vlanmc->member = vlan4k.member;
0118             vlanmc->untag = vlan4k.untag;
0119             vlanmc->fid = vlan4k.fid;
0120             ret = priv->ops->set_vlan_mc(priv, i, vlanmc);
0121             if (ret) {
0122                 dev_err(priv->dev, "unable to set/update VLAN MC %d for VID %d\n",
0123                     i, vid);
0124                 return ret;
0125             }
0126             dev_dbg(priv->dev, "recycled MC at index %i for VID %d\n",
0127                 i, vid);
0128             return i;
0129         }
0130     }
0131 
0132     dev_err(priv->dev, "all VLAN member configurations are in use\n");
0133     return -ENOSPC;
0134 }
0135 
0136 int rtl8366_set_vlan(struct realtek_priv *priv, int vid, u32 member,
0137              u32 untag, u32 fid)
0138 {
0139     struct rtl8366_vlan_mc vlanmc;
0140     struct rtl8366_vlan_4k vlan4k;
0141     int mc;
0142     int ret;
0143 
0144     if (!priv->ops->is_vlan_valid(priv, vid))
0145         return -EINVAL;
0146 
0147     dev_dbg(priv->dev,
0148         "setting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n",
0149         vid, member, untag);
0150 
0151     /* Update the 4K table */
0152     ret = priv->ops->get_vlan_4k(priv, vid, &vlan4k);
0153     if (ret)
0154         return ret;
0155 
0156     vlan4k.member |= member;
0157     vlan4k.untag |= untag;
0158     vlan4k.fid = fid;
0159     ret = priv->ops->set_vlan_4k(priv, &vlan4k);
0160     if (ret)
0161         return ret;
0162 
0163     dev_dbg(priv->dev,
0164         "resulting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n",
0165         vid, vlan4k.member, vlan4k.untag);
0166 
0167     /* Find or allocate a member config for this VID */
0168     ret = rtl8366_obtain_mc(priv, vid, &vlanmc);
0169     if (ret < 0)
0170         return ret;
0171     mc = ret;
0172 
0173     /* Update the MC entry */
0174     vlanmc.member |= member;
0175     vlanmc.untag |= untag;
0176     vlanmc.fid = fid;
0177 
0178     /* Commit updates to the MC entry */
0179     ret = priv->ops->set_vlan_mc(priv, mc, &vlanmc);
0180     if (ret)
0181         dev_err(priv->dev, "failed to commit changes to VLAN MC index %d for VID %d\n",
0182             mc, vid);
0183     else
0184         dev_dbg(priv->dev,
0185             "resulting VLAN%d MC members: 0x%02x, untagged: 0x%02x\n",
0186             vid, vlanmc.member, vlanmc.untag);
0187 
0188     return ret;
0189 }
0190 EXPORT_SYMBOL_GPL(rtl8366_set_vlan);
0191 
0192 int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port,
0193              unsigned int vid)
0194 {
0195     struct rtl8366_vlan_mc vlanmc;
0196     int mc;
0197     int ret;
0198 
0199     if (!priv->ops->is_vlan_valid(priv, vid))
0200         return -EINVAL;
0201 
0202     /* Find or allocate a member config for this VID */
0203     ret = rtl8366_obtain_mc(priv, vid, &vlanmc);
0204     if (ret < 0)
0205         return ret;
0206     mc = ret;
0207 
0208     ret = priv->ops->set_mc_index(priv, port, mc);
0209     if (ret) {
0210         dev_err(priv->dev, "set PVID: failed to set MC index %d for port %d\n",
0211             mc, port);
0212         return ret;
0213     }
0214 
0215     dev_dbg(priv->dev, "set PVID: the PVID for port %d set to %d using existing MC index %d\n",
0216         port, vid, mc);
0217 
0218     return 0;
0219 }
0220 EXPORT_SYMBOL_GPL(rtl8366_set_pvid);
0221 
0222 int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable)
0223 {
0224     int ret;
0225 
0226     /* To enable 4k VLAN, ordinary VLAN must be enabled first,
0227      * but if we disable 4k VLAN it is fine to leave ordinary
0228      * VLAN enabled.
0229      */
0230     if (enable) {
0231         /* Make sure VLAN is ON */
0232         ret = priv->ops->enable_vlan(priv, true);
0233         if (ret)
0234             return ret;
0235 
0236         priv->vlan_enabled = true;
0237     }
0238 
0239     ret = priv->ops->enable_vlan4k(priv, enable);
0240     if (ret)
0241         return ret;
0242 
0243     priv->vlan4k_enabled = enable;
0244     return 0;
0245 }
0246 EXPORT_SYMBOL_GPL(rtl8366_enable_vlan4k);
0247 
0248 int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable)
0249 {
0250     int ret;
0251 
0252     ret = priv->ops->enable_vlan(priv, enable);
0253     if (ret)
0254         return ret;
0255 
0256     priv->vlan_enabled = enable;
0257 
0258     /* If we turn VLAN off, make sure that we turn off
0259      * 4k VLAN as well, if that happened to be on.
0260      */
0261     if (!enable) {
0262         priv->vlan4k_enabled = false;
0263         ret = priv->ops->enable_vlan4k(priv, false);
0264     }
0265 
0266     return ret;
0267 }
0268 EXPORT_SYMBOL_GPL(rtl8366_enable_vlan);
0269 
0270 int rtl8366_reset_vlan(struct realtek_priv *priv)
0271 {
0272     struct rtl8366_vlan_mc vlanmc;
0273     int ret;
0274     int i;
0275 
0276     rtl8366_enable_vlan(priv, false);
0277     rtl8366_enable_vlan4k(priv, false);
0278 
0279     /* Clear the 16 VLAN member configurations */
0280     vlanmc.vid = 0;
0281     vlanmc.priority = 0;
0282     vlanmc.member = 0;
0283     vlanmc.untag = 0;
0284     vlanmc.fid = 0;
0285     for (i = 0; i < priv->num_vlan_mc; i++) {
0286         ret = priv->ops->set_vlan_mc(priv, i, &vlanmc);
0287         if (ret)
0288             return ret;
0289     }
0290 
0291     return 0;
0292 }
0293 EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
0294 
0295 int rtl8366_vlan_add(struct dsa_switch *ds, int port,
0296              const struct switchdev_obj_port_vlan *vlan,
0297              struct netlink_ext_ack *extack)
0298 {
0299     bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
0300     bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID);
0301     struct realtek_priv *priv = ds->priv;
0302     u32 member = 0;
0303     u32 untag = 0;
0304     int ret;
0305 
0306     if (!priv->ops->is_vlan_valid(priv, vlan->vid)) {
0307         NL_SET_ERR_MSG_MOD(extack, "VLAN ID not valid");
0308         return -EINVAL;
0309     }
0310 
0311     /* Enable VLAN in the hardware
0312      * FIXME: what's with this 4k business?
0313      * Just rtl8366_enable_vlan() seems inconclusive.
0314      */
0315     ret = rtl8366_enable_vlan4k(priv, true);
0316     if (ret) {
0317         NL_SET_ERR_MSG_MOD(extack, "Failed to enable VLAN 4K");
0318         return ret;
0319     }
0320 
0321     dev_dbg(priv->dev, "add VLAN %d on port %d, %s, %s\n",
0322         vlan->vid, port, untagged ? "untagged" : "tagged",
0323         pvid ? "PVID" : "no PVID");
0324 
0325     member |= BIT(port);
0326 
0327     if (untagged)
0328         untag |= BIT(port);
0329 
0330     ret = rtl8366_set_vlan(priv, vlan->vid, member, untag, 0);
0331     if (ret) {
0332         dev_err(priv->dev, "failed to set up VLAN %04x", vlan->vid);
0333         return ret;
0334     }
0335 
0336     if (!pvid)
0337         return 0;
0338 
0339     ret = rtl8366_set_pvid(priv, port, vlan->vid);
0340     if (ret) {
0341         dev_err(priv->dev, "failed to set PVID on port %d to VLAN %04x",
0342             port, vlan->vid);
0343         return ret;
0344     }
0345 
0346     return 0;
0347 }
0348 EXPORT_SYMBOL_GPL(rtl8366_vlan_add);
0349 
0350 int rtl8366_vlan_del(struct dsa_switch *ds, int port,
0351              const struct switchdev_obj_port_vlan *vlan)
0352 {
0353     struct realtek_priv *priv = ds->priv;
0354     int ret, i;
0355 
0356     dev_dbg(priv->dev, "del VLAN %d on port %d\n", vlan->vid, port);
0357 
0358     for (i = 0; i < priv->num_vlan_mc; i++) {
0359         struct rtl8366_vlan_mc vlanmc;
0360 
0361         ret = priv->ops->get_vlan_mc(priv, i, &vlanmc);
0362         if (ret)
0363             return ret;
0364 
0365         if (vlan->vid == vlanmc.vid) {
0366             /* Remove this port from the VLAN */
0367             vlanmc.member &= ~BIT(port);
0368             vlanmc.untag &= ~BIT(port);
0369             /*
0370              * If no ports are members of this VLAN
0371              * anymore then clear the whole member
0372              * config so it can be reused.
0373              */
0374             if (!vlanmc.member) {
0375                 vlanmc.vid = 0;
0376                 vlanmc.priority = 0;
0377                 vlanmc.fid = 0;
0378             }
0379             ret = priv->ops->set_vlan_mc(priv, i, &vlanmc);
0380             if (ret) {
0381                 dev_err(priv->dev,
0382                     "failed to remove VLAN %04x\n",
0383                     vlan->vid);
0384                 return ret;
0385             }
0386             break;
0387         }
0388     }
0389 
0390     return 0;
0391 }
0392 EXPORT_SYMBOL_GPL(rtl8366_vlan_del);
0393 
0394 void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset,
0395              uint8_t *data)
0396 {
0397     struct realtek_priv *priv = ds->priv;
0398     struct rtl8366_mib_counter *mib;
0399     int i;
0400 
0401     if (port >= priv->num_ports)
0402         return;
0403 
0404     for (i = 0; i < priv->num_mib_counters; i++) {
0405         mib = &priv->mib_counters[i];
0406         strncpy(data + i * ETH_GSTRING_LEN,
0407             mib->name, ETH_GSTRING_LEN);
0408     }
0409 }
0410 EXPORT_SYMBOL_GPL(rtl8366_get_strings);
0411 
0412 int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset)
0413 {
0414     struct realtek_priv *priv = ds->priv;
0415 
0416     /* We only support SS_STATS */
0417     if (sset != ETH_SS_STATS)
0418         return 0;
0419     if (port >= priv->num_ports)
0420         return -EINVAL;
0421 
0422     return priv->num_mib_counters;
0423 }
0424 EXPORT_SYMBOL_GPL(rtl8366_get_sset_count);
0425 
0426 void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
0427 {
0428     struct realtek_priv *priv = ds->priv;
0429     int i;
0430     int ret;
0431 
0432     if (port >= priv->num_ports)
0433         return;
0434 
0435     for (i = 0; i < priv->num_mib_counters; i++) {
0436         struct rtl8366_mib_counter *mib;
0437         u64 mibvalue = 0;
0438 
0439         mib = &priv->mib_counters[i];
0440         ret = priv->ops->get_mib_counter(priv, port, mib, &mibvalue);
0441         if (ret) {
0442             dev_err(priv->dev, "error reading MIB counter %s\n",
0443                 mib->name);
0444         }
0445         data[i] = mibvalue;
0446     }
0447 }
0448 EXPORT_SYMBOL_GPL(rtl8366_get_ethtool_stats);