brintos

brintos / linux-shallow public Read only

0
0
Text · 10.3 KiB · 7c6520b Raw
445 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Realtek SMI library helpers for the RTL8366x variants3 * RTL8366RB and RTL8366S4 *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_NS_GPL(rtl8366_mc_is_used, REALTEK_DSA);38 39/**40 * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration41 * @priv: the Realtek SMI device instance42 * @vid: the VLAN ID to look up or allocate43 * @vlanmc: the pointer will be assigned to a pointer to a valid member config44 * if successful45 * @return: index of a new member config or negative error number46 */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	else184		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_NS_GPL(rtl8366_set_vlan, REALTEK_DSA);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_NS_GPL(rtl8366_set_pvid, REALTEK_DSA);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 ordinary228	 * 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_NS_GPL(rtl8366_enable_vlan4k, REALTEK_DSA);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 off259	 * 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_NS_GPL(rtl8366_enable_vlan, REALTEK_DSA);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_NS_GPL(rtl8366_reset_vlan, REALTEK_DSA);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 hardware312	 * 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_NS_GPL(rtl8366_vlan_add, REALTEK_DSA);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 VLAN371			 * anymore then clear the whole member372			 * 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_NS_GPL(rtl8366_vlan_del, REALTEK_DSA);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	int i;399 400	if (port >= priv->num_ports)401		return;402 403	for (i = 0; i < priv->num_mib_counters; i++)404		ethtool_puts(&data, priv->mib_counters[i].name);405}406EXPORT_SYMBOL_NS_GPL(rtl8366_get_strings, REALTEK_DSA);407 408int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset)409{410	struct realtek_priv *priv = ds->priv;411 412	/* We only support SS_STATS */413	if (sset != ETH_SS_STATS)414		return 0;415	if (port >= priv->num_ports)416		return -EINVAL;417 418	return priv->num_mib_counters;419}420EXPORT_SYMBOL_NS_GPL(rtl8366_get_sset_count, REALTEK_DSA);421 422void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)423{424	struct realtek_priv *priv = ds->priv;425	int i;426	int ret;427 428	if (port >= priv->num_ports)429		return;430 431	for (i = 0; i < priv->num_mib_counters; i++) {432		struct rtl8366_mib_counter *mib;433		u64 mibvalue = 0;434 435		mib = &priv->mib_counters[i];436		ret = priv->ops->get_mib_counter(priv, port, mib, &mibvalue);437		if (ret) {438			dev_err(priv->dev, "error reading MIB counter %s\n",439				mib->name);440		}441		data[i] = mibvalue;442	}443}444EXPORT_SYMBOL_NS_GPL(rtl8366_get_ethtool_stats, REALTEK_DSA);445