forked from mirrors/linux
		
	net: dsa: mv88e6xxx: Link aggregation support
Support offloading of LAGs to hardware. LAGs may be attached to a bridge in which case VLANs, multicast groups, etc. are also offloaded as usual. Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> Reviewed-by: Vladimir Oltean <olteanv@gmail.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
		
							parent
							
								
									058102a6e9
								
							
						
					
					
						commit
						57e661aae6
					
				
					 5 changed files with 330 additions and 5 deletions
				
			
		| 
						 | 
					@ -1396,15 +1396,32 @@ static int mv88e6xxx_mac_setup(struct mv88e6xxx_chip *chip)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port)
 | 
					static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct dsa_switch_tree *dst = chip->ds->dst;
 | 
				
			||||||
 | 
						struct dsa_switch *ds;
 | 
				
			||||||
 | 
						struct dsa_port *dp;
 | 
				
			||||||
	u16 pvlan = 0;
 | 
						u16 pvlan = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!mv88e6xxx_has_pvt(chip))
 | 
						if (!mv88e6xxx_has_pvt(chip))
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Skip the local source device, which uses in-chip port VLAN */
 | 
						/* Skip the local source device, which uses in-chip port VLAN */
 | 
				
			||||||
	if (dev != chip->ds->index)
 | 
						if (dev != chip->ds->index) {
 | 
				
			||||||
		pvlan = mv88e6xxx_port_vlan(chip, dev, port);
 | 
							pvlan = mv88e6xxx_port_vlan(chip, dev, port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ds = dsa_switch_find(dst->index, dev);
 | 
				
			||||||
 | 
							dp = ds ? dsa_to_port(ds, port) : NULL;
 | 
				
			||||||
 | 
							if (dp && dp->lag_dev) {
 | 
				
			||||||
 | 
								/* As the PVT is used to limit flooding of
 | 
				
			||||||
 | 
								 * FORWARD frames, which use the LAG ID as the
 | 
				
			||||||
 | 
								 * source port, we must translate dev/port to
 | 
				
			||||||
 | 
								 * the special "LAG device" in the PVT, using
 | 
				
			||||||
 | 
								 * the LAG ID as the port number.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								dev = MV88E6XXX_G2_PVT_ADRR_DEV_TRUNK;
 | 
				
			||||||
 | 
								port = dsa_lag_id(dst, dp->lag_dev);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return mv88e6xxx_g2_pvt_write(chip, dev, port, pvlan);
 | 
						return mv88e6xxx_g2_pvt_write(chip, dev, port, pvlan);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5364,6 +5381,271 @@ static int mv88e6xxx_port_egress_floods(struct dsa_switch *ds, int port,
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool mv88e6xxx_lag_can_offload(struct dsa_switch *ds,
 | 
				
			||||||
 | 
									      struct net_device *lag,
 | 
				
			||||||
 | 
									      struct netdev_lag_upper_info *info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct dsa_port *dp;
 | 
				
			||||||
 | 
						int id, members = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						id = dsa_lag_id(ds->dst, lag);
 | 
				
			||||||
 | 
						if (id < 0 || id >= ds->num_lag_ids)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dsa_lag_foreach_port(dp, ds->dst, lag)
 | 
				
			||||||
 | 
							/* Includes the port joining the LAG */
 | 
				
			||||||
 | 
							members++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (members > 8)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* We could potentially relax this to include active
 | 
				
			||||||
 | 
						 * backup in the future.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Ideally we would also validate that the hash type matches
 | 
				
			||||||
 | 
						 * the hardware. Alas, this is always set to unknown on team
 | 
				
			||||||
 | 
						 * interfaces.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mv88e6xxx_lag_sync_map(struct dsa_switch *ds, struct net_device *lag)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mv88e6xxx_chip *chip = ds->priv;
 | 
				
			||||||
 | 
						struct dsa_port *dp;
 | 
				
			||||||
 | 
						u16 map = 0;
 | 
				
			||||||
 | 
						int id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						id = dsa_lag_id(ds->dst, lag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Build the map of all ports to distribute flows destined for
 | 
				
			||||||
 | 
						 * this LAG. This can be either a local user port, or a DSA
 | 
				
			||||||
 | 
						 * port if the LAG port is on a remote chip.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						dsa_lag_foreach_port(dp, ds->dst, lag)
 | 
				
			||||||
 | 
							map |= BIT(dsa_towards_port(ds, dp->ds->index, dp->index));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return mv88e6xxx_g2_trunk_mapping_write(chip, id, map);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const u8 mv88e6xxx_lag_mask_table[8][8] = {
 | 
				
			||||||
 | 
						/* Row number corresponds to the number of active members in a
 | 
				
			||||||
 | 
						 * LAG. Each column states which of the eight hash buckets are
 | 
				
			||||||
 | 
						 * mapped to the column:th port in the LAG.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Example: In a LAG with three active ports, the second port
 | 
				
			||||||
 | 
						 * ([2][1]) would be selected for traffic mapped to buckets
 | 
				
			||||||
 | 
						 * 3,4,5 (0x38).
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						{ 0xff,    0,    0,    0,    0,    0,    0,    0 },
 | 
				
			||||||
 | 
						{ 0x0f, 0xf0,    0,    0,    0,    0,    0,    0 },
 | 
				
			||||||
 | 
						{ 0x07, 0x38, 0xc0,    0,    0,    0,    0,    0 },
 | 
				
			||||||
 | 
						{ 0x03, 0x0c, 0x30, 0xc0,    0,    0,    0,    0 },
 | 
				
			||||||
 | 
						{ 0x03, 0x0c, 0x30, 0x40, 0x80,    0,    0,    0 },
 | 
				
			||||||
 | 
						{ 0x03, 0x0c, 0x10, 0x20, 0x40, 0x80,    0,    0 },
 | 
				
			||||||
 | 
						{ 0x03, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,    0 },
 | 
				
			||||||
 | 
						{ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void mv88e6xxx_lag_set_port_mask(u16 *mask, int port,
 | 
				
			||||||
 | 
										int num_tx, int nth)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u8 active = 0;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						num_tx = num_tx <= 8 ? num_tx : 8;
 | 
				
			||||||
 | 
						if (nth < num_tx)
 | 
				
			||||||
 | 
							active = mv88e6xxx_lag_mask_table[num_tx - 1][nth];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < 8; i++) {
 | 
				
			||||||
 | 
							if (BIT(i) & active)
 | 
				
			||||||
 | 
								mask[i] |= BIT(port);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mv88e6xxx_chip *chip = ds->priv;
 | 
				
			||||||
 | 
						unsigned int id, num_tx;
 | 
				
			||||||
 | 
						struct net_device *lag;
 | 
				
			||||||
 | 
						struct dsa_port *dp;
 | 
				
			||||||
 | 
						int i, err, nth;
 | 
				
			||||||
 | 
						u16 mask[8];
 | 
				
			||||||
 | 
						u16 ivec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Assume no port is a member of any LAG. */
 | 
				
			||||||
 | 
						ivec = BIT(mv88e6xxx_num_ports(chip)) - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Disable all masks for ports that _are_ members of a LAG. */
 | 
				
			||||||
 | 
						list_for_each_entry(dp, &ds->dst->ports, list) {
 | 
				
			||||||
 | 
							if (!dp->lag_dev || dp->ds != ds)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ivec &= ~BIT(dp->index);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < 8; i++)
 | 
				
			||||||
 | 
							mask[i] = ivec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Enable the correct subset of masks for all LAG ports that
 | 
				
			||||||
 | 
						 * are in the Tx set.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						dsa_lags_foreach_id(id, ds->dst) {
 | 
				
			||||||
 | 
							lag = dsa_lag_dev(ds->dst, id);
 | 
				
			||||||
 | 
							if (!lag)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							num_tx = 0;
 | 
				
			||||||
 | 
							dsa_lag_foreach_port(dp, ds->dst, lag) {
 | 
				
			||||||
 | 
								if (dp->lag_tx_enabled)
 | 
				
			||||||
 | 
									num_tx++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!num_tx)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							nth = 0;
 | 
				
			||||||
 | 
							dsa_lag_foreach_port(dp, ds->dst, lag) {
 | 
				
			||||||
 | 
								if (!dp->lag_tx_enabled)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (dp->ds == ds)
 | 
				
			||||||
 | 
									mv88e6xxx_lag_set_port_mask(mask, dp->index,
 | 
				
			||||||
 | 
												    num_tx, nth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								nth++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < 8; i++) {
 | 
				
			||||||
 | 
							err = mv88e6xxx_g2_trunk_mask_write(chip, i, true, mask[i]);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mv88e6xxx_lag_sync_masks_map(struct dsa_switch *ds,
 | 
				
			||||||
 | 
										struct net_device *lag)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mv88e6xxx_lag_sync_masks(ds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!err)
 | 
				
			||||||
 | 
							err = mv88e6xxx_lag_sync_map(ds, lag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mv88e6xxx_port_lag_change(struct dsa_switch *ds, int port)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mv88e6xxx_chip *chip = ds->priv;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mv88e6xxx_reg_lock(chip);
 | 
				
			||||||
 | 
						err = mv88e6xxx_lag_sync_masks(ds);
 | 
				
			||||||
 | 
						mv88e6xxx_reg_unlock(chip);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mv88e6xxx_port_lag_join(struct dsa_switch *ds, int port,
 | 
				
			||||||
 | 
									   struct net_device *lag,
 | 
				
			||||||
 | 
									   struct netdev_lag_upper_info *info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mv88e6xxx_chip *chip = ds->priv;
 | 
				
			||||||
 | 
						int err, id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!mv88e6xxx_lag_can_offload(ds, lag, info))
 | 
				
			||||||
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						id = dsa_lag_id(ds->dst, lag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mv88e6xxx_reg_lock(chip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mv88e6xxx_port_set_trunk(chip, port, true, id);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto err_unlock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mv88e6xxx_lag_sync_masks_map(ds, lag);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto err_clear_trunk;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mv88e6xxx_reg_unlock(chip);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					err_clear_trunk:
 | 
				
			||||||
 | 
						mv88e6xxx_port_set_trunk(chip, port, false, 0);
 | 
				
			||||||
 | 
					err_unlock:
 | 
				
			||||||
 | 
						mv88e6xxx_reg_unlock(chip);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mv88e6xxx_port_lag_leave(struct dsa_switch *ds, int port,
 | 
				
			||||||
 | 
									    struct net_device *lag)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mv88e6xxx_chip *chip = ds->priv;
 | 
				
			||||||
 | 
						int err_sync, err_trunk;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mv88e6xxx_reg_lock(chip);
 | 
				
			||||||
 | 
						err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag);
 | 
				
			||||||
 | 
						err_trunk = mv88e6xxx_port_set_trunk(chip, port, false, 0);
 | 
				
			||||||
 | 
						mv88e6xxx_reg_unlock(chip);
 | 
				
			||||||
 | 
						return err_sync ? : err_trunk;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mv88e6xxx_crosschip_lag_change(struct dsa_switch *ds, int sw_index,
 | 
				
			||||||
 | 
										  int port)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mv88e6xxx_chip *chip = ds->priv;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mv88e6xxx_reg_lock(chip);
 | 
				
			||||||
 | 
						err = mv88e6xxx_lag_sync_masks(ds);
 | 
				
			||||||
 | 
						mv88e6xxx_reg_unlock(chip);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mv88e6xxx_crosschip_lag_join(struct dsa_switch *ds, int sw_index,
 | 
				
			||||||
 | 
										int port, struct net_device *lag,
 | 
				
			||||||
 | 
										struct netdev_lag_upper_info *info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mv88e6xxx_chip *chip = ds->priv;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!mv88e6xxx_lag_can_offload(ds, lag, info))
 | 
				
			||||||
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mv88e6xxx_reg_lock(chip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mv88e6xxx_lag_sync_masks_map(ds, lag);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto unlock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mv88e6xxx_pvt_map(chip, sw_index, port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unlock:
 | 
				
			||||||
 | 
						mv88e6xxx_reg_unlock(chip);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mv88e6xxx_crosschip_lag_leave(struct dsa_switch *ds, int sw_index,
 | 
				
			||||||
 | 
										 int port, struct net_device *lag)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mv88e6xxx_chip *chip = ds->priv;
 | 
				
			||||||
 | 
						int err_sync, err_pvt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mv88e6xxx_reg_lock(chip);
 | 
				
			||||||
 | 
						err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag);
 | 
				
			||||||
 | 
						err_pvt = mv88e6xxx_pvt_map(chip, sw_index, port);
 | 
				
			||||||
 | 
						mv88e6xxx_reg_unlock(chip);
 | 
				
			||||||
 | 
						return err_sync ? : err_pvt;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
 | 
					static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
 | 
				
			||||||
	.get_tag_protocol	= mv88e6xxx_get_tag_protocol,
 | 
						.get_tag_protocol	= mv88e6xxx_get_tag_protocol,
 | 
				
			||||||
	.setup			= mv88e6xxx_setup,
 | 
						.setup			= mv88e6xxx_setup,
 | 
				
			||||||
| 
						 | 
					@ -5416,6 +5698,12 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
 | 
				
			||||||
	.devlink_param_get	= mv88e6xxx_devlink_param_get,
 | 
						.devlink_param_get	= mv88e6xxx_devlink_param_get,
 | 
				
			||||||
	.devlink_param_set	= mv88e6xxx_devlink_param_set,
 | 
						.devlink_param_set	= mv88e6xxx_devlink_param_set,
 | 
				
			||||||
	.devlink_info_get	= mv88e6xxx_devlink_info_get,
 | 
						.devlink_info_get	= mv88e6xxx_devlink_info_get,
 | 
				
			||||||
 | 
						.port_lag_change	= mv88e6xxx_port_lag_change,
 | 
				
			||||||
 | 
						.port_lag_join		= mv88e6xxx_port_lag_join,
 | 
				
			||||||
 | 
						.port_lag_leave		= mv88e6xxx_port_lag_leave,
 | 
				
			||||||
 | 
						.crosschip_lag_change	= mv88e6xxx_crosschip_lag_change,
 | 
				
			||||||
 | 
						.crosschip_lag_join	= mv88e6xxx_crosschip_lag_join,
 | 
				
			||||||
 | 
						.crosschip_lag_leave	= mv88e6xxx_crosschip_lag_leave,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
 | 
					static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
 | 
				
			||||||
| 
						 | 
					@ -5435,6 +5723,12 @@ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
 | 
				
			||||||
	ds->ageing_time_min = chip->info->age_time_coeff;
 | 
						ds->ageing_time_min = chip->info->age_time_coeff;
 | 
				
			||||||
	ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
 | 
						ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Some chips support up to 32, but that requires enabling the
 | 
				
			||||||
 | 
						 * 5-bit port mode, which we do not support. 640k^W16 ought to
 | 
				
			||||||
 | 
						 * be enough for anyone.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						ds->num_lag_ids = 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev_set_drvdata(dev, ds);
 | 
						dev_set_drvdata(dev, ds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return dsa_register_switch(ds);
 | 
						return dsa_register_switch(ds);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,8 +126,8 @@ int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Offset 0x07: Trunk Mask Table register */
 | 
					/* Offset 0x07: Trunk Mask Table register */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
 | 
					int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
 | 
				
			||||||
					 bool hash, u16 mask)
 | 
									  bool hash, u16 mask)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	u16 val = (num << 12) | (mask & mv88e6xxx_port_mask(chip));
 | 
						u16 val = (num << 12) | (mask & mv88e6xxx_port_mask(chip));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -140,8 +140,8 @@ static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Offset 0x08: Trunk Mapping Table register */
 | 
					/* Offset 0x08: Trunk Mapping Table register */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
 | 
					int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
 | 
				
			||||||
					    u16 map)
 | 
									     u16 map)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
 | 
						const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
 | 
				
			||||||
	u16 val = (id << 11) | (map & port_mask);
 | 
						u16 val = (id << 11) | (map & port_mask);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,6 +101,7 @@
 | 
				
			||||||
#define MV88E6XXX_G2_PVT_ADDR_OP_WRITE_PVLAN	0x3000
 | 
					#define MV88E6XXX_G2_PVT_ADDR_OP_WRITE_PVLAN	0x3000
 | 
				
			||||||
#define MV88E6XXX_G2_PVT_ADDR_OP_READ		0x4000
 | 
					#define MV88E6XXX_G2_PVT_ADDR_OP_READ		0x4000
 | 
				
			||||||
#define MV88E6XXX_G2_PVT_ADDR_PTR_MASK		0x01ff
 | 
					#define MV88E6XXX_G2_PVT_ADDR_PTR_MASK		0x01ff
 | 
				
			||||||
 | 
					#define MV88E6XXX_G2_PVT_ADRR_DEV_TRUNK		0x1f
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Offset 0x0C: Cross-chip Port VLAN Data Register */
 | 
					/* Offset 0x0C: Cross-chip Port VLAN Data Register */
 | 
				
			||||||
#define MV88E6XXX_G2_PVT_DATA		0x0c
 | 
					#define MV88E6XXX_G2_PVT_DATA		0x0c
 | 
				
			||||||
| 
						 | 
					@ -345,6 +346,10 @@ int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip);
 | 
					int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
 | 
				
			||||||
 | 
									  bool hash, u16 mask);
 | 
				
			||||||
 | 
					int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
 | 
				
			||||||
 | 
									     u16 map);
 | 
				
			||||||
int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip);
 | 
					int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target,
 | 
					int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -851,6 +851,27 @@ int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
 | 
				
			||||||
	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val);
 | 
						return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port,
 | 
				
			||||||
 | 
								     bool trunk, u8 id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u16 val;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1, &val);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						val &= ~MV88E6XXX_PORT_CTL1_TRUNK_ID_MASK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (trunk)
 | 
				
			||||||
 | 
							val |= MV88E6XXX_PORT_CTL1_TRUNK_PORT |
 | 
				
			||||||
 | 
								(id << MV88E6XXX_PORT_CTL1_TRUNK_ID_SHIFT);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							val &= ~MV88E6XXX_PORT_CTL1_TRUNK_PORT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Offset 0x06: Port Based VLAN Map */
 | 
					/* Offset 0x06: Port Based VLAN Map */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
 | 
					int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -168,6 +168,9 @@
 | 
				
			||||||
/* Offset 0x05: Port Control 1 */
 | 
					/* Offset 0x05: Port Control 1 */
 | 
				
			||||||
#define MV88E6XXX_PORT_CTL1			0x05
 | 
					#define MV88E6XXX_PORT_CTL1			0x05
 | 
				
			||||||
#define MV88E6XXX_PORT_CTL1_MESSAGE_PORT	0x8000
 | 
					#define MV88E6XXX_PORT_CTL1_MESSAGE_PORT	0x8000
 | 
				
			||||||
 | 
					#define MV88E6XXX_PORT_CTL1_TRUNK_PORT		0x4000
 | 
				
			||||||
 | 
					#define MV88E6XXX_PORT_CTL1_TRUNK_ID_MASK	0x0f00
 | 
				
			||||||
 | 
					#define MV88E6XXX_PORT_CTL1_TRUNK_ID_SHIFT	8
 | 
				
			||||||
#define MV88E6XXX_PORT_CTL1_FID_11_4_MASK	0x00ff
 | 
					#define MV88E6XXX_PORT_CTL1_FID_11_4_MASK	0x00ff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Offset 0x06: Port Based VLAN Map */
 | 
					/* Offset 0x06: Port Based VLAN Map */
 | 
				
			||||||
| 
						 | 
					@ -351,6 +354,8 @@ int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
 | 
				
			||||||
				  u16 etype);
 | 
									  u16 etype);
 | 
				
			||||||
int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
 | 
					int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
 | 
				
			||||||
				    bool message_port);
 | 
									    bool message_port);
 | 
				
			||||||
 | 
					int mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port,
 | 
				
			||||||
 | 
								     bool trunk, u8 id);
 | 
				
			||||||
int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port,
 | 
					int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port,
 | 
				
			||||||
				  size_t size);
 | 
									  size_t size);
 | 
				
			||||||
int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port);
 | 
					int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue