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

Commit 5e31c701 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'dsa-mv88e6xxx-g2-cleanup-stp'



Vivien Didelot says:

====================
net: dsa: mv88e6xxx: Global2 cleanup and STP

The Marvell switches registers are organized in distinct internal SMI
devices, such as PHY, Port, Global 1 or Global 2 registers sets.

Since not all chips support every registers sets or have slightly
differences in them (such as old 88E6060 or new 88E6390 likely to be
supported soon), make the setup code clearer now by removing a few
family checks and adding flags to describe the Global 2 registers map.

This patchset enables basic STP support and bridging on most chips when
getting rid of a few inconsistencies in chip descriptions (patch 1) and
add bridge Ageing Time support to DSA and the mv88e6xxx driver.

Changes v2 -> v3:
  - rename mv88e6xxx_update_write to mv88e6xxx_update
  - set fastest ageing time in use in the chip for multiple bridges,
    tested with a few printk

Changes v1 -> v2:
  - add a write helper for pointer-data Update registers
  - add ageing time support
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 183fc153 2cfcd964
Loading
Loading
Loading
Loading
+338 −178
Original line number Original line Diff line number Diff line
@@ -216,6 +216,32 @@ static int mv88e6xxx_write(struct mv88e6xxx_chip *chip,
	return 0;
	return 0;
}
}


/* Indirect write to single pointer-data register with an Update bit */
static int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg,
			    u16 update)
{
	u16 val;
	int i, err;

	/* Wait until the previous operation is completed */
	for (i = 0; i < 16; ++i) {
		err = mv88e6xxx_read(chip, addr, reg, &val);
		if (err)
			return err;

		if (!(val & BIT(15)))
			break;
	}

	if (i == 16)
		return -ETIMEDOUT;

	/* Set the Update bit to trigger a write operation */
	val = BIT(15) | update;

	return mv88e6xxx_write(chip, addr, reg, val);
}

static int _mv88e6xxx_reg_read(struct mv88e6xxx_chip *chip, int addr, int reg)
static int _mv88e6xxx_reg_read(struct mv88e6xxx_chip *chip, int addr, int reg)
{
{
	u16 val;
	u16 val;
@@ -257,68 +283,6 @@ static int mv88e6xxx_reg_write(struct mv88e6xxx_chip *chip, int addr,
	return ret;
	return ret;
}
}


static int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
{
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);
	int err;

	err = mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_MAC_01,
				  (addr[0] << 8) | addr[1]);
	if (err)
		return err;

	err = mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_MAC_23,
				  (addr[2] << 8) | addr[3]);
	if (err)
		return err;

	return mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_MAC_45,
				   (addr[4] << 8) | addr[5]);
}

static int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
{
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);
	int ret;
	int i;

	for (i = 0; i < 6; i++) {
		int j;

		/* Write the MAC address byte. */
		ret = mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MAC,
					  GLOBAL2_SWITCH_MAC_BUSY |
					  (i << 8) | addr[i]);
		if (ret)
			return ret;

		/* Wait for the write to complete. */
		for (j = 0; j < 16; j++) {
			ret = mv88e6xxx_reg_read(chip, REG_GLOBAL2,
						 GLOBAL2_SWITCH_MAC);
			if (ret < 0)
				return ret;

			if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0)
				break;
		}
		if (j == 16)
			return -ETIMEDOUT;
	}

	return 0;
}

static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
{
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);

	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_SWITCH_MAC))
		return mv88e6xxx_set_addr_indirect(ds, addr);
	else
		return mv88e6xxx_set_addr_direct(ds, addr);
}

