Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4bb2f84a authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-dsa-mv88e6xxx-add-PCL-support'



Vivien Didelot says:

====================
net: dsa: mv88e6xxx: add PCL support

This small series implements the ethtool RXNFC operations in the
mv88e6xxx DSA driver to configure a port's Layer 2 Policy Control List
(PCL) supported by models such as 88E6352 and 88E6390 and equivalent.

This allows to configure a port to discard frames based on a configured
destination or source MAC address and an optional VLAN, with e.g.:

    # ethtool --config-nfc lan1 flow-type ether src 00:11:22:33:44:55 action -1
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c21815f1 da7dc875
Loading
Loading
Loading
Loading
+230 −11
Original line number Diff line number Diff line
@@ -1497,7 +1497,7 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
		fid = vlan.fid;
	}

	entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED;
	entry.state = 0;
	ether_addr_copy(entry.mac, addr);
	eth_addr_dec(entry.mac);

@@ -1506,17 +1506,16 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
		return err;

	/* Initialize a fresh ATU entry if it isn't found */
	if (entry.state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED ||
	    !ether_addr_equal(entry.mac, addr)) {
	if (!entry.state || !ether_addr_equal(entry.mac, addr)) {
		memset(&entry, 0, sizeof(entry));
		ether_addr_copy(entry.mac, addr);
	}

	/* Purge the ATU entry only if no port is using it anymore */
	if (state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) {
	if (!state) {
		entry.portvec &= ~BIT(port);
		if (!entry.portvec)
			entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED;
			entry.state = 0;
	} else {
		entry.portvec |= BIT(port);
		entry.state = state;
@@ -1525,6 +1524,216 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
	return mv88e6xxx_g1_atu_loadpurge(chip, fid, &entry);
}

static int mv88e6xxx_policy_apply(struct mv88e6xxx_chip *chip, int port,
				  const struct mv88e6xxx_policy *policy)
{
	enum mv88e6xxx_policy_mapping mapping = policy->mapping;
	enum mv88e6xxx_policy_action action = policy->action;
	const u8 *addr = policy->addr;
	u16 vid = policy->vid;
	u8 state;
	int err;
	int id;

	if (!chip->info->ops->port_set_policy)
		return -EOPNOTSUPP;

	switch (mapping) {
	case MV88E6XXX_POLICY_MAPPING_DA:
	case MV88E6XXX_POLICY_MAPPING_SA:
		if (action == MV88E6XXX_POLICY_ACTION_NORMAL)
			state = 0; /* Dissociate the port and address */
		else if (action == MV88E6XXX_POLICY_ACTION_DISCARD &&
			 is_multicast_ether_addr(addr))
			state = MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY;
		else if (action == MV88E6XXX_POLICY_ACTION_DISCARD &&
			 is_unicast_ether_addr(addr))
			state = MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY;
		else
			return -EOPNOTSUPP;

		err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid,
						   state);
		if (err)
			return err;
		break;
	default:
		return -EOPNOTSUPP;
	}

	/* Skip the port's policy clearing if the mapping is still in use */
	if (action == MV88E6XXX_POLICY_ACTION_NORMAL)
		idr_for_each_entry(&chip->policies, policy, id)
			if (policy->port == port &&
			    policy->mapping == mapping &&
			    policy->action != action)
				return 0;

	return chip->info->ops->port_set_policy(chip, port, mapping, action);
}

static int mv88e6xxx_policy_insert(struct mv88e6xxx_chip *chip, int port,
				   struct ethtool_rx_flow_spec *fs)
{
	struct ethhdr *mac_entry = &fs->h_u.ether_spec;
	struct ethhdr *mac_mask = &fs->m_u.ether_spec;
	enum mv88e6xxx_policy_mapping mapping;
	enum mv88e6xxx_policy_action action;
	struct mv88e6xxx_policy *policy;
	u16 vid = 0;
	u8 *addr;
	int err;
	int id;

	if (fs->location != RX_CLS_LOC_ANY)
		return -EINVAL;

	if (fs->ring_cookie == RX_CLS_FLOW_DISC)
		action = MV88E6XXX_POLICY_ACTION_DISCARD;
	else
		return -EOPNOTSUPP;

	switch (fs->flow_type & ~FLOW_EXT) {
	case ETHER_FLOW:
		if (!is_zero_ether_addr(mac_mask->h_dest) &&
		    is_zero_ether_addr(mac_mask->h_source)) {
			mapping = MV88E6XXX_POLICY_MAPPING_DA;
			addr = mac_entry->h_dest;
		} else if (is_zero_ether_addr(mac_mask->h_dest) &&
		    !is_zero_ether_addr(mac_mask->h_source)) {
			mapping = MV88E6XXX_POLICY_MAPPING_SA;
			addr = mac_entry->h_source;
		} else {
			/* Cannot support DA and SA mapping in the same rule */
			return -EOPNOTSUPP;
		}
		break;
	default:
		return -EOPNOTSUPP;
	}

	if ((fs->flow_type & FLOW_EXT) && fs->m_ext.vlan_tci) {
		if (fs->m_ext.vlan_tci != 0xffff)
			return -EOPNOTSUPP;
		vid = be16_to_cpu(fs->h_ext.vlan_tci) & VLAN_VID_MASK;
	}

	idr_for_each_entry(&chip->policies, policy, id) {
		if (policy->port == port && policy->mapping == mapping &&
		    policy->action == action && policy->vid == vid &&
		    ether_addr_equal(policy->addr, addr))
			return -EEXIST;
	}

	policy = devm_kzalloc(chip->dev, sizeof(*policy), GFP_KERNEL);
	if (!policy)
		return -ENOMEM;

	fs->location = 0;
	err = idr_alloc_u32(&chip->policies, policy, &fs->location, 0xffffffff,
			    GFP_KERNEL);
	if (err) {
		devm_kfree(chip->dev, policy);
		return err;
	}

	memcpy(&policy->fs, fs, sizeof(*fs));
	ether_addr_copy(policy->addr, addr);
	policy->mapping = mapping;
	policy->action = action;
	policy->port = port;
	policy->vid = vid;

	err = mv88e6xxx_policy_apply(chip, port, policy);
	if (err) {
		idr_remove(&chip->policies, fs->location);
		devm_kfree(chip->dev, policy);
		return err;
	}

	return 0;
}

static int mv88e6xxx_get_rxnfc(struct dsa_switch *ds, int port,
			       struct ethtool_rxnfc *rxnfc, u32 *rule_locs)
{
	struct ethtool_rx_flow_spec *fs = &rxnfc->fs;
	struct mv88e6xxx_chip *chip = ds->priv;
	struct mv88e6xxx_policy *policy;
	int err;
	int id;

	mv88e6xxx_reg_lock(chip);

	switch (rxnfc->cmd) {
	case ETHTOOL_GRXCLSRLCNT:
		rxnfc->data = 0;
		rxnfc->data |= RX_CLS_LOC_SPECIAL;
		rxnfc->rule_cnt = 0;
		idr_for_each_entry(&chip->policies, policy, id)
			if (policy->port == port)
				rxnfc->rule_cnt++;
		err = 0;
		break;
	case ETHTOOL_GRXCLSRULE:
		err = -ENOENT;
		policy = idr_find(&chip->policies, fs->location);
		if (policy) {
			memcpy(fs, &policy->fs, sizeof(*fs));
			err = 0;
		}
		break;
	case ETHTOOL_GRXCLSRLALL:
		rxnfc->data = 0;
		rxnfc->rule_cnt = 0;
		idr_for_each_entry(&chip->policies, policy, id)
			if (policy->port == port)
				rule_locs[rxnfc->rule_cnt++] = id;
		err = 0;
		break;
	default:
		err = -EOPNOTSUPP;
		break;
	}

	mv88e6xxx_reg_unlock(chip);

	return err;
}

static int mv88e6xxx_set_rxnfc(struct dsa_switch *ds, int port,
			       struct ethtool_rxnfc *rxnfc)
{
	struct ethtool_rx_flow_spec *fs = &rxnfc->fs;
	struct mv88e6xxx_chip *chip = ds->priv;
	struct mv88e6xxx_policy *policy;
	int err;

	mv88e6xxx_reg_lock(chip);

	switch (rxnfc->cmd) {
	case ETHTOOL_SRXCLSRLINS:
		err = mv88e6xxx_policy_insert(chip, port, fs);
		break;
	case ETHTOOL_SRXCLSRLDEL:
		err = -ENOENT;
		policy = idr_remove(&chip->policies, fs->location);
		if (policy) {
			policy->action = MV88E6XXX_POLICY_ACTION_NORMAL;
			err = mv88e6xxx_policy_apply(chip, port, policy);
			devm_kfree(chip->dev, policy);
		}
		break;
	default:
		err = -EOPNOTSUPP;
		break;
	}

	mv88e6xxx_reg_unlock(chip);

	return err;
}

static int mv88e6xxx_port_add_broadcast(struct mv88e6xxx_chip *chip, int port,
					u16 vid)
{
@@ -1732,8 +1941,7 @@ static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
	int err;

	mv88e6xxx_reg_lock(chip);
	err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid,
					   MV88E6XXX_G1_ATU_DATA_STATE_UNUSED);
	err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, 0);
	mv88e6xxx_reg_unlock(chip);

	return err;
