diff options
Diffstat (limited to 'drivers/net/dsa/microchip')
| -rw-r--r-- | drivers/net/dsa/microchip/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/Makefile | 2 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz8.h | 9 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz8795.c | 251 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz8795_reg.h | 10 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz9477.c | 6 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz9477_tc_flower.c | 3 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz_common.c | 224 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz_common.h | 16 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz_dcb.c | 819 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz_dcb.h | 23 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz_spi.c | 8 | 
12 files changed, 1162 insertions, 211 deletions
diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig index 394ca8678d2b..c1b906c05a02 100644 --- a/drivers/net/dsa/microchip/Kconfig +++ b/drivers/net/dsa/microchip/Kconfig @@ -4,6 +4,8 @@ menuconfig NET_DSA_MICROCHIP_KSZ_COMMON  	depends on NET_DSA  	select NET_DSA_TAG_KSZ  	select NET_DSA_TAG_NONE +	select NET_IEEE8021Q_HELPERS +	select DCB  	help  	  This driver adds support for Microchip KSZ9477 series switch and  	  KSZ8795/KSZ88x3 switch chips. diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile index 49459a50dbc8..1cfba1ec9355 100644 --- a/drivers/net/dsa/microchip/Makefile +++ b/drivers/net/dsa/microchip/Makefile @@ -1,6 +1,6 @@  # SPDX-License-Identifier: GPL-2.0-only  obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON)	+= ksz_switch.o -ksz_switch-objs := ksz_common.o +ksz_switch-objs := ksz_common.o ksz_dcb.o  ksz_switch-objs += ksz9477.o ksz9477_acl.o ksz9477_tc_flower.o  ksz_switch-objs += ksz8795.o  ksz_switch-objs += lan937x_main.o diff --git a/drivers/net/dsa/microchip/ksz8.h b/drivers/net/dsa/microchip/ksz8.h index 1a5225264e6a..ae43077e76c3 100644 --- a/drivers/net/dsa/microchip/ksz8.h +++ b/drivers/net/dsa/microchip/ksz8.h @@ -19,8 +19,6 @@ void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port);  void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port);  int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val);  int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val); -int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr, -			 u8 *fid, u8 *src_port, u8 *timestamp, u16 *entries);  void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt);  void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,  		    u64 *dropped, u64 *cnt); @@ -56,9 +54,10 @@ int ksz8_reset_switch(struct ksz_device *dev);  int ksz8_switch_init(struct ksz_device *dev);  void ksz8_switch_exit(struct ksz_device *dev);  int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu); -void ksz8_phylink_mac_link_up(struct ksz_device *dev, int port, -			      unsigned int mode, phy_interface_t interface, -			      struct phy_device *phydev, int speed, int duplex, +void ksz8_phylink_mac_link_up(struct phylink_config *config, +			      struct phy_device *phydev, unsigned int mode, +			      phy_interface_t interface, int speed, int duplex,  			      bool tx_pause, bool rx_pause); +int ksz8_all_queues_split(struct ksz_device *dev, int queues);  #endif diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index 14923535ca7e..d27b9c36d73f 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -127,37 +127,71 @@ int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu)  	return -EOPNOTSUPP;  } -static void ksz8795_set_prio_queue(struct ksz_device *dev, int port, int queue) +static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues)  { -	u8 hi, lo; +	u8 mask_4q, mask_2q; +	u8 reg_4q, reg_2q; +	u8 data_4q = 0; +	u8 data_2q = 0; +	int ret; -	/* Number of queues can only be 1, 2, or 4. */ -	switch (queue) { -	case 4: -	case 3: -		queue = PORT_QUEUE_SPLIT_4; -		break; -	case 2: -		queue = PORT_QUEUE_SPLIT_2; -		break; -	default: -		queue = PORT_QUEUE_SPLIT_1; +	if (ksz_is_ksz88x3(dev)) { +		mask_4q = KSZ8873_PORT_4QUEUE_SPLIT_EN; +		mask_2q = KSZ8873_PORT_2QUEUE_SPLIT_EN; +		reg_4q = REG_PORT_CTRL_0; +		reg_2q = REG_PORT_CTRL_2; + +		/* KSZ8795 family switches have Weighted Fair Queueing (WFQ) +		 * enabled by default. Enable it for KSZ8873 family switches +		 * too. Default value for KSZ8873 family is strict priority, +		 * which should be enabled by using TC_SETUP_QDISC_ETS, not +		 * by default. +		 */ +		ret = ksz_rmw8(dev, REG_SW_CTRL_3, WEIGHTED_FAIR_QUEUE_ENABLE, +			       WEIGHTED_FAIR_QUEUE_ENABLE); +		if (ret) +			return ret; +	} else { +		mask_4q = KSZ8795_PORT_4QUEUE_SPLIT_EN; +		mask_2q = KSZ8795_PORT_2QUEUE_SPLIT_EN; +		reg_4q = REG_PORT_CTRL_13; +		reg_2q = REG_PORT_CTRL_0; + +		/* TODO: this is legacy from initial KSZ8795 driver, should be +		 * moved to appropriate place in the future. +		 */ +		ret = ksz_rmw8(dev, REG_SW_CTRL_19, +			       SW_OUT_RATE_LIMIT_QUEUE_BASED, +			       SW_OUT_RATE_LIMIT_QUEUE_BASED); +		if (ret) +			return ret;  	} -	ksz_pread8(dev, port, REG_PORT_CTRL_0, &lo); -	ksz_pread8(dev, port, P_DROP_TAG_CTRL, &hi); -	lo &= ~PORT_QUEUE_SPLIT_L; -	if (queue & PORT_QUEUE_SPLIT_2) -		lo |= PORT_QUEUE_SPLIT_L; -	hi &= ~PORT_QUEUE_SPLIT_H; -	if (queue & PORT_QUEUE_SPLIT_4) -		hi |= PORT_QUEUE_SPLIT_H; -	ksz_pwrite8(dev, port, REG_PORT_CTRL_0, lo); -	ksz_pwrite8(dev, port, P_DROP_TAG_CTRL, hi); - -	/* Default is port based for egress rate limit. */ -	if (queue != PORT_QUEUE_SPLIT_1) -		ksz_cfg(dev, REG_SW_CTRL_19, SW_OUT_RATE_LIMIT_QUEUE_BASED, -			true); + +	if (queues == 4) +		data_4q = mask_4q; +	else if (queues == 2) +		data_2q = mask_2q; + +	ret = ksz_prmw8(dev, port, reg_4q, mask_4q, data_4q); +	if (ret) +		return ret; + +	return ksz_prmw8(dev, port, reg_2q, mask_2q, data_2q); +} + +int ksz8_all_queues_split(struct ksz_device *dev, int queues) +{ +	struct dsa_switch *ds = dev->ds; +	const struct dsa_port *dp; + +	dsa_switch_for_each_port(dp, ds) { +		int ret = ksz8_port_queue_split(dev, dp->index, queues); + +		if (ret) +			return ret; +	} + +	return 0;  }  void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt) @@ -385,39 +419,39 @@ static int ksz8_valid_dyn_entry(struct ksz_device *dev, u8 *data)  	int timeout = 100;  	const u32 *masks;  	const u16 *regs; +	int ret;  	masks = dev->info->masks;  	regs = dev->info->regs;  	do { -		ksz_read8(dev, regs[REG_IND_DATA_CHECK], data); +		ret = ksz_read8(dev, regs[REG_IND_DATA_CHECK], data); +		if (ret) +			return ret; +  		timeout--;  	} while ((*data & masks[DYNAMIC_MAC_TABLE_NOT_READY]) && timeout);  	/* Entry is not ready for accessing. */ -	if (*data & masks[DYNAMIC_MAC_TABLE_NOT_READY]) { -		return -EAGAIN; -	/* Entry is ready for accessing. */ -	} else { -		ksz_read8(dev, regs[REG_IND_DATA_8], data); +	if (*data & masks[DYNAMIC_MAC_TABLE_NOT_READY]) +		return -ETIMEDOUT; -		/* There is no valid entry in the table. */ -		if (*data & masks[DYNAMIC_MAC_TABLE_MAC_EMPTY]) -			return -ENXIO; -	} -	return 0; +	/* Entry is ready for accessing. */ +	return ksz_read8(dev, regs[REG_IND_DATA_8], data);  } -int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr, -			 u8 *fid, u8 *src_port, u8 *timestamp, u16 *entries) +static int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr, +				u8 *fid, u8 *src_port, u16 *entries)  {  	u32 data_hi, data_lo;  	const u8 *shifts;  	const u32 *masks;  	const u16 *regs;  	u16 ctrl_addr; +	u64 buf = 0;  	u8 data; -	int rc; +	int cnt; +	int ret;  	shifts = dev->info->shifts;  	masks = dev->info->masks; @@ -426,49 +460,50 @@ int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr,  	ctrl_addr = IND_ACC_TABLE(TABLE_DYNAMIC_MAC | TABLE_READ) | addr;  	mutex_lock(&dev->alu_mutex); -	ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); +	ret = ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); +	if (ret) +		goto unlock_alu; + +	ret = ksz8_valid_dyn_entry(dev, &data); +	if (ret) +		goto unlock_alu; -	rc = ksz8_valid_dyn_entry(dev, &data); -	if (rc == -EAGAIN) { -		if (addr == 0) -			*entries = 0; -	} else if (rc == -ENXIO) { +	if (data & masks[DYNAMIC_MAC_TABLE_MAC_EMPTY]) {  		*entries = 0; -	/* At least one valid entry in the table. */ -	} else { -		u64 buf = 0; -		int cnt; - -		ksz_read64(dev, regs[REG_IND_DATA_HI], &buf); -		data_hi = (u32)(buf >> 32); -		data_lo = (u32)buf; - -		/* Check out how many valid entry in the table. */ -		cnt = data & masks[DYNAMIC_MAC_TABLE_ENTRIES_H]; -		cnt <<= shifts[DYNAMIC_MAC_ENTRIES_H]; -		cnt |= (data_hi & masks[DYNAMIC_MAC_TABLE_ENTRIES]) >> -			shifts[DYNAMIC_MAC_ENTRIES]; -		*entries = cnt + 1; - -		*fid = (data_hi & masks[DYNAMIC_MAC_TABLE_FID]) >> -			shifts[DYNAMIC_MAC_FID]; -		*src_port = (data_hi & masks[DYNAMIC_MAC_TABLE_SRC_PORT]) >> -			shifts[DYNAMIC_MAC_SRC_PORT]; -		*timestamp = (data_hi & masks[DYNAMIC_MAC_TABLE_TIMESTAMP]) >> -			shifts[DYNAMIC_MAC_TIMESTAMP]; - -		mac_addr[5] = (u8)data_lo; -		mac_addr[4] = (u8)(data_lo >> 8); -		mac_addr[3] = (u8)(data_lo >> 16); -		mac_addr[2] = (u8)(data_lo >> 24); - -		mac_addr[1] = (u8)data_hi; -		mac_addr[0] = (u8)(data_hi >> 8); -		rc = 0; +		goto unlock_alu;  	} + +	ret = ksz_read64(dev, regs[REG_IND_DATA_HI], &buf); +	if (ret) +		goto unlock_alu; + +	data_hi = (u32)(buf >> 32); +	data_lo = (u32)buf; + +	/* Check out how many valid entry in the table. */ +	cnt = data & masks[DYNAMIC_MAC_TABLE_ENTRIES_H]; +	cnt <<= shifts[DYNAMIC_MAC_ENTRIES_H]; +	cnt |= (data_hi & masks[DYNAMIC_MAC_TABLE_ENTRIES]) >> +		shifts[DYNAMIC_MAC_ENTRIES]; +	*entries = cnt + 1; + +	*fid = (data_hi & masks[DYNAMIC_MAC_TABLE_FID]) >> +		shifts[DYNAMIC_MAC_FID]; +	*src_port = (data_hi & masks[DYNAMIC_MAC_TABLE_SRC_PORT]) >> +		shifts[DYNAMIC_MAC_SRC_PORT]; + +	mac_addr[5] = (u8)data_lo; +	mac_addr[4] = (u8)(data_lo >> 8); +	mac_addr[3] = (u8)(data_lo >> 16); +	mac_addr[2] = (u8)(data_lo >> 24); + +	mac_addr[1] = (u8)data_hi; +	mac_addr[0] = (u8)(data_hi >> 8); + +unlock_alu:  	mutex_unlock(&dev->alu_mutex); -	return rc; +	return ret;  }  static int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr, @@ -1193,28 +1228,28 @@ void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port)  int ksz8_fdb_dump(struct ksz_device *dev, int port,  		  dsa_fdb_dump_cb_t *cb, void *data)  { -	int ret = 0; -	u16 i = 0; -	u16 entries = 0; -	u8 timestamp = 0; -	u8 fid; -	u8 src_port;  	u8 mac[ETH_ALEN]; +	u8 src_port, fid; +	u16 entries = 0; +	int ret, i; -	do { +	for (i = 0; i < KSZ8_DYN_MAC_ENTRIES; i++) {  		ret = ksz8_r_dyn_mac_table(dev, i, mac, &fid, &src_port, -					   ×tamp, &entries); -		if (!ret && port == src_port) { +					   &entries); +		if (ret) +			return ret; + +		if (i >= entries) +			return 0; + +		if (port == src_port) {  			ret = cb(mac, fid, false, data);  			if (ret) -				break; +				return ret;  		} -		i++; -	} while (i < entries); -	if (i >= entries) -		ret = 0; +	} -	return ret; +	return 0;  }  static int ksz8_add_sta_mac(struct ksz_device *dev, int port, @@ -1512,6 +1547,7 @@ void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port)  {  	struct dsa_switch *ds = dev->ds;  	const u32 *masks; +	int queues;  	u8 member;  	masks = dev->info->masks; @@ -1519,19 +1555,20 @@ void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port)  	/* enable broadcast storm limit */  	ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true); -	if (!ksz_is_ksz88x3(dev)) -		ksz8795_set_prio_queue(dev, port, 4); +	/* For KSZ88x3 enable only one queue by default, otherwise we won't +	 * be able to get rid of PCP prios on Port 2. +	 */ +	if (ksz_is_ksz88x3(dev)) +		queues = 1; +	else +		queues = dev->info->num_tx_queues; -	/* disable DiffServ priority */ -	ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_ENABLE, false); +	ksz8_port_queue_split(dev, port, queues);  	/* replace priority */  	ksz_port_cfg(dev, port, P_802_1P_CTRL,  		     masks[PORT_802_1P_REMAPPING], false); -	/* enable 802.1p priority */ -	ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_ENABLE, true); -  	if (cpu_port)  		member = dsa_user_ports(ds);  	else @@ -1701,11 +1738,15 @@ static void ksz8_cpu_port_link_up(struct ksz_device *dev, int speed, int duplex,  		 SW_10_MBIT, ctrl);  } -void ksz8_phylink_mac_link_up(struct ksz_device *dev, int port, -			      unsigned int mode, phy_interface_t interface, -			      struct phy_device *phydev, int speed, int duplex, +void ksz8_phylink_mac_link_up(struct phylink_config *config, +			      struct phy_device *phydev, unsigned int mode, +			      phy_interface_t interface, int speed, int duplex,  			      bool tx_pause, bool rx_pause)  { +	struct dsa_port *dp = dsa_phylink_to_port(config); +	struct ksz_device *dev = dp->ds->priv; +	int port = dp->index; +  	/* If the port is the CPU port, apply special handling. Only the CPU  	 * port is configured via global registers.  	 */ diff --git a/drivers/net/dsa/microchip/ksz8795_reg.h b/drivers/net/dsa/microchip/ksz8795_reg.h index 7c9341ef73b0..69566a5d9cda 100644 --- a/drivers/net/dsa/microchip/ksz8795_reg.h +++ b/drivers/net/dsa/microchip/ksz8795_reg.h @@ -124,7 +124,8 @@  #define PORT_BASED_PRIO_3		3  #define PORT_INSERT_TAG			BIT(2)  #define PORT_REMOVE_TAG			BIT(1) -#define PORT_QUEUE_SPLIT_L		BIT(0) +#define KSZ8795_PORT_2QUEUE_SPLIT_EN	BIT(0) +#define KSZ8873_PORT_4QUEUE_SPLIT_EN	BIT(0)  #define REG_PORT_1_CTRL_1		0x11  #define REG_PORT_2_CTRL_1		0x21 @@ -143,6 +144,7 @@  #define REG_PORT_4_CTRL_2		0x42  #define REG_PORT_5_CTRL_2		0x52 +#define KSZ8873_PORT_2QUEUE_SPLIT_EN	BIT(7)  #define PORT_INGRESS_FILTER		BIT(6)  #define PORT_DISCARD_NON_VID		BIT(5)  #define PORT_FORCE_FLOW_CTRL		BIT(4) @@ -463,10 +465,7 @@  #define REG_PORT_4_CTRL_13		0xE1  #define REG_PORT_5_CTRL_13		0xF1 -#define PORT_QUEUE_SPLIT_H		BIT(1) -#define PORT_QUEUE_SPLIT_1		0 -#define PORT_QUEUE_SPLIT_2		1 -#define PORT_QUEUE_SPLIT_4		2 +#define KSZ8795_PORT_4QUEUE_SPLIT_EN	BIT(1)  #define PORT_DROP_TAG			BIT(0)  #define REG_PORT_1_CTRL_14		0xB2 @@ -794,5 +793,6 @@  #define TAIL_TAG_LOOKUP			BIT(7)  #define FID_ENTRIES			128 +#define KSZ8_DYN_MAC_ENTRIES		1024  #endif diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 7f745628c84d..f8ad7833f5d9 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -1158,18 +1158,12 @@ void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port)  	/* enable broadcast storm limit */  	ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true); -	/* disable DiffServ priority */ -	ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_PRIO_ENABLE, false); -  	/* replace priority */  	ksz_port_cfg(dev, port, REG_PORT_MRI_MAC_CTRL, PORT_USER_PRIO_CEILING,  		     false);  	ksz9477_port_cfg32(dev, port, REG_PORT_MTI_QUEUE_CTRL_0__4,  			   MTI_PVID_REPLACE, false); -	/* enable 802.1p priority */ -	ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true); -  	/* force flow control for non-PHY ports only */  	ksz_port_cfg(dev, port, REG_PORT_CTRL_0,  		     PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL, diff --git a/drivers/net/dsa/microchip/ksz9477_tc_flower.c b/drivers/net/dsa/microchip/ksz9477_tc_flower.c index 8b2f5be667e0..ca7830ab168a 100644 --- a/drivers/net/dsa/microchip/ksz9477_tc_flower.c +++ b/drivers/net/dsa/microchip/ksz9477_tc_flower.c @@ -124,6 +124,9 @@ static int ksz9477_flower_parse_key(struct ksz_device *dev, int port,  		return -EOPNOTSUPP;  	} +	if (flow_rule_match_has_control_flags(rule, extack)) +		return -EOPNOTSUPP; +  	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC) ||  	    flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {  		ret = ksz9477_flower_parse_key_l2(dev, port, extack, rule, diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 2b510f150dd8..1e0085cd9a9a 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -24,10 +24,12 @@  #include <linux/of_net.h>  #include <linux/micrel_phy.h>  #include <net/dsa.h> +#include <net/ieee8021q.h>  #include <net/pkt_cls.h>  #include <net/switchdev.h>  #include "ksz_common.h" +#include "ksz_dcb.h"  #include "ksz_ptp.h"  #include "ksz8.h"  #include "ksz9477.h" @@ -253,6 +255,28 @@ static const struct ksz_drive_strength ksz8830_drive_strengths[] = {  	{ KSZ8873_DRIVE_STRENGTH_16MA, 16000 },  }; +static void ksz8830_phylink_mac_config(struct phylink_config *config, +				       unsigned int mode, +				       const struct phylink_link_state *state); +static void ksz_phylink_mac_config(struct phylink_config *config, +				   unsigned int mode, +				   const struct phylink_link_state *state); +static void ksz_phylink_mac_link_down(struct phylink_config *config, +				      unsigned int mode, +				      phy_interface_t interface); + +static const struct phylink_mac_ops ksz8830_phylink_mac_ops = { +	.mac_config	= ksz8830_phylink_mac_config, +	.mac_link_down	= ksz_phylink_mac_link_down, +	.mac_link_up	= ksz8_phylink_mac_link_up, +}; + +static const struct phylink_mac_ops ksz8_phylink_mac_ops = { +	.mac_config	= ksz_phylink_mac_config, +	.mac_link_down	= ksz_phylink_mac_link_down, +	.mac_link_up	= ksz8_phylink_mac_link_up, +}; +  static const struct ksz_dev_ops ksz8_dev_ops = {  	.setup = ksz8_setup,  	.get_port_addr = ksz8_get_port_addr, @@ -277,7 +301,6 @@ static const struct ksz_dev_ops ksz8_dev_ops = {  	.mirror_add = ksz8_port_mirror_add,  	.mirror_del = ksz8_port_mirror_del,  	.get_caps = ksz8_get_caps, -	.phylink_mac_link_up = ksz8_phylink_mac_link_up,  	.config_cpu_port = ksz8_config_cpu_port,  	.enable_stp_addr = ksz8_enable_stp_addr,  	.reset = ksz8_reset_switch, @@ -286,13 +309,19 @@ static const struct ksz_dev_ops ksz8_dev_ops = {  	.change_mtu = ksz8_change_mtu,  }; -static void ksz9477_phylink_mac_link_up(struct ksz_device *dev, int port, +static void ksz9477_phylink_mac_link_up(struct phylink_config *config, +					struct phy_device *phydev,  					unsigned int mode,  					phy_interface_t interface, -					struct phy_device *phydev, int speed, -					int duplex, bool tx_pause, +					int speed, int duplex, bool tx_pause,  					bool rx_pause); +static const struct phylink_mac_ops ksz9477_phylink_mac_ops = { +	.mac_config	= ksz_phylink_mac_config, +	.mac_link_down	= ksz_phylink_mac_link_down, +	.mac_link_up	= ksz9477_phylink_mac_link_up, +}; +  static const struct ksz_dev_ops ksz9477_dev_ops = {  	.setup = ksz9477_setup,  	.get_port_addr = ksz9477_get_port_addr, @@ -319,7 +348,6 @@ static const struct ksz_dev_ops ksz9477_dev_ops = {  	.mdb_add = ksz9477_mdb_add,  	.mdb_del = ksz9477_mdb_del,  	.change_mtu = ksz9477_change_mtu, -	.phylink_mac_link_up = ksz9477_phylink_mac_link_up,  	.get_wol = ksz9477_get_wol,  	.set_wol = ksz9477_set_wol,  	.wol_pre_shutdown = ksz9477_wol_pre_shutdown, @@ -331,6 +359,12 @@ static const struct ksz_dev_ops ksz9477_dev_ops = {  	.exit = ksz9477_switch_exit,  }; +static const struct phylink_mac_ops lan937x_phylink_mac_ops = { +	.mac_config	= ksz_phylink_mac_config, +	.mac_link_down	= ksz_phylink_mac_link_down, +	.mac_link_up	= ksz9477_phylink_mac_link_up, +}; +  static const struct ksz_dev_ops lan937x_dev_ops = {  	.setup = lan937x_setup,  	.teardown = lan937x_teardown, @@ -359,7 +393,6 @@ static const struct ksz_dev_ops lan937x_dev_ops = {  	.mdb_add = ksz9477_mdb_add,  	.mdb_del = ksz9477_mdb_del,  	.change_mtu = lan937x_change_mtu, -	.phylink_mac_link_up = ksz9477_phylink_mac_link_up,  	.config_cpu_port = lan937x_config_cpu_port,  	.tc_cbs_set_cinc = lan937x_tc_cbs_set_cinc,  	.enable_stp_addr = ksz9477_enable_stp_addr, @@ -1194,9 +1227,10 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_cnt = 3,		/* total port count */  		.port_nirqs = 3,  		.num_tx_queues = 4, +		.num_ipms = 8,  		.tc_cbs_supported = true, -		.tc_ets_supported = true,  		.ops = &ksz9477_dev_ops, +		.phylink_mac_ops = &ksz9477_phylink_mac_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),  		.reg_mib_cnt = MIB_COUNTER_NUM, @@ -1223,7 +1257,9 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.cpu_ports = 0x10,	/* can be configured as cpu port */  		.port_cnt = 5,		/* total cpu and user ports */  		.num_tx_queues = 4, +		.num_ipms = 4,  		.ops = &ksz8_dev_ops, +		.phylink_mac_ops = &ksz8_phylink_mac_ops,  		.ksz87xx_eee_link_erratum = true,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1262,7 +1298,9 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.cpu_ports = 0x10,	/* can be configured as cpu port */  		.port_cnt = 5,		/* total cpu and user ports */  		.num_tx_queues = 4, +		.num_ipms = 4,  		.ops = &ksz8_dev_ops, +		.phylink_mac_ops = &ksz8_phylink_mac_ops,  		.ksz87xx_eee_link_erratum = true,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1287,7 +1325,9 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.cpu_ports = 0x10,	/* can be configured as cpu port */  		.port_cnt = 5,		/* total cpu and user ports */  		.num_tx_queues = 4, +		.num_ipms = 4,  		.ops = &ksz8_dev_ops, +		.phylink_mac_ops = &ksz8_phylink_mac_ops,  		.ksz87xx_eee_link_erratum = true,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1312,7 +1352,9 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.cpu_ports = 0x4,	/* can be configured as cpu port */  		.port_cnt = 3,  		.num_tx_queues = 4, +		.num_ipms = 4,  		.ops = &ksz8_dev_ops, +		.phylink_mac_ops = &ksz8830_phylink_mac_ops,  		.mib_names = ksz88xx_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz88xx_mib_names),  		.reg_mib_cnt = MIB_COUNTER_NUM, @@ -1336,9 +1378,10 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_cnt = 7,		/* total physical port count */  		.port_nirqs = 4,  		.num_tx_queues = 4, +		.num_ipms = 8,  		.tc_cbs_supported = true, -		.tc_ets_supported = true,  		.ops = &ksz9477_dev_ops, +		.phylink_mac_ops = &ksz9477_phylink_mac_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),  		.reg_mib_cnt = MIB_COUNTER_NUM, @@ -1370,7 +1413,9 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_cnt = 6,		/* total physical port count */  		.port_nirqs = 2,  		.num_tx_queues = 4, +		.num_ipms = 8,  		.ops = &ksz9477_dev_ops, +		.phylink_mac_ops = &ksz9477_phylink_mac_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),  		.reg_mib_cnt = MIB_COUNTER_NUM, @@ -1402,7 +1447,9 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_cnt = 7,		/* total physical port count */  		.port_nirqs = 2,  		.num_tx_queues = 4, +		.num_ipms = 8,  		.ops = &ksz9477_dev_ops, +		.phylink_mac_ops = &ksz9477_phylink_mac_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),  		.reg_mib_cnt = MIB_COUNTER_NUM, @@ -1432,7 +1479,9 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_cnt = 3,		/* total port count */  		.port_nirqs = 2,  		.num_tx_queues = 4, +		.num_ipms = 8,  		.ops = &ksz9477_dev_ops, +		.phylink_mac_ops = &ksz9477_phylink_mac_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),  		.reg_mib_cnt = MIB_COUNTER_NUM, @@ -1458,9 +1507,10 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_cnt = 3,		/* total port count */  		.port_nirqs = 3,  		.num_tx_queues = 4, +		.num_ipms = 8,  		.tc_cbs_supported = true, -		.tc_ets_supported = true,  		.ops = &ksz9477_dev_ops, +		.phylink_mac_ops = &ksz9477_phylink_mac_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),  		.reg_mib_cnt = MIB_COUNTER_NUM, @@ -1486,9 +1536,10 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_cnt = 7,		/* total port count */  		.port_nirqs = 3,  		.num_tx_queues = 4, +		.num_ipms = 8,  		.tc_cbs_supported = true, -		.tc_ets_supported = true,  		.ops = &ksz9477_dev_ops, +		.phylink_mac_ops = &ksz9477_phylink_mac_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),  		.reg_mib_cnt = MIB_COUNTER_NUM, @@ -1519,8 +1570,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_cnt = 7,		/* total physical port count */  		.port_nirqs = 3,  		.num_tx_queues = 4, +		.num_ipms = 8,  		.tc_cbs_supported = true, -		.tc_ets_supported = true,  		.ops = &ksz9477_dev_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1551,9 +1602,10 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_cnt = 5,		/* total physical port count */  		.port_nirqs = 6,  		.num_tx_queues = 8, +		.num_ipms = 8,  		.tc_cbs_supported = true, -		.tc_ets_supported = true,  		.ops = &lan937x_dev_ops, +		.phylink_mac_ops = &lan937x_phylink_mac_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),  		.reg_mib_cnt = MIB_COUNTER_NUM, @@ -1578,9 +1630,10 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_cnt = 6,		/* total physical port count */  		.port_nirqs = 6,  		.num_tx_queues = 8, +		.num_ipms = 8,  		.tc_cbs_supported = true, -		.tc_ets_supported = true,  		.ops = &lan937x_dev_ops, +		.phylink_mac_ops = &lan937x_phylink_mac_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),  		.reg_mib_cnt = MIB_COUNTER_NUM, @@ -1605,9 +1658,10 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_cnt = 8,		/* total physical port count */  		.port_nirqs = 6,  		.num_tx_queues = 8, +		.num_ipms = 8,  		.tc_cbs_supported = true, -		.tc_ets_supported = true,  		.ops = &lan937x_dev_ops, +		.phylink_mac_ops = &lan937x_phylink_mac_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),  		.reg_mib_cnt = MIB_COUNTER_NUM, @@ -1636,9 +1690,10 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_cnt = 5,		/* total physical port count */  		.port_nirqs = 6,  		.num_tx_queues = 8, +		.num_ipms = 8,  		.tc_cbs_supported = true, -		.tc_ets_supported = true,  		.ops = &lan937x_dev_ops, +		.phylink_mac_ops = &lan937x_phylink_mac_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),  		.reg_mib_cnt = MIB_COUNTER_NUM, @@ -1667,9 +1722,10 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_cnt = 8,		/* total physical port count */  		.port_nirqs = 6,  		.num_tx_queues = 8, +		.num_ipms = 8,  		.tc_cbs_supported = true, -		.tc_ets_supported = true,  		.ops = &lan937x_dev_ops, +		.phylink_mac_ops = &lan937x_phylink_mac_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),  		.reg_mib_cnt = MIB_COUNTER_NUM, @@ -2304,6 +2360,7 @@ static int ksz_setup(struct dsa_switch *ds)  	ksz_init_mib_timer(dev);  	ds->configure_vlan_while_not_filtering = false; +	ds->dscp_prio_mapping_is_global = true;  	if (dev->dev_ops->setup) {  		ret = dev->dev_ops->setup(ds); @@ -2347,6 +2404,10 @@ static int ksz_setup(struct dsa_switch *ds)  		goto out_ptp_clock_unregister;  	} +	ret = ksz_dcb_init(dev); +	if (ret) +		goto out_ptp_clock_unregister; +  	/* start switch */  	regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL],  			   SW_START, SW_START); @@ -2523,14 +2584,15 @@ static u32 ksz_get_phy_flags(struct dsa_switch *ds, int port)  	return 0;  } -static void ksz_mac_link_down(struct dsa_switch *ds, int port, -			      unsigned int mode, phy_interface_t interface) +static void ksz_phylink_mac_link_down(struct phylink_config *config, +				      unsigned int mode, +				      phy_interface_t interface)  { -	struct ksz_device *dev = ds->priv; -	struct ksz_port *p = &dev->ports[port]; +	struct dsa_port *dp = dsa_phylink_to_port(config); +	struct ksz_device *dev = dp->ds->priv;  	/* Read all MIB counters when the link is going down. */ -	p->read = true; +	dev->ports[dp->index].read = true;  	/* timer started */  	if (dev->mib_read_interval)  		schedule_delayed_work(&dev->mib_read, 0); @@ -2660,9 +2722,33 @@ static int ksz_port_mdb_del(struct dsa_switch *ds, int port,  	return dev->dev_ops->mdb_del(dev, port, mdb, db);  } +static int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, +						  int port) +{ +	u32 queue_map = 0; +	int ipm; + +	for (ipm = 0; ipm < dev->info->num_ipms; ipm++) { +		int queue; + +		/* Traffic Type (TT) is corresponding to the Internal Priority +		 * Map (IPM) in the switch. Traffic Class (TC) is +		 * corresponding to the queue in the switch. +		 */ +		queue = ieee8021q_tt_to_tc(ipm, dev->info->num_tx_queues); +		if (queue < 0) +			return queue; + +		queue_map |= queue << (ipm * KSZ9477_PORT_TC_MAP_S); +	} + +	return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map); +} +  static int ksz_port_setup(struct dsa_switch *ds, int port)  {  	struct ksz_device *dev = ds->priv; +	int ret;  	if (!dsa_is_user_port(ds, port))  		return 0; @@ -2670,11 +2756,17 @@ static int ksz_port_setup(struct dsa_switch *ds, int port)  	/* setup user port */  	dev->dev_ops->port_setup(dev, port, false); +	if (!is_ksz8(dev)) { +		ret = ksz9477_set_default_prio_queue_mapping(dev, port); +		if (ret) +			return ret; +	} +  	/* port_stp_state_set() will be called after to enable the port so  	 * there is no need to do anything.  	 */ -	return 0; +	return ksz_dcb_init_port(dev, port);  }  void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) @@ -3065,16 +3157,23 @@ phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit)  	return interface;  } -static void ksz_phylink_mac_config(struct dsa_switch *ds, int port, +static void ksz8830_phylink_mac_config(struct phylink_config *config, +				       unsigned int mode, +				       const struct phylink_link_state *state) +{ +	struct dsa_port *dp = dsa_phylink_to_port(config); +	struct ksz_device *dev = dp->ds->priv; + +	dev->ports[dp->index].manual_flow = !(state->pause & MLO_PAUSE_AN); +} + +static void ksz_phylink_mac_config(struct phylink_config *config,  				   unsigned int mode,  				   const struct phylink_link_state *state)  { -	struct ksz_device *dev = ds->priv; - -	if (ksz_is_ksz88x3(dev)) { -		dev->ports[port].manual_flow = !(state->pause & MLO_PAUSE_AN); -		return; -	} +	struct dsa_port *dp = dsa_phylink_to_port(config); +	struct ksz_device *dev = dp->ds->priv; +	int port = dp->index;  	/* Internal PHYs */  	if (dev->info->internal_phy[port]) @@ -3087,9 +3186,6 @@ static void ksz_phylink_mac_config(struct dsa_switch *ds, int port,  	ksz_set_xmii(dev, port, state->interface); -	if (dev->dev_ops->phylink_mac_config) -		dev->dev_ops->phylink_mac_config(dev, port, mode, state); -  	if (dev->dev_ops->setup_rgmii_delay)  		dev->dev_ops->setup_rgmii_delay(dev, port);  } @@ -3187,13 +3283,16 @@ static void ksz_duplex_flowctrl(struct ksz_device *dev, int port, int duplex,  	ksz_prmw8(dev, port, regs[P_XMII_CTRL_0], mask, val);  } -static void ksz9477_phylink_mac_link_up(struct ksz_device *dev, int port, +static void ksz9477_phylink_mac_link_up(struct phylink_config *config, +					struct phy_device *phydev,  					unsigned int mode,  					phy_interface_t interface, -					struct phy_device *phydev, int speed, -					int duplex, bool tx_pause, +					int speed, int duplex, bool tx_pause,  					bool rx_pause)  { +	struct dsa_port *dp = dsa_phylink_to_port(config); +	struct ksz_device *dev = dp->ds->priv; +	int port = dp->index;  	struct ksz_port *p;  	p = &dev->ports[port]; @@ -3209,18 +3308,6 @@ static void ksz9477_phylink_mac_link_up(struct ksz_device *dev, int port,  	ksz_duplex_flowctrl(dev, port, duplex, tx_pause, rx_pause);  } -static void ksz_phylink_mac_link_up(struct dsa_switch *ds, int port, -				    unsigned int mode, -				    phy_interface_t interface, -				    struct phy_device *phydev, int speed, -				    int duplex, bool tx_pause, bool rx_pause) -{ -	struct ksz_device *dev = ds->priv; - -	dev->dev_ops->phylink_mac_link_up(dev, port, mode, interface, phydev, -					  speed, duplex, tx_pause, rx_pause); -} -  static int ksz_switch_detect(struct ksz_device *dev)  {  	u8 id1, id2, id4; @@ -3522,7 +3609,7 @@ static int ksz_tc_ets_add(struct ksz_device *dev, int port,  	for (tc_prio = 0; tc_prio < ARRAY_SIZE(p->priomap); tc_prio++) {  		int queue; -		if (tc_prio > KSZ9477_MAX_TC_PRIO) +		if (tc_prio >= dev->info->num_ipms)  			break;  		queue = ksz_ets_band_to_queue(p, p->priomap[tc_prio]); @@ -3534,8 +3621,7 @@ static int ksz_tc_ets_add(struct ksz_device *dev, int port,  static int ksz_tc_ets_del(struct ksz_device *dev, int port)  { -	int ret, queue, tc_prio, s; -	u32 queue_map = 0; +	int ret, queue;  	/* To restore the default chip configuration, set all queues to use the  	 * WRR scheduler with a weight of 1. @@ -3547,31 +3633,10 @@ static int ksz_tc_ets_del(struct ksz_device *dev, int port)  			return ret;  	} -	switch (dev->info->num_tx_queues) { -	case 2: -		s = 2; -		break; -	case 4: -		s = 1; -		break; -	case 8: -		s = 0; -		break; -	default: -		return -EINVAL; -	} -  	/* Revert the queue mapping for TC-priority to its default setting on  	 * the chip.  	 */ -	for (tc_prio = 0; tc_prio <= KSZ9477_MAX_TC_PRIO; tc_prio++) { -		int queue; - -		queue = tc_prio >> s; -		queue_map |= queue << (tc_prio * KSZ9477_PORT_TC_MAP_S); -	} - -	return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map); +	return ksz9477_set_default_prio_queue_mapping(dev, port);  }  static int ksz_tc_ets_validate(struct ksz_device *dev, int port, @@ -3616,7 +3681,7 @@ static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port,  	struct ksz_device *dev = ds->priv;  	int ret; -	if (!dev->info->tc_ets_supported) +	if (is_ksz8(dev))  		return -EOPNOTSUPP;  	if (qopt->parent != TC_H_ROOT) { @@ -3881,9 +3946,6 @@ static const struct dsa_switch_ops ksz_switch_ops = {  	.phy_read		= ksz_phy_read16,  	.phy_write		= ksz_phy_write16,  	.phylink_get_caps	= ksz_phylink_get_caps, -	.phylink_mac_config	= ksz_phylink_mac_config, -	.phylink_mac_link_up	= ksz_phylink_mac_link_up, -	.phylink_mac_link_down	= ksz_mac_link_down,  	.port_setup		= ksz_port_setup,  	.set_ageing_time	= ksz_set_ageing_time,  	.get_strings		= ksz_get_strings, @@ -3925,6 +3987,13 @@ static const struct dsa_switch_ops ksz_switch_ops = {  	.port_setup_tc		= ksz_setup_tc,  	.get_mac_eee		= ksz_get_mac_eee,  	.set_mac_eee		= ksz_set_mac_eee, +	.port_get_default_prio	= ksz_port_get_default_prio, +	.port_set_default_prio	= ksz_port_set_default_prio, +	.port_get_dscp_prio	= ksz_port_get_dscp_prio, +	.port_add_dscp_prio	= ksz_port_add_dscp_prio, +	.port_del_dscp_prio	= ksz_port_del_dscp_prio, +	.port_get_apptrust	= ksz_port_get_apptrust, +	.port_set_apptrust	= ksz_port_set_apptrust,  };  struct ksz_device *ksz_switch_alloc(struct device *base, void *priv) @@ -4328,6 +4397,9 @@ int ksz_switch_register(struct ksz_device *dev)  	/* set the real number of ports */  	dev->ds->num_ports = dev->info->port_cnt; +	/* set the phylink ops */ +	dev->ds->phylink_mac_ops = dev->info->phylink_mac_ops; +  	/* Host port interface will be self detected, or specifically set in  	 * device tree.  	 */ diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 40c11b0d6b62..c784fd23a993 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -19,9 +19,13 @@  #include "ksz_ptp.h"  #define KSZ_MAX_NUM_PORTS 8 +/* all KSZ switches count ports from 1 */ +#define KSZ_PORT_1 0 +#define KSZ_PORT_2 1  struct ksz_device;  struct ksz_port; +struct phylink_mac_ops;  enum ksz_regmap_width {  	KSZ_REGMAP_8, @@ -58,9 +62,10 @@ struct ksz_chip_data {  	int port_cnt;  	u8 port_nirqs;  	u8 num_tx_queues; +	u8 num_ipms; /* number of Internal Priority Maps */  	bool tc_cbs_supported; -	bool tc_ets_supported;  	const struct ksz_dev_ops *ops; +	const struct phylink_mac_ops *phylink_mac_ops;  	bool ksz87xx_eee_link_erratum;  	const struct ksz_mib_names *mib_names;  	int mib_cnt; @@ -349,9 +354,6 @@ struct ksz_dev_ops {  	int (*change_mtu)(struct ksz_device *dev, int port, int mtu);  	void (*freeze_mib)(struct ksz_device *dev, int port, bool freeze);  	void (*port_init_cnt)(struct ksz_device *dev, int port); -	void (*phylink_mac_config)(struct ksz_device *dev, int port, -				   unsigned int mode, -				   const struct phylink_link_state *state);  	void (*phylink_mac_link_up)(struct ksz_device *dev, int port,  				    unsigned int mode,  				    phy_interface_t interface, @@ -620,6 +622,11 @@ static inline bool ksz_is_ksz88x3(struct ksz_device *dev)  	return dev->chip_id == KSZ8830_CHIP_ID;  } +static inline bool is_ksz8(struct ksz_device *dev) +{ +	return ksz_is_ksz87xx(dev) || ksz_is_ksz88x3(dev); +} +  static inline int is_lan937x(struct ksz_device *dev)  {  	return dev->chip_id == LAN9370_CHIP_ID || @@ -722,7 +729,6 @@ static inline int is_lan937x(struct ksz_device *dev)  #define KSZ9477_PORT_MRI_TC_MAP__4	0x0808  #define KSZ9477_PORT_TC_MAP_S		4 -#define KSZ9477_MAX_TC_PRIO		7  /* CBS related registers */  #define REG_PORT_MTI_QUEUE_INDEX__4	0x0900 diff --git a/drivers/net/dsa/microchip/ksz_dcb.c b/drivers/net/dsa/microchip/ksz_dcb.c new file mode 100644 index 000000000000..086bc9b3cf53 --- /dev/null +++ b/drivers/net/dsa/microchip/ksz_dcb.c @@ -0,0 +1,819 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2024 Pengutronix, Oleksij Rempel <[email protected]> + +#include <linux/dsa/ksz_common.h> +#include <net/dsa.h> +#include <net/dscp.h> +#include <net/ieee8021q.h> + +#include "ksz_common.h" +#include "ksz_dcb.h" +#include "ksz8.h" + +#define KSZ8_REG_PORT_1_CTRL_0			0x10 +#define KSZ8_PORT_DIFFSERV_ENABLE		BIT(6) +#define KSZ8_PORT_802_1P_ENABLE			BIT(5) +#define KSZ8_PORT_BASED_PRIO_M			GENMASK(4, 3) + +#define KSZ88X3_REG_TOS_DSCP_CTRL		0x60 +#define KSZ8765_REG_TOS_DSCP_CTRL		0x90 + +#define KSZ9477_REG_SW_MAC_TOS_CTRL		0x033e +#define KSZ9477_SW_TOS_DSCP_REMAP		BIT(0) +#define KSZ9477_SW_TOS_DSCP_DEFAULT_PRIO_M	GENMASK(5, 3) + +#define KSZ9477_REG_DIFFSERV_PRIO_MAP		0x0340 + +#define KSZ9477_REG_PORT_MRI_PRIO_CTRL		0x0801 +#define KSZ9477_PORT_HIGHEST_PRIO		BIT(7) +#define KSZ9477_PORT_OR_PRIO			BIT(6) +#define KSZ9477_PORT_MAC_PRIO_ENABLE		BIT(4) +#define KSZ9477_PORT_VLAN_PRIO_ENABLE		BIT(3) +#define KSZ9477_PORT_802_1P_PRIO_ENABLE		BIT(2) +#define KSZ9477_PORT_DIFFSERV_PRIO_ENABLE	BIT(1) +#define KSZ9477_PORT_ACL_PRIO_ENABLE		BIT(0) + +#define KSZ9477_REG_PORT_MRI_MAC_CTRL		0x0802 +#define KSZ9477_PORT_BASED_PRIO_M		GENMASK(2, 0) + +struct ksz_apptrust_map { +	u8 apptrust; +	u8 bit; +}; + +static const struct ksz_apptrust_map ksz8_apptrust_map_to_bit[] = { +	{ DCB_APP_SEL_PCP, KSZ8_PORT_802_1P_ENABLE }, +	{ IEEE_8021QAZ_APP_SEL_DSCP, KSZ8_PORT_DIFFSERV_ENABLE }, +}; + +static const struct ksz_apptrust_map ksz9477_apptrust_map_to_bit[] = { +	{ DCB_APP_SEL_PCP, KSZ9477_PORT_802_1P_PRIO_ENABLE }, +	{ IEEE_8021QAZ_APP_SEL_DSCP, KSZ9477_PORT_DIFFSERV_PRIO_ENABLE }, +}; + +/* ksz_supported_apptrust[] - Supported apptrust selectors and Priority Order + *			      of Internal Priority Map (IPM) sources. + * + * This array defines the apptrust selectors supported by the hardware, where + * the index within the array indicates the priority of the selector - lower + * indices correspond to higher priority. This fixed priority scheme is due to + * the hardware's design, which does not support configurable priority among + * different priority sources. + * + * The priority sources, including Tail Tag, ACL, VLAN PCP and DSCP are ordered + * by the hardware's fixed logic, as detailed below. The order reflects a + * non-configurable precedence where certain types of priority information + * override others: + * + * 1. Tail Tag - Highest priority, overrides ACL, VLAN PCP, and DSCP priorities. + * 2. ACL - Overrides VLAN PCP and DSCP priorities. + * 3. VLAN PCP - Overrides DSCP priority. + * 4. DSCP - Lowest priority, does not override any other priority source. + * + * In this context, the array's lower index (higher priority) for + * 'DCB_APP_SEL_PCP' suggests its relative priority over + * 'IEEE_8021QAZ_APP_SEL_DSCP' within the system's fixed priority scheme. + * + * DCB_APP_SEL_PCP - Priority Code Point selector + * IEEE_8021QAZ_APP_SEL_DSCP - Differentiated Services Code Point selector + */ +static const u8 ksz_supported_apptrust[] = { +	DCB_APP_SEL_PCP, +	IEEE_8021QAZ_APP_SEL_DSCP, +}; + +static const char * const ksz_supported_apptrust_variants[] = { +	"empty", "dscp", "pcp", "dscp pcp" +}; + +static void ksz_get_default_port_prio_reg(struct ksz_device *dev, int *reg, +					  u8 *mask, int *shift) +{ +	if (is_ksz8(dev)) { +		*reg = KSZ8_REG_PORT_1_CTRL_0; +		*mask = KSZ8_PORT_BASED_PRIO_M; +		*shift = __bf_shf(KSZ8_PORT_BASED_PRIO_M); +	} else { +		*reg = KSZ9477_REG_PORT_MRI_MAC_CTRL; +		*mask = KSZ9477_PORT_BASED_PRIO_M; +		*shift = __bf_shf(KSZ9477_PORT_BASED_PRIO_M); +	} +} + +/** + * ksz_get_dscp_prio_reg - Retrieves the DSCP-to-priority-mapping register + * @dev: Pointer to the KSZ switch device structure + * @reg: Pointer to the register address to be set + * @per_reg: Pointer to the number of DSCP values per register + * @mask: Pointer to the mask to be set + * + * This function retrieves the DSCP to priority mapping register, the number of + * DSCP values per register, and the mask to be set. + */ +static void ksz_get_dscp_prio_reg(struct ksz_device *dev, int *reg, +				  int *per_reg, u8 *mask) +{ +	if (ksz_is_ksz87xx(dev)) { +		*reg = KSZ8765_REG_TOS_DSCP_CTRL; +		*per_reg = 4; +		*mask = GENMASK(1, 0); +	} else if (ksz_is_ksz88x3(dev)) { +		*reg = KSZ88X3_REG_TOS_DSCP_CTRL; +		*per_reg = 4; +		*mask = GENMASK(1, 0); +	} else { +		*reg = KSZ9477_REG_DIFFSERV_PRIO_MAP; +		*per_reg = 2; +		*mask = GENMASK(2, 0); +	} +} + +/** + * ksz_get_apptrust_map_and_reg - Retrieves the apptrust map and register + * @dev: Pointer to the KSZ switch device structure + * @map: Pointer to the apptrust map to be set + * @reg: Pointer to the register address to be set + * @mask: Pointer to the mask to be set + * + * This function retrieves the apptrust map and register address for the + * apptrust configuration. + */ +static void ksz_get_apptrust_map_and_reg(struct ksz_device *dev, +					 const struct ksz_apptrust_map **map, +					 int *reg, u8 *mask) +{ +	if (is_ksz8(dev)) { +		*map = ksz8_apptrust_map_to_bit; +		*reg = KSZ8_REG_PORT_1_CTRL_0; +		*mask = KSZ8_PORT_DIFFSERV_ENABLE | KSZ8_PORT_802_1P_ENABLE; +	} else { +		*map = ksz9477_apptrust_map_to_bit; +		*reg = KSZ9477_REG_PORT_MRI_PRIO_CTRL; +		*mask = KSZ9477_PORT_802_1P_PRIO_ENABLE | +			KSZ9477_PORT_DIFFSERV_PRIO_ENABLE; +	} +} + +/** + * ksz_port_get_default_prio - Retrieves the default priority for a port on a + *			       KSZ switch + * @ds: Pointer to the DSA switch structure + * @port: Port number from which to get the default priority + * + * This function fetches the default priority for the specified port on a KSZ + * switch. + * + * Return: The default priority of the port on success, or a negative error + * code on failure. + */ +int ksz_port_get_default_prio(struct dsa_switch *ds, int port) +{ +	struct ksz_device *dev = ds->priv; +	int ret, reg, shift; +	u8 data, mask; + +	ksz_get_default_port_prio_reg(dev, ®, &mask, &shift); + +	ret = ksz_pread8(dev, port, reg, &data); +	if (ret) +		return ret; + +	return (data & mask) >> shift; +} + +/** + * ksz88x3_port_set_default_prio_quirks - Quirks for default priority + * @dev: Pointer to the KSZ switch device structure + * @port: Port number for which to set the default priority + * @prio: Priority value to set + * + * This function implements quirks for setting the default priority on KSZ88x3 + * devices. On Port 2, no other priority providers are working + * except of PCP. So, configuring default priority on Port 2 is not possible. + * On Port 1, it is not possible to configure port priority if PCP + * apptrust on Port 2 is disabled. Since we disable multiple queues on the + * switch to disable PCP on Port 2, we need to ensure that the default priority + * configuration on Port 1 is in agreement with the configuration on Port 2. + * + * Return: 0 on success, or a negative error code on failure + */ +static int ksz88x3_port_set_default_prio_quirks(struct ksz_device *dev, int port, +						u8 prio) +{ +	if (!prio) +		return 0; + +	if (port == KSZ_PORT_2) { +		dev_err(dev->dev, "Port priority configuration is not working on Port 2\n"); +		return -EINVAL; +	} else if (port == KSZ_PORT_1) { +		u8 port2_data; +		int ret; + +		ret = ksz_pread8(dev, KSZ_PORT_2, KSZ8_REG_PORT_1_CTRL_0, +				 &port2_data); +		if (ret) +			return ret; + +		if (!(port2_data & KSZ8_PORT_802_1P_ENABLE)) { +			dev_err(dev->dev, "Not possible to configure port priority on Port 1 if PCP apptrust on Port 2 is disabled\n"); +			return -EINVAL; +		} +	} + +	return 0; +} + +/** + * ksz_port_set_default_prio - Sets the default priority for a port on a KSZ + *			       switch + * @ds: Pointer to the DSA switch structure + * @port: Port number for which to set the default priority + * @prio: Priority value to set + * + * This function sets the default priority for the specified port on a KSZ + * switch. + * + * Return: 0 on success, or a negative error code on failure. + */ +int ksz_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio) +{ +	struct ksz_device *dev = ds->priv; +	int reg, shift, ret; +	u8 mask; + +	if (prio >= dev->info->num_ipms) +		return -EINVAL; + +	if (ksz_is_ksz88x3(dev)) { +		ret = ksz88x3_port_set_default_prio_quirks(dev, port, prio); +		if (ret) +			return ret; +	} + +	ksz_get_default_port_prio_reg(dev, ®, &mask, &shift); + +	return ksz_prmw8(dev, port, reg, mask, (prio << shift) & mask); +} + +/** + * ksz_port_get_dscp_prio - Retrieves the priority for a DSCP value on a KSZ + *			    switch + * @ds: Pointer to the DSA switch structure + * @port: Port number for which to get the priority + * @dscp: DSCP value for which to get the priority + * + * This function fetches the priority value from switch global DSCP-to-priorty + * mapping table for the specified DSCP value. + * + * Return: The priority value for the DSCP on success, or a negative error + * code on failure. + */ +int ksz_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp) +{ +	struct ksz_device *dev = ds->priv; +	int reg, per_reg, ret, shift; +	u8 data, mask; + +	ksz_get_dscp_prio_reg(dev, ®, &per_reg, &mask); + +	/* If DSCP remapping is disabled, DSCP bits 3-5 are used as Internal +	 * Priority Map (IPM) +	 */ +	if (!is_ksz8(dev)) { +		ret = ksz_read8(dev, KSZ9477_REG_SW_MAC_TOS_CTRL, &data); +		if (ret) +			return ret; + +		/* If DSCP remapping is disabled, DSCP bits 3-5 are used as +		 * Internal Priority Map (IPM) +		 */ +		if (!(data & KSZ9477_SW_TOS_DSCP_REMAP)) +			return FIELD_GET(KSZ9477_SW_TOS_DSCP_DEFAULT_PRIO_M, +					 dscp); +	} + +	/* In case DSCP remapping is enabled, we need to write the DSCP to +	 * priority mapping table. +	 */ +	reg += dscp / per_reg; +	ret = ksz_read8(dev, reg, &data); +	if (ret) +		return ret; + +	shift = (dscp % per_reg) * (8 / per_reg); + +	return (data >> shift) & mask; +} + +/** + * ksz_set_global_dscp_entry - Sets the global DSCP-to-priority mapping entry + * @dev: Pointer to the KSZ switch device structure + * @dscp: DSCP value for which to set the priority + * @ipm: Priority value to set + * + * This function sets the global DSCP-to-priority mapping entry for the + * specified DSCP value. + * + * Return: 0 on success, or a negative error code on failure. + */ +static int ksz_set_global_dscp_entry(struct ksz_device *dev, u8 dscp, u8 ipm) +{ +	int reg, per_reg, shift; +	u8 mask; + +	ksz_get_dscp_prio_reg(dev, ®, &per_reg, &mask); + +	shift = (dscp % per_reg) * (8 / per_reg); + +	return ksz_rmw8(dev, reg + (dscp / per_reg), mask << shift, +			ipm << shift); +} + +/** + * ksz_init_global_dscp_map - Initializes the global DSCP-to-priority mapping + * @dev: Pointer to the KSZ switch device structure + * + * This function initializes the global DSCP-to-priority mapping table for the + * switch. + * + * Return: 0 on success, or a negative error code on failure + */ +static int ksz_init_global_dscp_map(struct ksz_device *dev) +{ +	int ret, dscp; + +	/* On KSZ9xxx variants, DSCP remapping is disabled by default. +	 * Enable to have, predictable and reproducible behavior across +	 * different devices. +	 */ +	if (!is_ksz8(dev)) { +		ret = ksz_rmw8(dev, KSZ9477_REG_SW_MAC_TOS_CTRL, +			       KSZ9477_SW_TOS_DSCP_REMAP, +			       KSZ9477_SW_TOS_DSCP_REMAP); +		if (ret) +			return ret; +	} + +	for (dscp = 0; dscp < DSCP_MAX; dscp++) { +		int ipm, tt; + +		/* Map DSCP to Traffic Type, which is corresponding to the +		 * Internal Priority Map (IPM) in the switch. +		 */ +		if (!is_ksz8(dev)) { +			ipm = ietf_dscp_to_ieee8021q_tt(dscp); +		} else { +			/* On KSZ8xxx variants we do not have IPM to queue +			 * remapping table. We need to convert DSCP to Traffic +			 * Type and then to queue. +			 */ +			tt = ietf_dscp_to_ieee8021q_tt(dscp); +			if (tt < 0) +				return tt; + +			ipm = ieee8021q_tt_to_tc(tt, dev->info->num_tx_queues); +		} + +		if (ipm < 0) +			return ipm; + +		ret = ksz_set_global_dscp_entry(dev, dscp, ipm); +	} + +	return 0; +} + +/** + * ksz_port_add_dscp_prio - Adds a DSCP-to-priority mapping entry for a port on + *			    a KSZ switch. + * @ds: Pointer to the DSA switch structure + * @port: Port number for which to add the DSCP-to-priority mapping entry + * @dscp: DSCP value for which to add the priority + * @prio: Priority value to set + * + * Return: 0 on success, or a negative error code on failure + */ +int ksz_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio) +{ +	struct ksz_device *dev = ds->priv; + +	if (prio >= dev->info->num_ipms) +		return -ERANGE; + +	return ksz_set_global_dscp_entry(dev, dscp, prio); +} + +/** + * ksz_port_del_dscp_prio - Deletes a DSCP-to-priority mapping entry for a port + *			    on a KSZ switch. + * @ds: Pointer to the DSA switch structure + * @port: Port number for which to delete the DSCP-to-priority mapping entry + * @dscp: DSCP value for which to delete the priority + * @prio: Priority value to delete + * + * Return: 0 on success, or a negative error code on failure + */ +int ksz_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio) +{ +	struct ksz_device *dev = ds->priv; +	int ipm; + +	if (ksz_port_get_dscp_prio(ds, port, dscp) != prio) +		return 0; + +	if (is_ksz8(dev)) { +		ipm = ieee8021q_tt_to_tc(IEEE8021Q_TT_BE, +					 dev->info->num_tx_queues); +		if (ipm < 0) +			return ipm; +	} else { +		ipm = IEEE8021Q_TT_BE; +	} + +	return ksz_set_global_dscp_entry(dev, dscp, ipm); +} + +/** + * ksz_apptrust_error - Prints an error message for an invalid apptrust selector + * @dev: Pointer to the KSZ switch device structure + * + * This function prints an error message when an invalid apptrust selector is + * provided. + */ +static void ksz_apptrust_error(struct ksz_device *dev) +{ +	char supported_apptrust_variants[64]; +	int i; + +	supported_apptrust_variants[0] = '\0'; +	for (i = 0; i < ARRAY_SIZE(ksz_supported_apptrust_variants); i++) { +		if (i > 0) +			strlcat(supported_apptrust_variants, ", ", +				sizeof(supported_apptrust_variants)); +		strlcat(supported_apptrust_variants, +			ksz_supported_apptrust_variants[i], +			sizeof(supported_apptrust_variants)); +	} + +	dev_err(dev->dev, "Invalid apptrust selector or priority order. Supported: %s\n", +		supported_apptrust_variants); +} + +/** + * ksz_port_set_apptrust_validate - Validates the apptrust selectors + * @dev: Pointer to the KSZ switch device structure + * @port: Port number for which to set the apptrust selectors + * @sel: Array of apptrust selectors to validate + * @nsel: Number of apptrust selectors in the array + * + * This function validates the apptrust selectors provided and ensures that + * they are in the correct order. + * + * This family of switches supports two apptrust selectors: DCB_APP_SEL_PCP and + * IEEE_8021QAZ_APP_SEL_DSCP. The priority order of the selectors is fixed and + * cannot be changed. The order is as follows: + * 1. DCB_APP_SEL_PCP - Priority Code Point selector (highest priority) + * 2. IEEE_8021QAZ_APP_SEL_DSCP - Differentiated Services Code Point selector + *   (lowest priority) + * + * Return: 0 on success, or a negative error code on failure + */ +static int ksz_port_set_apptrust_validate(struct ksz_device *dev, int port, +					  const u8 *sel, int nsel) +{ +	int i, j, found; +	int j_prev = 0; + +	/* Iterate through the requested selectors */ +	for (i = 0; i < nsel; i++) { +		found = 0; + +		/* Check if the current selector is supported by the hardware */ +		for (j = 0; j < sizeof(ksz_supported_apptrust); j++) { +			if (sel[i] != ksz_supported_apptrust[j]) +				continue; + +			found = 1; + +			/* Ensure that no higher priority selector (lower index) +			 * precedes a lower priority one +			 */ +			if (i > 0 && j <= j_prev) +				goto err_sel_not_vaild; + +			j_prev = j; +			break; +		} + +		if (!found) +			goto err_sel_not_vaild; +	} + +	return 0; + +err_sel_not_vaild: +	ksz_apptrust_error(dev); + +	return -EINVAL; +} + +/** + * ksz88x3_port1_apptrust_quirk - Quirk for apptrust configuration on Port 1 + *				  of KSZ88x3 devices + * @dev: Pointer to the KSZ switch device structure + * @port: Port number for which to set the apptrust selectors + * @reg: Register address for the apptrust configuration + * @port1_data: Data to set for the apptrust configuration + * + * This function implements a quirk for apptrust configuration on Port 1 of + * KSZ88x3 devices. It ensures that apptrust configuration on Port 1 is not + * possible if PCP apptrust on Port 2 is disabled. This is because the Port 2 + * seems to be permanently hardwired to PCP classification, so we need to + * do Port 1 configuration always in agreement with Port 2 configuration. + * + * Return: 0 on success, or a negative error code on failure + */ +static int ksz88x3_port1_apptrust_quirk(struct ksz_device *dev, int port, +					int reg, u8 port1_data) +{ +	u8 port2_data; +	int ret; + +	/* If no apptrust is requested for Port 1, no need to care about Port 2 +	 * configuration. +	 */ +	if (!(port1_data & (KSZ8_PORT_802_1P_ENABLE | KSZ8_PORT_DIFFSERV_ENABLE))) +		return 0; + +	/* We got request to enable any apptrust on Port 1. To make it possible, +	 * we need to enable multiple queues on the switch. If we enable +	 * multiqueue support, PCP classification on Port 2 will be +	 * automatically activated by HW. +	 */ +	ret = ksz_pread8(dev, KSZ_PORT_2, reg, &port2_data); +	if (ret) +		return ret; + +	/* If KSZ8_PORT_802_1P_ENABLE bit is set on Port 2, the driver showed +	 * the interest in PCP classification on Port 2. In this case, +	 * multiqueue support is enabled and we can enable any apptrust on +	 * Port 1. +	 * If KSZ8_PORT_802_1P_ENABLE bit is not set on Port 2, the PCP +	 * classification on Port 2 is still active, but the driver disabled +	 * multiqueue support and made frame prioritization inactive for +	 * all ports. In this case, we can't enable any apptrust on Port 1. +	 */ +	if (!(port2_data & KSZ8_PORT_802_1P_ENABLE)) { +		dev_err(dev->dev, "Not possible to enable any apptrust on Port 1 if PCP apptrust on Port 2 is disabled\n"); +		return -EINVAL; +	} + +	return 0; +} + +/** + * ksz88x3_port2_apptrust_quirk - Quirk for apptrust configuration on Port 2 + *				  of KSZ88x3 devices + * @dev: Pointer to the KSZ switch device structure + * @port: Port number for which to set the apptrust selectors + * @reg: Register address for the apptrust configuration + * @port2_data: Data to set for the apptrust configuration + * + * This function implements a quirk for apptrust configuration on Port 2 of + * KSZ88x3 devices. It ensures that DSCP apptrust is not working on Port 2 and + * that it is not possible to disable PCP on Port 2. The only way to disable PCP + * on Port 2 is to disable multiple queues on the switch. + * + * Return: 0 on success, or a negative error code on failure + */ +static int ksz88x3_port2_apptrust_quirk(struct ksz_device *dev, int port, +					int reg, u8 port2_data) +{ +	struct dsa_switch *ds = dev->ds; +	u8 port1_data; +	int ret; + +	/* First validate Port 2 configuration. DiffServ/DSCP is not working +	 * on this port. +	 */ +	if (port2_data & KSZ8_PORT_DIFFSERV_ENABLE) { +		dev_err(dev->dev, "DSCP apptrust is not working on Port 2\n"); +		return -EINVAL; +	} + +	/* If PCP support is requested, we need to enable all queues on the +	 * switch to make PCP priority working on Port 2. +	 */ +	if (port2_data & KSZ8_PORT_802_1P_ENABLE) +		return ksz8_all_queues_split(dev, dev->info->num_tx_queues); + +	/* We got request to disable PCP priority on Port 2. +	 * Now, we need to compare Port 2 configuration with Port 1 +	 * configuration. +	 */ +	ret = ksz_pread8(dev, KSZ_PORT_1, reg, &port1_data); +	if (ret) +		return ret; + +	/* If Port 1 has any apptrust enabled, we can't disable multiple queues +	 * on the switch, so we can't disable PCP on Port 2. +	 */ +	if (port1_data & (KSZ8_PORT_802_1P_ENABLE | KSZ8_PORT_DIFFSERV_ENABLE)) { +		dev_err(dev->dev, "Not possible to disable PCP on Port 2 if any apptrust is enabled on Port 1\n"); +		return -EINVAL; +	} + +	/* Now we need to ensure that default priority on Port 1 is set to 0 +	 * otherwise we can't disable multiqueue support on the switch. +	 */ +	ret = ksz_port_get_default_prio(ds, KSZ_PORT_1); +	if (ret < 0) { +		return ret; +	} else if (ret) { +		dev_err(dev->dev, "Not possible to disable PCP on Port 2 if non zero default priority is set on Port 1\n"); +		return -EINVAL; +	} + +	/* Port 1 has no apptrust or default priority set and we got request to +	 * disable PCP on Port 2. We can disable multiqueue support to disable +	 * PCP on Port 2. +	 */ +	return ksz8_all_queues_split(dev, 1); +} + +/** + * ksz88x3_port_apptrust_quirk - Quirk for apptrust configuration on KSZ88x3 + *			       devices + * @dev: Pointer to the KSZ switch device structure + * @port: Port number for which to set the apptrust selectors + * @reg: Register address for the apptrust configuration + * @data: Data to set for the apptrust configuration + * + * This function implements a quirk for apptrust configuration on KSZ88x3 + * devices. It ensures that apptrust configuration on Port 1 and + * Port 2 is done in agreement with each other. + * + * Return: 0 on success, or a negative error code on failure + */ +static int ksz88x3_port_apptrust_quirk(struct ksz_device *dev, int port, +				       int reg, u8 data) +{ +	if (port == KSZ_PORT_1) +		return ksz88x3_port1_apptrust_quirk(dev, port, reg, data); +	else if (port == KSZ_PORT_2) +		return ksz88x3_port2_apptrust_quirk(dev, port, reg, data); + +	return 0; +} + +/** + * ksz_port_set_apptrust - Sets the apptrust selectors for a port on a KSZ + *			   switch + * @ds: Pointer to the DSA switch structure + * @port: Port number for which to set the apptrust selectors + * @sel: Array of apptrust selectors to set + * @nsel: Number of apptrust selectors in the array + * + * This function sets the apptrust selectors for the specified port on a KSZ + * switch. + * + * Return: 0 on success, or a negative error code on failure + */ +int ksz_port_set_apptrust(struct dsa_switch *ds, int port, +			  const u8 *sel, int nsel) +{ +	const struct ksz_apptrust_map *map; +	struct ksz_device *dev = ds->priv; +	int reg, i, ret; +	u8 data = 0; +	u8 mask; + +	ret = ksz_port_set_apptrust_validate(dev, port, sel, nsel); +	if (ret) +		return ret; + +	ksz_get_apptrust_map_and_reg(dev, &map, ®, &mask); + +	for (i = 0; i < nsel; i++) { +		int j; + +		for (j = 0; j < ARRAY_SIZE(ksz_supported_apptrust); j++) { +			if (sel[i] != ksz_supported_apptrust[j]) +				continue; + +			data |= map[j].bit; +			break; +		} +	} + +	if (ksz_is_ksz88x3(dev)) { +		ret = ksz88x3_port_apptrust_quirk(dev, port, reg, data); +		if (ret) +			return ret; +	} + +	return ksz_prmw8(dev, port, reg, mask, data); +} + +/** + * ksz_port_get_apptrust - Retrieves the apptrust selectors for a port on a KSZ + *			   switch + * @ds: Pointer to the DSA switch structure + * @port: Port number for which to get the apptrust selectors + * @sel: Array to store the apptrust selectors + * @nsel: Number of apptrust selectors in the array + * + * This function fetches the apptrust selectors for the specified port on a KSZ + * switch. + * + * Return: 0 on success, or a negative error code on failure + */ +int ksz_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, int *nsel) +{ +	const struct ksz_apptrust_map *map; +	struct ksz_device *dev = ds->priv; +	int reg, i, ret; +	u8 data; +	u8 mask; + +	ksz_get_apptrust_map_and_reg(dev, &map, ®, &mask); + +	ret = ksz_pread8(dev, port, reg, &data); +	if (ret) +		return ret; + +	*nsel = 0; +	for (i = 0; i < ARRAY_SIZE(ksz_supported_apptrust); i++) { +		if (data & map[i].bit) +			sel[(*nsel)++] = ksz_supported_apptrust[i]; +	} + +	return 0; +} + +/** + * ksz_dcb_init_port - Initializes the DCB configuration for a port on a KSZ + * @dev: Pointer to the KSZ switch device structure + * @port: Port number for which to initialize the DCB configuration + * + * This function initializes the DCB configuration for the specified port on a + * KSZ switch. Particular DCB configuration is set for the port, including the + * default priority and apptrust selectors. + * The default priority is set to Best Effort, and the apptrust selectors are + * set to all supported selectors. + * + * Return: 0 on success, or a negative error code on failure + */ +int ksz_dcb_init_port(struct ksz_device *dev, int port) +{ +	const u8 ksz_default_apptrust[] = { DCB_APP_SEL_PCP }; +	int ret, ipm; + +	if (is_ksz8(dev)) { +		ipm = ieee8021q_tt_to_tc(IEEE8021Q_TT_BE, +					 dev->info->num_tx_queues); +		if (ipm < 0) +			return ipm; +	} else { +		ipm = IEEE8021Q_TT_BE; +	} + +	/* Set the default priority for the port to Best Effort */ +	ret = ksz_port_set_default_prio(dev->ds, port, ipm); +	if (ret) +		return ret; + +	return ksz_port_set_apptrust(dev->ds, port, ksz_default_apptrust, +				     ARRAY_SIZE(ksz_default_apptrust)); +} + +/** + * ksz_dcb_init - Initializes the DCB configuration for a KSZ switch + * @dev: Pointer to the KSZ switch device structure + * + * This function initializes the DCB configuration for a KSZ switch. The global + * DSCP-to-priority mapping table is initialized. + * + * Return: 0 on success, or a negative error code on failure + */ +int ksz_dcb_init(struct ksz_device *dev) +{ +	int ret; + +	ret = ksz_init_global_dscp_map(dev); +	if (ret) +		return ret; + +	/* Enable 802.1p priority control on Port 2 during switch initialization. +	 * This setup is critical for the apptrust functionality on Port 1, which +	 * relies on the priority settings of Port 2. Note: Port 1 is naturally +	 * configured before Port 2, necessitating this configuration order. +	 */ +	if (ksz_is_ksz88x3(dev)) +		return ksz_prmw8(dev, KSZ_PORT_2, KSZ8_REG_PORT_1_CTRL_0, +				 KSZ8_PORT_802_1P_ENABLE, +				 KSZ8_PORT_802_1P_ENABLE); + +	return 0; +} diff --git a/drivers/net/dsa/microchip/ksz_dcb.h b/drivers/net/dsa/microchip/ksz_dcb.h new file mode 100644 index 000000000000..e2065223ba90 --- /dev/null +++ b/drivers/net/dsa/microchip/ksz_dcb.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Pengutronix, Oleksij Rempel <[email protected]> */ + +#ifndef __KSZ_DCB_H +#define __KSZ_DCB_H + +#include <net/dsa.h> + +#include "ksz_common.h" + +int ksz_port_get_default_prio(struct dsa_switch *ds, int port); +int ksz_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio); +int ksz_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp); +int ksz_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio); +int ksz_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio); +int ksz_port_set_apptrust(struct dsa_switch *ds, int port, +			  const unsigned char *sel, +			  int nsel); +int ksz_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, int *nsel); +int ksz_dcb_init_port(struct ksz_device *dev, int port); +int ksz_dcb_init(struct ksz_device *dev); + +#endif /* __KSZ_DCB_H */ diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c index c8166fb440ab..8e8d83213b04 100644 --- a/drivers/net/dsa/microchip/ksz_spi.c +++ b/drivers/net/dsa/microchip/ksz_spi.c @@ -222,7 +222,6 @@ MODULE_DEVICE_TABLE(spi, ksz_spi_ids);  static struct spi_driver ksz_spi_driver = {  	.driver = {  		.name	= "ksz-switch", -		.owner	= THIS_MODULE,  		.of_match_table = ksz_dt_ids,  	},  	.id_table = ksz_spi_ids, @@ -233,13 +232,6 @@ static struct spi_driver ksz_spi_driver = {  module_spi_driver(ksz_spi_driver); -MODULE_ALIAS("spi:ksz9477"); -MODULE_ALIAS("spi:ksz9896"); -MODULE_ALIAS("spi:ksz9897"); -MODULE_ALIAS("spi:ksz9893"); -MODULE_ALIAS("spi:ksz9563"); -MODULE_ALIAS("spi:ksz8563"); -MODULE_ALIAS("spi:ksz9567");  MODULE_ALIAS("spi:lan937x");  MODULE_AUTHOR("Tristram Ha <[email protected]>");  MODULE_DESCRIPTION("Microchip ksz Series Switch SPI Driver");  |