static int mv88e6xxx_mdio_read_direct(struct mv88e6xxx_chip *chip,
static int mv88e6xxx_mdio_read_direct(struct mv88e6xxx_chip *chip,
				      int addr, int regnum)
				      int addr, int regnum)
{
{
@@ -1460,9 +1424,6 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
	int stp_state;
	int stp_state;
	int err;
	int err;


	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_PORTSTATE))
		return;

	switch (state) {
	switch (state) {
	case BR_STATE_DISABLED:
	case BR_STATE_DISABLED:
		stp_state = PORT_CONTROL_STATE_DISABLED;
		stp_state = PORT_CONTROL_STATE_DISABLED;
@@ -2398,11 +2359,6 @@ static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
				      const struct switchdev_obj_port_fdb *fdb,
				      const struct switchdev_obj_port_fdb *fdb,
				      struct switchdev_trans *trans)
				      struct switchdev_trans *trans)
{
{
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);

	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_ATU))
		return -EOPNOTSUPP;

	/* We don't need any dynamic resource from the kernel (yet),
	/* We don't need any dynamic resource from the kernel (yet),
	 * so skip the prepare phase.
	 * so skip the prepare phase.
	 */
	 */
@@ -2418,9 +2374,6 @@ static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
		GLOBAL_ATU_DATA_STATE_UC_STATIC;
		GLOBAL_ATU_DATA_STATE_UC_STATIC;
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);


	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_ATU))
		return;

	mutex_lock(&chip->reg_lock);
	mutex_lock(&chip->reg_lock);
	if (_mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid, state))
	if (_mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid, state))
		netdev_err(ds->ports[port].netdev,
		netdev_err(ds->ports[port].netdev,
@@ -2434,9 +2387,6 @@ static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);
	int ret;
	int ret;


	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_ATU))
		return -EOPNOTSUPP;

	mutex_lock(&chip->reg_lock);
	mutex_lock(&chip->reg_lock);
	ret = _mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid,
	ret = _mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid,
				       GLOBAL_ATU_DATA_STATE_UNUSED);
				       GLOBAL_ATU_DATA_STATE_UNUSED);
@@ -2542,9 +2492,6 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
	u16 fid;
	u16 fid;
	int err;
	int err;


	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_ATU))
		return -EOPNOTSUPP;

	mutex_lock(&chip->reg_lock);
	mutex_lock(&chip->reg_lock);


	/* Dump port's default Filtering Information Database (VLAN ID 0) */
	/* Dump port's default Filtering Information Database (VLAN ID 0) */
@@ -2587,9 +2534,6 @@ static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);
	int i, err = 0;
	int i, err = 0;


	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VLANTABLE))
		return -EOPNOTSUPP;

	mutex_lock(&chip->reg_lock);
	mutex_lock(&chip->reg_lock);


	/* Assign the bridge and remap each port's VLANTable */
	/* Assign the bridge and remap each port's VLANTable */
@@ -2614,9 +2558,6 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
	struct net_device *bridge = chip->ports[port].bridge_dev;
	struct net_device *bridge = chip->ports[port].bridge_dev;
	int i;
	int i;


	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VLANTABLE))
		return;

	mutex_lock(&chip->reg_lock);
	mutex_lock(&chip->reg_lock);


	/* Unassign the bridge and remap each port's VLANTable */
	/* Unassign the bridge and remap each port's VLANTable */
@@ -3016,13 +2957,70 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
	return 0;
	return 0;
}
}


static int mv88e6xxx_setup_global(struct mv88e6xxx_chip *chip)
static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
{
	int err;

	err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_01,
			      (addr[0] << 8) | addr[1]);
	if (err)
		return err;

	err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_23,
			      (addr[2] << 8) | addr[3]);
	if (err)
		return err;

	return mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_45,
			       (addr[4] << 8) | addr[5]);
}

static int mv88e6xxx_g1_set_age_time(struct mv88e6xxx_chip *chip,
				     unsigned int msecs)
{
	const unsigned int coeff = chip->info->age_time_coeff;
	const unsigned int min = 0x01 * coeff;
	const unsigned int max = 0xff * coeff;
	u8 age_time;
	u16 val;
	int err;

	if (msecs < min || msecs > max)
		return -ERANGE;

	/* Round to nearest multiple of coeff */
	age_time = (msecs + coeff / 2) / coeff;

	err = mv88e6xxx_read(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL, &val);
	if (err)
		return err;

	/* AgeTime is 11:4 bits */
	val &= ~0xff0;
	val |= age_time << 4;

	return mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL, val);
}

static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
				     unsigned int ageing_time)
{
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);
	int err;

	mutex_lock(&chip->reg_lock);
	err = mv88e6xxx_g1_set_age_time(chip, ageing_time);
	mutex_unlock(&chip->reg_lock);

	return err;
}

static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
{
{
	struct dsa_switch *ds = chip->ds;
	struct dsa_switch *ds = chip->ds;
	u32 upstream_port = dsa_upstream_port(ds);
	u32 upstream_port = dsa_upstream_port(ds);
	u16 reg;
	u16 reg;
	int err;
	int err;
	int i;


	/* Enable the PHY Polling Unit if present, don't discard any packets,
	/* Enable the PHY Polling Unit if present, don't discard any packets,
	 * and mask all interrupt sources.
	 * and mask all interrupt sources.
@@ -3054,12 +3052,26 @@ static int mv88e6xxx_setup_global(struct mv88e6xxx_chip *chip)
	if (err)
	if (err)
		return err;
		return err;


	/* Clear all the VTU and STU entries */
	err = _mv88e6xxx_vtu_stu_flush(chip);
	if (err < 0)
		return err;

	/* Set the default address aging time to 5 minutes, and
	/* Set the default address aging time to 5 minutes, and
	 * enable address learn messages to be sent to all message
	 * enable address learn messages to be sent to all message
	 * ports.
	 * ports.
	 */
	 */
	err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL,
	err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL,
				   0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
			      GLOBAL_ATU_CONTROL_LEARN2ALL);
	if (err)
		return err;

	err = mv88e6xxx_g1_set_age_time(chip, 300000);
	if (err)
		return err;

	/* Clear all ATU entries */
	err = _mv88e6xxx_atu_flush(chip, 0, true);
	if (err)
	if (err)
		return err;
		return err;


@@ -3094,133 +3106,236 @@ static int mv88e6xxx_setup_global(struct mv88e6xxx_chip *chip)
	if (err)
	if (err)
		return err;
		return err;


	/* Send all frames with destination addresses matching
	/* Clear the statistics counters for all ports */
	 * 01:80:c2:00:00:0x to the CPU port.
	err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
	 */
				   GLOBAL_STATS_OP_FLUSH_ALL);
	err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X,
				   0xffff);
	if (err)
	if (err)
		return err;
		return err;


	/* Ignore removed tag data on doubly tagged packets, disable
	/* Wait for the flush to complete. */
	 * flow control messages, force flow control priority to the
	err = _mv88e6xxx_stats_wait(chip);
	 * highest, and send all special multicast frames to the CPU
	 * port at the highest priority.
	 */
	err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
				   0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
				   GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
	if (err)
	if (err)
		return err;
		return err;


	/* Program the DSA routing table. */
	return 0;
	for (i = 0; i < 32; i++) {
}
		int nexthop = 0x1f;


		if (i != ds->index && i < DSA_MAX_SWITCHES)
static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
			nexthop = ds->rtable[i] & 0x1f;
					     int target, int port)
{
	u16 val = (target << 8) | (port & 0xf);

	return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_DEVICE_MAPPING, val);
}


		err = _mv88e6xxx_reg_write(
static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
			chip, REG_GLOBAL2,
{
			GLOBAL2_DEVICE_MAPPING,
	int target, port;
			GLOBAL2_DEVICE_MAPPING_UPDATE |
	int err;
			(i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop);

	/* Initialize the routing port to the 32 possible target devices */
	for (target = 0; target < 32; ++target) {
		port = 0xf;

		if (target < DSA_MAX_SWITCHES) {
			port = chip->ds->rtable[target];
			if (port == DSA_RTABLE_NONE)
				port = 0xf;
		}

		err = mv88e6xxx_g2_device_mapping_write(chip, target, port);
		if (err)
		if (err)
			break;
	}

	return err;
	return err;
}
}


	/* Clear all trunk masks. */