@@ -1747,7 +1955,7 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
	bool is_static;
	int err;

	addr.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED;
	addr.state = 0;
	eth_broadcast_addr(addr.mac);

	do {
@@ -1755,7 +1963,7 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
		if (err)
			return err;

		if (addr.state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED)
		if (!addr.state)
			break;

		if (addr.trunk || (addr.portvec & BIT(port)) == 0)
@@ -3134,6 +3342,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
	.port_set_speed = mv88e6352_port_set_speed,
	.port_tag_remap = mv88e6095_port_tag_remap,
	.port_set_policy = mv88e6352_port_set_policy,
	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
	.port_set_ether_type = mv88e6351_port_set_ether_type,
@@ -3220,6 +3429,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
	.port_set_speed = mv88e6352_port_set_speed,
	.port_tag_remap = mv88e6095_port_tag_remap,
	.port_set_policy = mv88e6352_port_set_policy,
	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
	.port_set_ether_type = mv88e6351_port_set_ether_type,
@@ -3305,6 +3515,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
	.port_set_speed = mv88e6390_port_set_speed,
	.port_max_speed_mode = mv88e6390_port_max_speed_mode,
	.port_tag_remap = mv88e6390_port_tag_remap,
	.port_set_policy = mv88e6352_port_set_policy,
	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
	.port_set_ether_type = mv88e6351_port_set_ether_type,
@@ -3353,6 +3564,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
	.port_set_speed = mv88e6390x_port_set_speed,
	.port_max_speed_mode = mv88e6390x_port_max_speed_mode,
	.port_tag_remap = mv88e6390_port_tag_remap,
	.port_set_policy = mv88e6352_port_set_policy,
	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
	.port_set_ether_type = mv88e6351_port_set_ether_type,
@@ -3450,6 +3662,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
	.port_set_speed = mv88e6352_port_set_speed,
	.port_tag_remap = mv88e6095_port_tag_remap,
	.port_set_policy = mv88e6352_port_set_policy,
	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
	.port_set_ether_type = mv88e6351_port_set_ether_type,
@@ -3541,6 +3754,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
	.port_set_speed = mv88e6390_port_set_speed,
	.port_max_speed_mode = mv88e6390_port_max_speed_mode,
	.port_tag_remap = mv88e6390_port_tag_remap,
	.port_set_policy = mv88e6352_port_set_policy,
	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
	.port_set_ether_type = mv88e6351_port_set_ether_type,
@@ -3811,6 +4025,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
	.port_set_speed = mv88e6352_port_set_speed,
	.port_tag_remap = mv88e6095_port_tag_remap,
	.port_set_policy = mv88e6352_port_set_policy,
	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
	.port_set_ether_type = mv88e6351_port_set_ether_type,
@@ -3865,6 +4080,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
	.port_set_speed = mv88e6390_port_set_speed,
	.port_max_speed_mode = mv88e6390_port_max_speed_mode,
	.port_tag_remap = mv88e6390_port_tag_remap,
	.port_set_policy = mv88e6352_port_set_policy,
	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
	.port_set_ether_type = mv88e6351_port_set_ether_type,
@@ -3917,6 +4133,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
	.port_set_speed = mv88e6390x_port_set_speed,
	.port_max_speed_mode = mv88e6390x_port_max_speed_mode,
	.port_tag_remap = mv88e6390_port_tag_remap,
	.port_set_policy = mv88e6352_port_set_policy,
	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
	.port_set_ether_type = mv88e6351_port_set_ether_type,
@@ -4648,6 +4865,7 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)

	mutex_init(&chip->reg_lock);
	INIT_LIST_HEAD(&chip->mdios);
	idr_init(&chip->policies);

	return chip;
}
@@ -4690,8 +4908,7 @@ static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
	int err;

	mv88e6xxx_reg_lock(chip);
	err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
					   MV88E6XXX_G1_ATU_DATA_STATE_UNUSED);
	err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, 0);
	mv88e6xxx_reg_unlock(chip);

	return err;
