#include "rtl8365mb_vlan.h"
#include "rtl8365mb_table.h"
#include <linux/if_bridge.h>
#include <linux/lockdep.h>
#include <linux/regmap.h>
#define RTL8365MB_CVLAN_ENTRY_SIZE 3
#define RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK GENMASK(7, 0)
#define RTL8365MB_CVLAN_MBR_LO_MASK GENMASK(7, 0)
#define RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK GENMASK(15, 8)
#define RTL8365MB_CVLAN_UNTAG_LO_MASK GENMASK(7, 0)
#define RTL8365MB_CVLAN_ENTRY_D1_FID_MASK GENMASK(3, 0)
#define RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK GENMASK(4, 4)
#define RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK GENMASK(7, 5)
#define RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK GENMASK(8, 8)
#define RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK GENMASK(13, 9)
#define RTL8365MB_CVLAN_METERIDX_LO_MASK GENMASK(4, 0)
#define RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK GENMASK(14, 14)
#define RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK GENMASK(2, 0)
#define RTL8365MB_CVLAN_MBR_HI_MASK GENMASK(10, 8)
#define RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK GENMASK(5, 3)
#define RTL8365MB_CVLAN_UNTAG_HI_MASK GENMASK(10, 8)
#define RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK GENMASK(6, 6)
#define RTL8365MB_CVLAN_METERIDX_HI_MASK GENMASK(5, 5)
#define RTL8365MB_VLAN_MC_BASE 0x0728
#define RTL8365MB_VLAN_MC_ENTRY_SIZE 4
#define RTL8365MB_VLAN_MC_REG(index) \
(RTL8365MB_VLAN_MC_BASE + \
(RTL8365MB_VLAN_MC_ENTRY_SIZE * (index)))
#define RTL8365MB_VLAN_MC_D0_MBR_MASK GENMASK(10, 0)
#define RTL8365MB_VLAN_MC_D1_FID_MASK GENMASK(3, 0)
#define RTL8365MB_VLAN_MC_D2_VBPEN_MASK GENMASK(0, 0)
#define RTL8365MB_VLAN_MC_D2_VBPRI_MASK GENMASK(3, 1)
#define RTL8365MB_VLAN_MC_D2_ENVLANPOL_MASK GENMASK(4, 4)
#define RTL8365MB_VLAN_MC_D2_METERIDX_MASK GENMASK(10, 5)
#define RTL8365MB_VLAN_MC_D3_EVID_MASK GENMASK(12, 0)
#define RTL8365MB_PRIORITYMAX 7
#define RTL8365MB_FIDMAX 15
#define RTL8365MB_METERMAX 63
#define RTL8365MB_VLAN_MCMAX 31
#define RTL8365MB_MAX_4K_VID 0x0FFF
#define RTL8365MB_MAX_MC_VID 0x1FFF
#define RTL8365MB_VLAN_PVID_CTRL_BASE 0x0700
#define RTL8365MB_VLAN_PVID_CTRL_REG(_p) \
(RTL8365MB_VLAN_PVID_CTRL_BASE + ((_p) >> 1))
#define RTL8365MB_VLAN_PVID_CTRL_PORT0_MCIDX_MASK 0x001F
#define RTL8365MB_VLAN_PVID_CTRL_PORT1_MCIDX_MASK 0x1F00
#define RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(_p) \
(((_p) & 1) << 3)
#define RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(_p) \
(0x1F << RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(_p))
#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_BASE 0x07aa
#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port) \
(RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_BASE + ((port) >> 3))
#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_MASK(port) \
(0x3 << RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port))
#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port) \
(((port) & 0x7) << 1)
struct rtl8365mb_vlan4k {
u16 vid;
u16 member;
u16 untag;
u8 fid : 4;
u8 priority : 3;
u8 priority_en : 1;
u8 policing_en : 1;
u8 ivl_en : 1;
u8 meteridx : 6;
};
struct rtl8365mb_vlanmc {
u16 evid;
u16 member;
u8 fid : 4;
u8 priority : 3;
u8 priority_en : 1;
u8 policing_en : 1;
u8 meteridx : 6;
};
static int rtl8365mb_vlan_4k_read(struct realtek_priv *priv, u16 vid,
struct rtl8365mb_vlan4k *vlan4k)
{
u16 data[RTL8365MB_CVLAN_ENTRY_SIZE];
int val;
int ret;
ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_CVLAN,
RTL8365MB_TABLE_OP_READ, &vid, 0, 0,
data, ARRAY_SIZE(data));
if (ret)
return ret;
memset(vlan4k, 0, sizeof(*vlan4k));
vlan4k->vid = vid;
val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK, data[0]);
vlan4k->member = FIELD_PREP(RTL8365MB_CVLAN_MBR_LO_MASK, val);
val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, data[2]);
vlan4k->member |= FIELD_PREP(RTL8365MB_CVLAN_MBR_HI_MASK, val);
val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK, data[0]);
vlan4k->untag = FIELD_PREP(RTL8365MB_CVLAN_UNTAG_LO_MASK, val);
val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, data[2]);
vlan4k->untag |= FIELD_PREP(RTL8365MB_CVLAN_UNTAG_HI_MASK, val);
vlan4k->fid = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, data[1]);
vlan4k->priority_en =
FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK, data[1]);
vlan4k->priority =
FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK, data[1]);
vlan4k->policing_en =
FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK, data[1]);
val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, data[1]);
val = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_LO_MASK, val);
vlan4k->meteridx = val;
val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, data[2]);
val = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_HI_MASK, val);
vlan4k->meteridx |= val;
vlan4k->ivl_en =
FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK, data[1]);
return 0;
}
static int rtl8365mb_vlan_4k_write(struct realtek_priv *priv,
const struct rtl8365mb_vlan4k *vlan4k)
{
u16 data[RTL8365MB_CVLAN_ENTRY_SIZE] = { 0 };
u16 vid;
int val;
val = FIELD_GET(RTL8365MB_CVLAN_MBR_LO_MASK, vlan4k->member);
data[0] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK, val);
val = FIELD_GET(RTL8365MB_CVLAN_UNTAG_LO_MASK, vlan4k->untag);
data[0] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK, val);
data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, vlan4k->fid);
data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK,
vlan4k->priority_en);
data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK,
vlan4k->priority);
data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK,
vlan4k->policing_en);
val = vlan4k->meteridx;
val = FIELD_GET(RTL8365MB_CVLAN_METERIDX_LO_MASK, val);
data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, val);
data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK,
vlan4k->ivl_en);
val = FIELD_GET(RTL8365MB_CVLAN_MBR_HI_MASK, vlan4k->member);
data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, val);
val = FIELD_GET(RTL8365MB_CVLAN_UNTAG_HI_MASK, vlan4k->untag);
data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, val);
val = vlan4k->meteridx;
val = FIELD_GET(RTL8365MB_CVLAN_METERIDX_HI_MASK, val);
data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, val);
vid = vlan4k->vid;
return rtl8365mb_table_query(priv, RTL8365MB_TABLE_CVLAN,
RTL8365MB_TABLE_OP_WRITE, &vid, 0, 0,
data, ARRAY_SIZE(data));
}
static int
rtl8365mb_vlan_4k_port_set(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct netlink_ext_ack *extack,
bool include)
{
struct realtek_priv *priv = ds->priv;
struct rtl8365mb_vlan4k vlan4k = {0};
int ret;
dev_dbg(priv->dev, "%s VLAN %d 4K on port %d\n",
include ? "add" : "del",
vlan->vid, port);
if (vlan->vid > RTL8365MB_MAX_4K_VID) {
NL_SET_ERR_MSG_MOD(extack, "VLAN ID greater than "
__stringify(RTL8365MB_MAX_4K_VID));
return -EINVAL;
}
ret = rtl8365mb_vlan_4k_read(priv, vlan->vid, &vlan4k);
if (ret) {
dev_err(priv->dev, "Failed to read VLAN 4k table\n");
return ret;
}
if (include)
vlan4k.member |= BIT(port);
else
vlan4k.member &= ~BIT(port);
if (include && (vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED))
vlan4k.untag |= BIT(port);
else
vlan4k.untag &= ~BIT(port);
vlan4k.ivl_en = true;
ret = rtl8365mb_vlan_4k_write(priv, &vlan4k);
if (ret) {
dev_err(priv->dev, "Failed to write VLAN 4k table\n");
return ret;
}
return 0;
}
int rtl8365mb_vlan_4k_port_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct netlink_ext_ack *extack)
{
struct realtek_priv *priv = ds->priv;
lockdep_assert_held(&priv->vlan_lock);
return rtl8365mb_vlan_4k_port_set(ds, port, vlan, extack, true);
}
int rtl8365mb_vlan_4k_port_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan)
{
struct realtek_priv *priv = ds->priv;
lockdep_assert_held(&priv->vlan_lock);
return rtl8365mb_vlan_4k_port_set(ds, port, vlan, NULL, false);
}
static int rtl8365mb_vlan_mc_read(struct realtek_priv *priv, u32 index,
struct rtl8365mb_vlanmc *vlanmc)
{
u16 data[RTL8365MB_VLAN_MC_ENTRY_SIZE];
int ret;
ret = regmap_bulk_read(priv->map, RTL8365MB_VLAN_MC_REG(index), &data,
RTL8365MB_VLAN_MC_ENTRY_SIZE);
if (ret)
return ret;
vlanmc->member = FIELD_GET(RTL8365MB_VLAN_MC_D0_MBR_MASK, data[0]);
vlanmc->fid = FIELD_GET(RTL8365MB_VLAN_MC_D1_FID_MASK, data[1]);
vlanmc->meteridx = FIELD_GET(RTL8365MB_VLAN_MC_D2_METERIDX_MASK,
data[2]);
vlanmc->policing_en = FIELD_GET(RTL8365MB_VLAN_MC_D2_ENVLANPOL_MASK,
data[2]);
vlanmc->priority = FIELD_GET(RTL8365MB_VLAN_MC_D2_VBPRI_MASK, data[2]);
vlanmc->priority_en = FIELD_GET(RTL8365MB_VLAN_MC_D2_VBPEN_MASK,
data[2]);
vlanmc->evid = FIELD_GET(RTL8365MB_VLAN_MC_D3_EVID_MASK, data[3]);
return 0;
}
static int rtl8365mb_vlan_mc_write(struct realtek_priv *priv, u32 index,
const struct rtl8365mb_vlanmc *vlanmc)
{
u16 data[RTL8365MB_VLAN_MC_ENTRY_SIZE] = { 0 };
int ret;
data[0] |= FIELD_PREP(RTL8365MB_VLAN_MC_D0_MBR_MASK, vlanmc->member);
data[1] |= FIELD_PREP(RTL8365MB_VLAN_MC_D1_FID_MASK, vlanmc->fid);
data[2] |= FIELD_PREP(RTL8365MB_VLAN_MC_D2_METERIDX_MASK,
vlanmc->meteridx);
data[2] |= FIELD_PREP(RTL8365MB_VLAN_MC_D2_ENVLANPOL_MASK,
vlanmc->policing_en);
data[2] |=
FIELD_PREP(RTL8365MB_VLAN_MC_D2_VBPRI_MASK, vlanmc->priority);
data[2] |= FIELD_PREP(RTL8365MB_VLAN_MC_D2_VBPEN_MASK,
vlanmc->priority_en);
data[3] |= FIELD_PREP(RTL8365MB_VLAN_MC_D3_EVID_MASK, vlanmc->evid);
ret = regmap_bulk_write(priv->map, RTL8365MB_VLAN_MC_REG(index), &data,
RTL8365MB_VLAN_MC_ENTRY_SIZE);
return ret;
}
static int rtl8365mb_vlan_mc_erase(struct realtek_priv *priv, u32 index)
{
u16 data[RTL8365MB_VLAN_MC_ENTRY_SIZE] = { 0 };
int ret;
ret = regmap_bulk_write(priv->map, RTL8365MB_VLAN_MC_REG(index), &data,
RTL8365MB_VLAN_MC_ENTRY_SIZE);
return ret;
}
static int rtl8365mb_vlan_mc_find(struct realtek_priv *priv, u16 vid,
u8 *index, u8 *first_free)
{
u32 vlan_entry_d3;
u8 vlanmc_idx;
u16 evid;
int ret;
*index = 0;
*first_free = 0;
for (vlanmc_idx = 1; vlanmc_idx <= RTL8365MB_VLAN_MCMAX; vlanmc_idx++) {
ret = regmap_read(priv->map,
RTL8365MB_VLAN_MC_REG(vlanmc_idx) + 3,
&vlan_entry_d3);
if (ret)
return ret;
evid = FIELD_GET(RTL8365MB_VLAN_MC_D3_EVID_MASK, vlan_entry_d3);
if (evid == vid) {
*index = vlanmc_idx;
return 0;
}
if (evid == 0x0 && *first_free < 1)
*first_free = vlanmc_idx;
}
return 0;
}
static int rtl8365mb_vlan_port_get_pvid_idx(struct realtek_priv *priv,
int port, u8 *vlanmc_idx)
{
u32 data;
int ret;
ret = regmap_read(priv->map, RTL8365MB_VLAN_PVID_CTRL_REG(port), &data);
if (ret)
return ret;
*vlanmc_idx = (data & RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(port))
>> RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(port);
return 0;
}
static
int rtl8365mb_vlan_mc_port_set(struct dsa_switch *ds, int port,
u16 vid, bool pvid,
struct netlink_ext_ack *extack,
bool include)
{
struct realtek_priv *priv = ds->priv;
struct rtl8365mb_vlanmc vlanmc = {0};
u8 first_unused = 0;
u8 vlanmc_idx = 0;
int ret;
dev_dbg(priv->dev, "%s VLAN %d MC on port %d\n",
include ? "add" : "del",
vid, port);
if (vid > RTL8365MB_MAX_MC_VID) {
NL_SET_ERR_MSG_MOD(extack, "VLAN ID greater than "
__stringify(RTL8365MB_MAX_MC_VID));
return -EINVAL;
}
ret = rtl8365mb_vlan_mc_find(priv, vid, &vlanmc_idx,
&first_unused);
if (ret) {
dev_err(priv->dev, "Failed to find a VLAN MC table index\n");
return ret;
}
if (vlanmc_idx) {
ret = rtl8365mb_vlan_mc_read(priv, vlanmc_idx, &vlanmc);
if (ret) {
dev_err(priv->dev, "Failed to read VLAN MC table\n");
return ret;
}
} else if (include) {
if (!pvid) {
dev_dbg(priv->dev,
"Not creating VlanMC for vlan %d until a port uses PVID (%d does not)\n",
vid, port);
return 0;
}
if (!first_unused) {
NL_SET_ERR_MSG_MOD(extack, "All VLAN MC entries (0.."
__stringify(RTL8365MB_VLAN_MCMAX)
") are in use.");
return -ENOSPC;
}
vlanmc_idx = first_unused;
vlanmc.evid = vid;
} else {
return 0;
}
dev_dbg(priv->dev,
"VLAN %d (idx: %d) PVID curr members: %08x\n",
vid, vlanmc_idx, vlanmc.member);
if (include)
vlanmc.member |= BIT(port);
else
vlanmc.member &= ~BIT(port);
if (!vlanmc.member) {
dev_dbg(priv->dev,
"Clearing VlanMC index %d previously used by VID %d\n",
vlanmc_idx, vid);
ret = rtl8365mb_vlan_mc_erase(priv, vlanmc_idx);
} else {
dev_dbg(priv->dev,
"Saving VlanMC index %d with VID %d\n",
vlanmc_idx, vid);
ret = rtl8365mb_vlan_mc_write(priv, vlanmc_idx, &vlanmc);
}
if (ret) {
dev_err(priv->dev, "Failed to write vlan MC entry\n");
return ret;
}
return 0;
}
static int rtl8365mb_vlan_port_set_pvid(struct realtek_priv *priv,
int port, u16 vlanmc_idx)
{
int ret;
u32 val;
dev_dbg(priv->dev, "set PVID IDX %d on port %d\n", vlanmc_idx, port);
val = vlanmc_idx << RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(port);
ret = regmap_update_bits(priv->map,
RTL8365MB_VLAN_PVID_CTRL_REG(port),
RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(port),
val);
if (ret)
return ret;
return 0;
}
static int rtl8365mb_vlan_get_pvid_mc(struct realtek_priv *priv,
int port, u8 *vlanmc_idx,
struct rtl8365mb_vlanmc *vlanmc)
{
int ret;
ret = rtl8365mb_vlan_port_get_pvid_idx(priv, port, vlanmc_idx);
if (ret)
return ret;
memset(vlanmc, 0, sizeof(*vlanmc));
if (!*vlanmc_idx)
return 0;
ret = rtl8365mb_vlan_mc_read(priv, *vlanmc_idx, vlanmc);
if (ret)
return ret;
return 0;
}
int rtl8365mb_vlan_port_get_pvid(struct realtek_priv *priv, int port, u16 *pvid)
{
struct rtl8365mb_vlanmc vlanmc;
u8 vlanmc_idx;
int ret;
ret = rtl8365mb_vlan_get_pvid_mc(priv, port, &vlanmc_idx, &vlanmc);
if (ret)
return ret;
*pvid = vlanmc.evid;
return 0;
}
int
rtl8365mb_vlan_port_get_framefilter(struct realtek_priv *priv,
int port,
enum rtl8365mb_frame_ingress *frame_type)
{
u32 val;
int ret;
ret = regmap_read(priv->map, RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port),
&val);
if (ret)
return ret;
*frame_type = field_get(RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_MASK(port),
val);
return 0;
}
int
rtl8365mb_vlan_port_set_framefilter(struct realtek_priv *priv,
int port,
enum rtl8365mb_frame_ingress frame_type)
{
u32 val;
val = frame_type << RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port);
return regmap_update_bits(priv->map,
RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port),
RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_MASK(port),
val);
}
int rtl8365mb_vlan_pvid_port_set(struct dsa_switch *ds, int port, u16 vid,
struct netlink_ext_ack *extack)
{
enum rtl8365mb_frame_ingress accepted_frame, prev_accepted_frame;
struct realtek_priv *priv = ds->priv;
struct rtl8365mb_vlanmc prev_vlanmc = {0};
u8 _unused_first_free_idx;
u8 prev_vlanmc_idx;
u8 vlanmc_idx;
int ret;
lockdep_assert_held(&priv->vlan_lock);
ret = rtl8365mb_vlan_get_pvid_mc(priv, port, &prev_vlanmc_idx,
&prev_vlanmc);
if (ret) {
dev_err(priv->dev, "Failed to read current VLAN MC\n");
return ret;
}
ret = rtl8365mb_vlan_port_get_framefilter(priv, port,
&prev_accepted_frame);
if (ret) {
dev_err(priv->dev, "Failed to get current framefilter\n");
return ret;
}
ret = rtl8365mb_vlan_mc_port_set(ds, port, vid, true, extack, true);
if (ret)
return ret;
ret = rtl8365mb_vlan_mc_find(priv, vid, &vlanmc_idx,
&_unused_first_free_idx);
if (ret) {
dev_err(priv->dev, "Failed to find a VLAN MC table index\n");
goto undo_vlan_mc_port_set;
}
if (!vlanmc_idx) {
dev_err(priv->dev, "VLAN should already exist in VLAN MC\n");
ret = -ENOENT;
goto undo_vlan_mc_port_set;
}
ret = rtl8365mb_vlan_port_set_pvid(priv, port, vlanmc_idx);
if (ret) {
dev_err(priv->dev, "Failed to set port PVID\n");
goto undo_vlan_mc_port_set;
}
accepted_frame = RTL8365MB_FRAME_TYPE_ANY_FRAME;
ret = rtl8365mb_vlan_port_set_framefilter(priv, port, accepted_frame);
if (ret) {
dev_err(priv->dev, "Failed to set port frame filter\n");
goto undo_vlan_port_set_pvid;
}
if (prev_vlanmc_idx && (prev_vlanmc.evid != vid)) {
ret = rtl8365mb_vlan_mc_port_set(ds, port, prev_vlanmc.evid,
false, NULL, false);
if (ret) {
dev_err(priv->dev, "Failed to clear old VLAN MC\n");
goto undo_set_framefilter;
}
}
return 0;
undo_set_framefilter:
(void)rtl8365mb_vlan_port_set_framefilter(priv, port,
prev_accepted_frame);
undo_vlan_port_set_pvid:
(void)rtl8365mb_vlan_port_set_pvid(priv, port, prev_vlanmc_idx);
undo_vlan_mc_port_set:
if (prev_vlanmc.evid != vid)
(void)rtl8365mb_vlan_mc_port_set(ds, port, vid, false, NULL,
false);
return ret;
}
int rtl8365mb_vlan_pvid_port_clear(struct dsa_switch *ds, int port, u16 vid)
{
enum rtl8365mb_frame_ingress accepted_frame, prev_accepted_frame;
struct realtek_priv *priv = ds->priv;
struct rtl8365mb_vlanmc vlanmc = {0};
u8 vlanmc_idx;
int ret;
lockdep_assert_held(&priv->vlan_lock);
ret = rtl8365mb_vlan_get_pvid_mc(priv, port, &vlanmc_idx,
&vlanmc);
if (ret) {
dev_err(priv->dev, "Failed to read current VLAN MC\n");
return ret;
}
if (!vlanmc_idx)
return 0;
if (vlanmc.evid != vid)
return 0;
ret = rtl8365mb_vlan_port_get_framefilter(priv, port,
&prev_accepted_frame);
if (ret) {
dev_err(priv->dev, "Failed to get current framefilter\n");
return ret;
}
if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) {
accepted_frame = RTL8365MB_FRAME_TYPE_TAGGED_ONLY;
ret = rtl8365mb_vlan_port_set_framefilter(priv, port,
accepted_frame);
if (ret) {
dev_err(priv->dev, "Failed to set port frame filter\n");
return ret;
}
} else {
accepted_frame = prev_accepted_frame;
}
ret = rtl8365mb_vlan_port_set_pvid(priv, port, 0);
if (ret) {
dev_err(priv->dev, "Failed to set port PVID to 0\n");
goto undo_set_framefilter;
}
ret = rtl8365mb_vlan_mc_port_set(ds, port, vlanmc.evid,
false, NULL, false);
if (ret)
goto undo_port_set_pvid;
return 0;
undo_port_set_pvid:
(void)rtl8365mb_vlan_port_set_pvid(priv, port, vlanmc_idx);
undo_set_framefilter:
if (prev_accepted_frame != accepted_frame)
(void)rtl8365mb_vlan_port_set_framefilter(priv, port,
prev_accepted_frame);
return ret;
}