static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
	for (i = 0; i < 8; i++) {
					 bool hask, u16 mask)
		err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
{
					   GLOBAL2_TRUNK_MASK,
	const u16 port_mask = BIT(chip->info->num_ports) - 1;
					   0x8000 |
	u16 val = (num << 12) | (mask & port_mask);
					   (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |

					   ((1 << chip->info->num_ports) - 1));
	if (hask)
		val |= GLOBAL2_TRUNK_MASK_HASK;

	return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_TRUNK_MASK, val);
}

static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
					    u16 map)
{
	const u16 port_mask = BIT(chip->info->num_ports) - 1;
	u16 val = (id << 11) | (map & port_mask);

	return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_TRUNK_MAPPING, val);
}

static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip)
{
	const u16 port_mask = BIT(chip->info->num_ports) - 1;
	int i, err;

	/* Clear all eight possible Trunk Mask vectors */
	for (i = 0; i < 8; ++i) {
		err = mv88e6xxx_g2_trunk_mask_write(chip, i, false, port_mask);
		if (err)
		if (err)
			return err;
			return err;
	}
	}


	/* Clear all trunk mappings. */
	/* Clear all sixteen possible Trunk ID routing vectors */
	for (i = 0; i < 16; i++) {
	for (i = 0; i < 16; ++i) {
		err = _mv88e6xxx_reg_write(
		err = mv88e6xxx_g2_trunk_mapping_write(chip, i, 0);
			chip, REG_GLOBAL2,
			GLOBAL2_TRUNK_MAPPING,
			GLOBAL2_TRUNK_MAPPING_UPDATE |
			(i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
		if (err)
		if (err)
			return err;
			return err;
	}
	}


	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
	return 0;
	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
}
	    mv88e6xxx_6320_family(chip)) {

		/* Send all frames with destination addresses matching
static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip)
		 * 01:80:c2:00:00:2x to the CPU port.
{
		 */
	int port, err;
		err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,

					   GLOBAL2_MGMT_EN_2X, 0xffff);
	/* Init all Ingress Rate Limit resources of all ports */
	for (port = 0; port < chip->info->num_ports; ++port) {
		/* XXX newer chips (like 88E6390) have different 2-bit ops */
		err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_IRL_CMD,
				      GLOBAL2_IRL_CMD_OP_INIT_ALL |
				      (port << 8));
		if (err)
		if (err)
			break;

		/* Wait for the operation to complete */
		err = _mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_IRL_CMD,
				      GLOBAL2_IRL_CMD_BUSY);
		if (err)
			break;
	}

	return err;
	return err;
}


		/* Initialise cross-chip port VLAN table to reset
/* Indirect write to the Switch MAC/WoL/WoF register */
		 * defaults.
static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
		 */
					 unsigned int pointer, u8 data)
		err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
{
					   GLOBAL2_PVT_ADDR, 0x9000);
	u16 val = (pointer << 8) | data;

	return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MAC, val);
}

static int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
{
	int i, err;

	for (i = 0; i < 6; i++) {
		err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
		if (err)
		if (err)
			break;
	}

	return err;
	return err;
}


		/* Clear the priority override table. */
static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
				  u8 data)
{
	u16 val = (pointer << 8) | (data & 0x7);

	return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_PRIO_OVERRIDE, val);
}