@@ -4733,6 +4950,8 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
	.set_eeprom		= mv88e6xxx_set_eeprom,
	.get_regs_len		= mv88e6xxx_get_regs_len,
	.get_regs		= mv88e6xxx_get_regs,
	.get_rxnfc		= mv88e6xxx_get_rxnfc,
	.set_rxnfc		= mv88e6xxx_set_rxnfc,
	.set_ageing_time	= mv88e6xxx_set_ageing_time,
	.port_bridge_join	= mv88e6xxx_port_bridge_join,
	.port_bridge_leave	= mv88e6xxx_port_bridge_leave,
+35 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#ifndef _MV88E6XXX_CHIP_H
#define _MV88E6XXX_CHIP_H

#include <linux/idr.h>
#include <linux/if_vlan.h>
#include <linux/irq.h>
#include <linux/gpio/consumer.h>
@@ -189,6 +190,33 @@ struct mv88e6xxx_port_hwtstamp {
	struct hwtstamp_config tstamp_config;
};

enum mv88e6xxx_policy_mapping {
	MV88E6XXX_POLICY_MAPPING_DA,
	MV88E6XXX_POLICY_MAPPING_SA,
	MV88E6XXX_POLICY_MAPPING_VTU,
	MV88E6XXX_POLICY_MAPPING_ETYPE,
	MV88E6XXX_POLICY_MAPPING_PPPOE,
	MV88E6XXX_POLICY_MAPPING_VBAS,
	MV88E6XXX_POLICY_MAPPING_OPT82,
	MV88E6XXX_POLICY_MAPPING_UDP,
};

