diff options
Diffstat (limited to 'drivers/net/dsa/microchip')
| -rw-r--r-- | drivers/net/dsa/microchip/ksz8795.c | 82 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz8795_reg.h | 4 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz_common.c | 4 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz_common.h | 9 | 
4 files changed, 77 insertions, 22 deletions
| diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index 560f6843bb65..c5142f86a3c7 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -687,8 +687,8 @@ static void ksz8_r_vlan_entries(struct ksz_device *dev, u16 addr)  	shifts = ksz8->shifts;  	ksz8_r_table(dev, TABLE_VLAN, addr, &data); -	addr *= dev->phy_port_cnt; -	for (i = 0; i < dev->phy_port_cnt; i++) { +	addr *= 4; +	for (i = 0; i < 4; i++) {  		dev->vlan_cache[addr + i].table[0] = (u16)data;  		data >>= shifts[VLAN_TABLE];  	} @@ -702,7 +702,7 @@ static void ksz8_r_vlan_table(struct ksz_device *dev, u16 vid, u16 *vlan)  	u64 buf;  	data = (u16 *)&buf; -	addr = vid / dev->phy_port_cnt; +	addr = vid / 4;  	index = vid & 3;  	ksz8_r_table(dev, TABLE_VLAN, addr, &buf);  	*vlan = data[index]; @@ -716,7 +716,7 @@ static void ksz8_w_vlan_table(struct ksz_device *dev, u16 vid, u16 vlan)  	u64 buf;  	data = (u16 *)&buf; -	addr = vid / dev->phy_port_cnt; +	addr = vid / 4;  	index = vid & 3;  	ksz8_r_table(dev, TABLE_VLAN, addr, &buf);  	data[index] = vlan; @@ -1119,24 +1119,67 @@ static int ksz8_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag,  	if (ksz_is_ksz88x3(dev))  		return -ENOTSUPP; +	/* Discard packets with VID not enabled on the switch */  	ksz_cfg(dev, S_MIRROR_CTRL, SW_VLAN_ENABLE, flag); +	/* Discard packets with VID not enabled on the ingress port */ +	for (port = 0; port < dev->phy_port_cnt; ++port) +		ksz_port_cfg(dev, port, REG_PORT_CTRL_2, PORT_INGRESS_FILTER, +			     flag); +  	return 0;  } +static void ksz8_port_enable_pvid(struct ksz_device *dev, int port, bool state) +{ +	if (ksz_is_ksz88x3(dev)) { +		ksz_cfg(dev, REG_SW_INSERT_SRC_PVID, +			0x03 << (4 - 2 * port), state); +	} else { +		ksz_pwrite8(dev, port, REG_PORT_CTRL_12, state ? 0x0f : 0x00); +	} +} +  static int ksz8_port_vlan_add(struct dsa_switch *ds, int port,  			      const struct switchdev_obj_port_vlan *vlan,  			      struct netlink_ext_ack *extack)  {  	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;  	struct ksz_device *dev = ds->priv; +	struct ksz_port *p = &dev->ports[port];  	u16 data, new_pvid = 0;  	u8 fid, member, valid;  	if (ksz_is_ksz88x3(dev))  		return -ENOTSUPP; -	ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged); +	/* If a VLAN is added with untagged flag different from the +	 * port's Remove Tag flag, we need to change the latter. +	 * Ignore VID 0, which is always untagged. +	 * Ignore CPU port, which will always be tagged. +	 */ +	if (untagged != p->remove_tag && vlan->vid != 0 && +	    port != dev->cpu_port) { +		unsigned int vid; + +		/* Reject attempts to add a VLAN that requires the +		 * Remove Tag flag to be changed, unless there are no +		 * other VLANs currently configured. +		 */ +		for (vid = 1; vid < dev->num_vlans; ++vid) { +			/* Skip the VID we are going to add or reconfigure */ +			if (vid == vlan->vid) +				continue; + +			ksz8_from_vlan(dev, dev->vlan_cache[vid].table[0], +				       &fid, &member, &valid); +			if (valid && (member & BIT(port))) +				return -EINVAL; +		} + +		ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged); +		p->remove_tag = untagged; +	}  	ksz8_r_vlan_table(dev, vlan->vid, &data);  	ksz8_from_vlan(dev, data, &fid, &member, &valid); @@ -1160,9 +1203,11 @@ static int ksz8_port_vlan_add(struct dsa_switch *ds, int port,  		u16 vid;  		ksz_pread16(dev, port, REG_PORT_CTRL_VID, &vid); -		vid &= 0xfff; +		vid &= ~VLAN_VID_MASK;  		vid |= new_pvid;  		ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, vid); + +		ksz8_port_enable_pvid(dev, port, true);  	}  	return 0; @@ -1171,9 +1216,8 @@ static int ksz8_port_vlan_add(struct dsa_switch *ds, int port,  static int ksz8_port_vlan_del(struct dsa_switch *ds, int port,  			      const struct switchdev_obj_port_vlan *vlan)  { -	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;  	struct ksz_device *dev = ds->priv; -	u16 data, pvid, new_pvid = 0; +	u16 data, pvid;  	u8 fid, member, valid;  	if (ksz_is_ksz88x3(dev)) @@ -1182,8 +1226,6 @@ static int ksz8_port_vlan_del(struct dsa_switch *ds, int port,  	ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid);  	pvid = pvid & 0xFFF; -	ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged); -  	ksz8_r_vlan_table(dev, vlan->vid, &data);  	ksz8_from_vlan(dev, data, &fid, &member, &valid); @@ -1195,14 +1237,11 @@ static int ksz8_port_vlan_del(struct dsa_switch *ds, int port,  		valid = 0;  	} -	if (pvid == vlan->vid) -		new_pvid = 1; -  	ksz8_to_vlan(dev, fid, member, valid, &data);  	ksz8_w_vlan_table(dev, vlan->vid, data); -	if (new_pvid != pvid) -		ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, pvid); +	if (pvid == vlan->vid) +		ksz8_port_enable_pvid(dev, port, false);  	return 0;  } @@ -1435,6 +1474,9 @@ static int ksz8_setup(struct dsa_switch *ds)  	ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false); +	if (!ksz_is_ksz88x3(dev)) +		ksz_cfg(dev, REG_SW_CTRL_19, SW_INS_TAG_ENABLE, true); +  	/* set broadcast storm protection 10% rate */  	regmap_update_bits(dev->regmap[1], S_REPLACE_VID_CTRL,  			   BROADCAST_STORM_RATE, @@ -1717,6 +1759,16 @@ static int ksz8_switch_init(struct ksz_device *dev)  	/* set the real number of ports */  	dev->ds->num_ports = dev->port_cnt; +	/* We rely on software untagging on the CPU port, so that we +	 * can support both tagged and untagged VLANs +	 */ +	dev->ds->untag_bridge_pvid = true; + +	/* VLAN filtering is partly controlled by the global VLAN +	 * Enable flag +	 */ +	dev->ds->vlan_filtering_is_global = true; +  	return 0;  } diff --git a/drivers/net/dsa/microchip/ksz8795_reg.h b/drivers/net/dsa/microchip/ksz8795_reg.h index a32355624f31..6b40bc25f7ff 100644 --- a/drivers/net/dsa/microchip/ksz8795_reg.h +++ b/drivers/net/dsa/microchip/ksz8795_reg.h @@ -631,6 +631,10 @@  #define REG_PORT_4_OUT_RATE_3		0xEE  #define REG_PORT_5_OUT_RATE_3		0xFE +/* 88x3 specific */ + +#define REG_SW_INSERT_SRC_PVID		0xC2 +  /* PME */  #define SW_PME_OUTPUT_ENABLE		BIT(1) diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index a7e5ac60baef..1542bfb8b5e5 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -419,8 +419,10 @@ int ksz_switch_register(struct ksz_device *dev,  				if (of_property_read_u32(port, "reg",  							 &port_num))  					continue; -				if (!(dev->port_mask & BIT(port_num))) +				if (!(dev->port_mask & BIT(port_num))) { +					of_node_put(port);  					return -EINVAL; +				}  				of_get_phy_mode(port,  						&dev->ports[port_num].interface);  			} diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 2e6bfd333f50..1597c63988b4 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -27,6 +27,7 @@ struct ksz_port_mib {  struct ksz_port {  	u16 member;  	u16 vid_member; +	bool remove_tag;		/* Remove Tag flag set, for ksz8795 only */  	int stp_state;  	struct phy_device phydev; @@ -205,12 +206,8 @@ static inline int ksz_read64(struct ksz_device *dev, u32 reg, u64 *val)  	int ret;  	ret = regmap_bulk_read(dev->regmap[2], reg, value, 2); -	if (!ret) { -		/* Ick! ToDo: Add 64bit R/W to regmap on 32bit systems */ -		value[0] = swab32(value[0]); -		value[1] = swab32(value[1]); -		*val = swab64((u64)*value); -	} +	if (!ret) +		*val = (u64)value[0] << 32 | value[1];  	return ret;  } |