static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
{
	int i, err;

	/* Clear all sixteen possible Priority Override entries */
	for (i = 0; i < 16; i++) {
	for (i = 0; i < 16; i++) {
			err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
		err = mv88e6xxx_g2_pot_write(chip, i, 0);
						   GLOBAL2_PRIO_OVERRIDE,
						   0x8000 | (i << 8));
		if (err)
		if (err)
				return err;
			break;
	}
	}

	return err;
}
}


	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
static int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
{
	    mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
	u16 reg;
	    mv88e6xxx_6320_family(chip)) {
	int err;
		/* Disable ingress rate limiting by resetting all

		 * ingress rate limit registers to their initial
	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) {
		 * state.
		/* Consider the frames with reserved multicast destination
		 * addresses matching 01:80:c2:00:00:2x as MGMT.
		 */
		 */
		for (i = 0; i < chip->info->num_ports; i++) {
		err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_2X,
			err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
				      0xffff);
						   GLOBAL2_INGRESS_OP,
						   0x9000 | (i << 8));
		if (err)
		if (err)
			return err;
			return err;
	}
	}

	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X)) {
		/* Consider the frames with reserved multicast destination
		 * addresses matching 01:80:c2:00:00:0x as MGMT.
		 */
		err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X,
				      0xffff);
		if (err)
			return err;
	}
	}


	/* Clear the statistics counters for all ports */
	/* Ignore removed tag data on doubly tagged packets, disable
	err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
	 * flow control messages, force flow control priority to the
				   GLOBAL_STATS_OP_FLUSH_ALL);
	 * highest, and send all special multicast frames to the CPU
	 * port at the highest priority.
	 */
	reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4);
	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) ||
	    mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X))
		reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7;
	err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT, reg);
	if (err)
	if (err)
		return err;
		return err;


	/* Wait for the flush to complete. */
	/* Program the DSA routing table. */
	err = _mv88e6xxx_stats_wait(chip);
	err = mv88e6xxx_g2_set_device_mapping(chip);
	if (err)
	if (err)
		return err;
		return err;


	/* Clear all ATU entries */
	/* Clear all trunk masks and mapping. */
	err = _mv88e6xxx_atu_flush(chip, 0, true);
	err = mv88e6xxx_g2_clear_trunk(chip);
	if (err)
	if (err)
		return err;
		return err;


	/* Clear all the VTU and STU entries */
	if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_IRL)) {
	err = _mv88e6xxx_vtu_stu_flush(chip);
		/* Disable ingress rate limiting by resetting all per port
	if (err < 0)
		 * ingress rate limit resources to their initial state.
		 */
		err = mv88e6xxx_g2_clear_irl(chip);
			if (err)
				return err;
				return err;
	}


	if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_PVT)) {
		/* Initialize Cross-chip Port VLAN Table to reset defaults */
		err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_PVT_ADDR,
				      GLOBAL2_PVT_ADDR_OP_INIT_ONES);
		if (err)
			return err;
	}

	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) {
		/* Clear the priority override table. */
		err = mv88e6xxx_g2_clear_pot(chip);
		if (err)
			return err;
			return err;
	}
	}


	return 0;
}

static int mv88e6xxx_setup(struct dsa_switch *ds)
static int mv88e6xxx_setup(struct dsa_switch *ds)
{
{
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);
@@ -3239,12 +3354,21 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
	if (err)
	if (err)
		goto unlock;
		goto unlock;


	err = mv88e6xxx_setup_global(chip);
	/* Setup Switch Port Registers */
	for (i = 0; i < chip->info->num_ports; i++) {
		err = mv88e6xxx_setup_port(chip, i);
		if (err)
		if (err)
			goto unlock;
			goto unlock;
	}


	for (i = 0; i < chip->info->num_ports; i++) {
	/* Setup Switch Global 1 Registers */
		err = mv88e6xxx_setup_port(chip, i);
	err = mv88e6xxx_g1_setup(chip);
	if (err)
		goto unlock;

	/* Setup Switch Global 2 Registers */
	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) {
		err = mv88e6xxx_g2_setup(chip);
		if (err)
		if (err)
			goto unlock;
			goto unlock;
	}
	}