enum mv88e6xxx_policy_action {
	MV88E6XXX_POLICY_ACTION_NORMAL,
	MV88E6XXX_POLICY_ACTION_MIRROR,
	MV88E6XXX_POLICY_ACTION_TRAP,
	MV88E6XXX_POLICY_ACTION_DISCARD,
};

struct mv88e6xxx_policy {
	enum mv88e6xxx_policy_mapping mapping;
	enum mv88e6xxx_policy_action action;
	struct ethtool_rx_flow_spec fs;
	u8 addr[ETH_ALEN];
	int port;
	u16 vid;
};

struct mv88e6xxx_port {
	struct mv88e6xxx_chip *chip;
	int port;
@@ -247,6 +275,9 @@ struct mv88e6xxx_chip {
	/* List of mdio busses */
	struct list_head mdios;

	/* Policy Control List IDs and rules */
	struct idr policies;

	/* There can be two interrupt controllers, which are chained
	 * off a GPIO as interrupt source
	 */
@@ -381,6 +412,10 @@ struct mv88e6xxx_ops {

	int (*port_tag_remap)(struct mv88e6xxx_chip *chip, int port);

	int (*port_set_policy)(struct mv88e6xxx_chip *chip, int port,
			       enum mv88e6xxx_policy_mapping mapping,
			       enum mv88e6xxx_policy_action action);

	int (*port_set_frame_mode)(struct mv88e6xxx_chip *chip, int port,
				   enum mv88e6xxx_frame_mode mode);
	int (*port_set_egress_floods)(struct mv88e6xxx_chip *chip, int port,
+30 −13
Original line number Diff line number Diff line
@@ -133,14 +133,31 @@
#define MV88E6XXX_G1_ATU_DATA_TRUNK_ID_MASK			0x00f0
#define MV88E6XXX_G1_ATU_DATA_PORT_VECTOR_MASK			0x3ff0
#define MV88E6XXX_G1_ATU_DATA_STATE_MASK			0x000f
#define MV88E6XXX_G1_ATU_DATA_STATE_UNUSED		0x0000
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_MGMT		0x000d
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_UNUSED			0x0000
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_1_OLDEST		0x0001
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_2			0x0002
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_3			0x0003
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_4			0x0004
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_5			0x0005
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_6			0x0006
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_7_NEWEST		0x0007
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY		0x0008
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY_PO		0x0009
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL		0x000a
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL_PO	0x000b
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT		0x000c
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT_PO	0x000d
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC			0x000e
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_PRIO_OVER	0x000f
#define MV88E6XXX_G1_ATU_DATA_STATE_MC_NONE_RATE	0x0005
#define MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_PO		0x000f
#define MV88E6XXX_G1_ATU_DATA_STATE_MC_UNUSED			0x0000
#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY		0x0004
#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL		0x0005
#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT		0x0006
#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC			0x0007
#define MV88E6XXX_G1_ATU_DATA_STATE_MC_MGMT		0x000e
#define MV88E6XXX_G1_ATU_DATA_STATE_MC_PRIO_OVER	0x000f
#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY_PO		0x000c
#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL_PO	0x000d
#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT_PO	0x000e
#define MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_PO		0x000f

/* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1
 * Offset 0x0E: ATU MAC Address Register Bytes 2 & 3
+3 −3
Original line number Diff line number Diff line
@@ -135,7 +135,7 @@ static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip,
		return err;

	entry->state = val & 0xf;
	if (entry->state != MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) {
	if (entry->state) {
		entry->trunk = !!(val & MV88E6XXX_G1_ATU_DATA_TRUNK);
		entry->portvec = (val >> 4) & mv88e6xxx_port_mask(chip);
	}
@@ -148,7 +148,7 @@ static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip,
{
	u16 data = entry->state & 0xf;

	if (entry->state != MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) {
	if (entry->state) {
		if (entry->trunk)
			data |= MV88E6XXX_G1_ATU_DATA_TRUNK;

@@ -209,7 +209,7 @@ int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
		return err;

	/* Write the MAC address to iterate from only once */
	if (entry->state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) {
	if (!entry->state) {
		err = mv88e6xxx_g1_atu_mac_write(chip, entry);
		if (err)
			return err;
+74 −0
Original line number Diff line number Diff line
@@ -1341,3 +1341,77 @@ int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port)

	return 0;
}

/* Offset 0x0E: Policy Control Register */

int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port,
			      enum mv88e6xxx_policy_mapping mapping,
			      enum mv88e6xxx_policy_action action)
{
	u16 reg, mask, val;
	int shift;
	int err;

	switch (mapping) {
	case MV88E6XXX_POLICY_MAPPING_DA:
		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_DA_MASK);
		mask = MV88E6XXX_PORT_POLICY_CTL_DA_MASK;
		break;
	case MV88E6XXX_POLICY_MAPPING_SA:
		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_SA_MASK);
		mask = MV88E6XXX_PORT_POLICY_CTL_SA_MASK;
		break;
	case MV88E6XXX_POLICY_MAPPING_VTU:
		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VTU_MASK);
		mask = MV88E6XXX_PORT_POLICY_CTL_VTU_MASK;
		break;
	case MV88E6XXX_POLICY_MAPPING_ETYPE:
		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK);
		mask = MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK;
		break;
	case MV88E6XXX_POLICY_MAPPING_PPPOE:
		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK);
		mask = MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK;
		break;
	case MV88E6XXX_POLICY_MAPPING_VBAS:
		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK);
		mask = MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK;
		break;
	case MV88E6XXX_POLICY_MAPPING_OPT82:
		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK);
		mask = MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK;
		break;
	case MV88E6XXX_POLICY_MAPPING_UDP:
		shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_UDP_MASK);
		mask = MV88E6XXX_PORT_POLICY_CTL_UDP_MASK;
		break;
	default:
		return -EOPNOTSUPP;
	}

	switch (action) {
	case MV88E6XXX_POLICY_ACTION_NORMAL:
		val = MV88E6XXX_PORT_POLICY_CTL_NORMAL;
		break;
	case MV88E6XXX_POLICY_ACTION_MIRROR:
		val = MV88E6XXX_PORT_POLICY_CTL_MIRROR;
		break;
	case MV88E6XXX_POLICY_ACTION_TRAP:
		val = MV88E6XXX_PORT_POLICY_CTL_TRAP;
		break;
	case MV88E6XXX_POLICY_ACTION_DISCARD:
		val = MV88E6XXX_PORT_POLICY_CTL_DISCARD;
		break;
	default:
		return -EOPNOTSUPP;
	}

	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_POLICY_CTL, &reg);
	if (err)
		return err;

	reg &= ~mask;
	reg |= (val << shift) & mask;

	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_POLICY_CTL, reg);
}
Loading