@@ -3255,6 +3379,24 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
	return err;
	return err;
}
}


static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
{
	struct mv88e6xxx_chip *chip = ds_to_priv(ds);
	int err;

	mutex_lock(&chip->reg_lock);

	/* Has an indirect Switch MAC/WoL/WoF register in Global 2? */
	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_SWITCH_MAC))
		err = mv88e6xxx_g2_set_switch_mac(chip, addr);
	else
		err = mv88e6xxx_g1_set_switch_mac(chip, addr);

	mutex_unlock(&chip->reg_lock);

	return err;
}

static int mv88e6xxx_mdio_page_read(struct dsa_switch *ds, int port, int page,
static int mv88e6xxx_mdio_page_read(struct dsa_switch *ds, int port, int page,
				    int reg)
				    int reg)
{
{
@@ -3536,6 +3678,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 10,
		.num_ports = 10,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6097,
		.flags = MV88E6XXX_FLAGS_FAMILY_6097,
	},
	},


@@ -3546,6 +3689,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 256,
		.num_databases = 256,
		.num_ports = 11,
		.num_ports = 11,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6095,
		.flags = MV88E6XXX_FLAGS_FAMILY_6095,
	},
	},


@@ -3556,6 +3700,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 3,
		.num_ports = 3,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
	},
	},


@@ -3566,6 +3711,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 256,
		.num_databases = 256,
		.num_ports = 8,
		.num_ports = 8,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6185,
		.flags = MV88E6XXX_FLAGS_FAMILY_6185,
	},
	},


@@ -3576,6 +3722,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 6,
		.num_ports = 6,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
	},
	},


@@ -3586,6 +3733,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 6,
		.num_ports = 6,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
	},
	},


@@ -3596,6 +3744,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 7,
		.num_ports = 7,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
	},
	},


@@ -3606,6 +3755,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 7,
		.num_ports = 7,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
	},
	},


@@ -3616,6 +3766,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 7,
		.num_ports = 7,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
	},
	},


@@ -3626,6 +3777,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 7,
		.num_ports = 7,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
	},
	},


@@ -3636,6 +3788,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 256,
		.num_databases = 256,
		.num_ports = 10,
		.num_ports = 10,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6185,
		.flags = MV88E6XXX_FLAGS_FAMILY_6185,
	},
	},


@@ -3646,6 +3799,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 7,
		.num_ports = 7,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
	},
	},


@@ -3656,6 +3810,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 7,
		.num_ports = 7,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6320,
		.flags = MV88E6XXX_FLAGS_FAMILY_6320,
	},
	},


@@ -3666,6 +3821,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 7,
		.num_ports = 7,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6320,
		.flags = MV88E6XXX_FLAGS_FAMILY_6320,
	},
	},


@@ -3676,6 +3832,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 7,
		.num_ports = 7,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
	},
	},


@@ -3686,6 +3843,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 7,
		.num_ports = 7,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
	},
	},


@@ -3696,6 +3854,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.num_databases = 4096,
		.num_databases = 4096,
		.num_ports = 7,
		.num_ports = 7,
		.port_base_addr = 0x10,
		.port_base_addr = 0x10,
		.age_time_coeff = 15000,
		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
	},
	},
};
};
@@ -3834,6 +3993,7 @@ static struct dsa_switch_driver mv88e6xxx_switch_driver = {
	.set_eeprom		= mv88e6xxx_set_eeprom,
	.set_eeprom		= mv88e6xxx_set_eeprom,
	.get_regs_len		= mv88e6xxx_get_regs_len,
	.get_regs_len		= mv88e6xxx_get_regs_len,
	.get_regs		= mv88e6xxx_get_regs,
	.get_regs		= mv88e6xxx_get_regs,
	.set_ageing_time	= mv88e6xxx_set_ageing_time,
	.port_bridge_join	= mv88e6xxx_port_bridge_join,
	.port_bridge_join	= mv88e6xxx_port_bridge_join,
	.port_bridge_leave	= mv88e6xxx_port_bridge_leave,
	.port_bridge_leave	= mv88e6xxx_port_bridge_leave,
	.port_stp_state_set	= mv88e6xxx_port_stp_state_set,
	.port_stp_state_set	= mv88e6xxx_port_stp_state_set,
+91 −57

File changed.

Preview size limit exceeded, changes collapsed.

+2 −0
Original line number Original line Diff line number Diff line
@@ -141,6 +141,7 @@ struct dsa_switch_tree {
struct dsa_port {
struct dsa_port {
	struct net_device	*netdev;
	struct net_device	*netdev;
	struct device_node	*dn;
	struct device_node	*dn;
	unsigned int		ageing_time;
};
};


struct dsa_switch {
struct dsa_switch {
@@ -329,6 +330,7 @@ struct dsa_switch_driver {
	/*
	/*
	 * Bridge integration
	 * Bridge integration
	 */
	 */
	int	(*set_ageing_time)(struct dsa_switch *ds, unsigned int msecs);
	int	(*port_bridge_join)(struct dsa_switch *ds, int port,
	int	(*port_bridge_join)(struct dsa_switch *ds, int port,
				    struct net_device *bridge);
				    struct net_device *bridge);
	void	(*port_bridge_leave)(struct dsa_switch *ds, int port);
	void	(*port_bridge_leave)(struct dsa_switch *ds, int port);
+41 −0
Original line number Original line Diff line number Diff line
@@ -333,6 +333,44 @@ static int dsa_slave_vlan_filtering(struct net_device *dev,
	return 0;
	return 0;
}
}


static int dsa_fastest_ageing_time(struct dsa_switch *ds,
				   unsigned int ageing_time)
{
	int i;

	for (i = 0; i < DSA_MAX_PORTS; ++i) {
		struct dsa_port *dp = &ds->ports[i];

		if (dp && dp->ageing_time && dp->ageing_time < ageing_time)
			ageing_time = dp->ageing_time;
	}

	return ageing_time;
}

static int dsa_slave_ageing_time(struct net_device *dev,
				 const struct switchdev_attr *attr,
				 struct switchdev_trans *trans)
{
	struct dsa_slave_priv *p = netdev_priv(dev);
	struct dsa_switch *ds = p->parent;
	unsigned long ageing_jiffies = clock_t_to_jiffies(attr->u.ageing_time);
	unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);

	/* bridge skips -EOPNOTSUPP, so skip the prepare phase */
	if (switchdev_trans_ph_prepare(trans))
		return 0;

	/* Keep the fastest ageing time in case of multiple bridges */
	ds->ports[p->port].ageing_time = ageing_time;
	ageing_time = dsa_fastest_ageing_time(ds, ageing_time);

	if (ds->drv->set_ageing_time)
		return ds->drv->set_ageing_time(ds, ageing_time);

	return 0;
}

static int dsa_slave_port_attr_set(struct net_device *dev,
static int dsa_slave_port_attr_set(struct net_device *dev,
				   const struct switchdev_attr *attr,
				   const struct switchdev_attr *attr,
				   struct switchdev_trans *trans)
				   struct switchdev_trans *trans)
@@ -346,6 +384,9 @@ static int dsa_slave_port_attr_set(struct net_device *dev,
	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
		ret = dsa_slave_vlan_filtering(dev, attr, trans);
		ret = dsa_slave_vlan_filtering(dev, attr, trans);
		break;
		break;
	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
		ret = dsa_slave_ageing_time(dev, attr, trans);
		break;
	default:
	default:
		ret = -EOPNOTSUPP;
		ret = -EOPNOTSUPP;
		break;
		break;