Commit e3f96b35 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'net-dsa-microchip-add-ksz8463-switch-support'

Tristram Ha says:

====================
net: dsa: microchip: Add KSZ8463 switch support

This series of patches is to add KSZ8463 switch support to the KSZ DSA
driver.
====================

Link: https://patch.msgid.link/20250725001753.6330-1-Tristram.Ha@microchip.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents d1f3dbad 620e2392
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ properties:
  # required and optional properties.
  compatible:
    enum:
      - microchip,ksz8463
      - microchip,ksz8765
      - microchip,ksz8794
      - microchip,ksz8795
+166 −22
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
 * Microchip KSZ8XXX series switch driver
 *
 * It supports the following switches:
 * - KSZ8463
 * - KSZ8863, KSZ8873 aka KSZ88X3
 * - KSZ8895, KSZ8864 aka KSZ8895 family
 * - KSZ8794, KSZ8795, KSZ8765 aka KSZ87XX
@@ -41,7 +42,8 @@ static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits,
			 bool set)
{
	regmap_update_bits(ksz_regmap_8(dev), PORT_CTRL_ADDR(port, offset),
	regmap_update_bits(ksz_regmap_8(dev),
			   dev->dev_ops->get_port_addr(port, offset),
			   bits, set ? bits : 0);
}

@@ -140,6 +142,11 @@ int ksz8_reset_switch(struct ksz_device *dev)
			KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, true);
		ksz_cfg(dev, KSZ8863_REG_SW_RESET,
			KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, false);
	} else if (ksz_is_ksz8463(dev)) {
		ksz_cfg(dev, KSZ8463_REG_SW_RESET,
			KSZ8463_GLOBAL_SOFTWARE_RESET, true);
		ksz_cfg(dev, KSZ8463_REG_SW_RESET,
			KSZ8463_GLOBAL_SOFTWARE_RESET, false);
	} else {
		/* reset switch */
		ksz_write8(dev, REG_POWER_MANAGEMENT_1,
@@ -194,6 +201,7 @@ int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu)
	case KSZ8794_CHIP_ID:
	case KSZ8765_CHIP_ID:
		return ksz8795_change_mtu(dev, frame_size);
	case KSZ8463_CHIP_ID:
	case KSZ88X3_CHIP_ID:
	case KSZ8864_CHIP_ID:
	case KSZ8895_CHIP_ID:
@@ -227,6 +235,11 @@ static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues)
			       WEIGHTED_FAIR_QUEUE_ENABLE);
		if (ret)
			return ret;
	} else if (ksz_is_ksz8463(dev)) {
		mask_4q = KSZ8873_PORT_4QUEUE_SPLIT_EN;
		mask_2q = KSZ8873_PORT_2QUEUE_SPLIT_EN;
		reg_4q = P1CR1;
		reg_2q = P1CR1 + 1;
	} else {
		mask_4q = KSZ8795_PORT_4QUEUE_SPLIT_EN;
		mask_2q = KSZ8795_PORT_2QUEUE_SPLIT_EN;
@@ -1265,12 +1278,15 @@ int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val)

void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member)
{
	int offset = P_MIRROR_CTRL;
	u8 data;

	ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
	data &= ~PORT_VLAN_MEMBERSHIP;
	if (ksz_is_ksz8463(dev))
		offset = P1CR2;
	ksz_pread8(dev, port, offset, &data);
	data &= ~dev->port_mask;
	data |= (member & dev->port_mask);
	ksz_pwrite8(dev, port, P_MIRROR_CTRL, data);
	ksz_pwrite8(dev, port, offset, data);
}

void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port)
@@ -1278,6 +1294,8 @@ void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port)
	u8 learn[DSA_MAX_PORTS];
	int first, index, cnt;
	const u16 *regs;
	int reg = S_FLUSH_TABLE_CTRL;
	int mask = SW_FLUSH_DYN_MAC_TABLE;

	regs = dev->info->regs;

@@ -1295,7 +1313,11 @@ void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port)
			ksz_pwrite8(dev, index, regs[P_STP_CTRL],
				    learn[index] | PORT_LEARN_DISABLE);
	}
	ksz_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_DYN_MAC_TABLE, true);
	if (ksz_is_ksz8463(dev)) {
		reg = KSZ8463_FLUSH_TABLE_CTRL;
		mask = KSZ8463_FLUSH_DYN_MAC_TABLE;
	}
	ksz_cfg(dev, reg, mask, true);
	for (index = first; index < cnt; index++) {
		if (!(learn[index] & PORT_LEARN_DISABLE))
			ksz_pwrite8(dev, index, regs[P_STP_CTRL], learn[index]);
@@ -1434,7 +1456,7 @@ int ksz8_fdb_del(struct ksz_device *dev, int port, const unsigned char *addr,
int ksz8_port_vlan_filtering(struct ksz_device *dev, int port, bool flag,
			     struct netlink_ext_ack *extack)
{
	if (ksz_is_ksz88x3(dev))
	if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))
		return -ENOTSUPP;

	/* Discard packets with VID not enabled on the switch */
@@ -1450,9 +1472,12 @@ int ksz8_port_vlan_filtering(struct ksz_device *dev, int port, bool flag,

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);
	if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)) {
		int reg = REG_SW_INSERT_SRC_PVID;

		if (ksz_is_ksz8463(dev))
			reg = KSZ8463_REG_SW_CTRL_9;
		ksz_cfg(dev, reg, 0x03 << (4 - 2 * port), state);
	} else {
		ksz_pwrite8(dev, port, REG_PORT_CTRL_12, state ? 0x0f : 0x00);
	}
@@ -1467,7 +1492,7 @@ int ksz8_port_vlan_add(struct ksz_device *dev, int port,
	u16 data, new_pvid = 0;
	u8 fid, member, valid;

	if (ksz_is_ksz88x3(dev))
	if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))
		return -ENOTSUPP;

	/* If a VLAN is added with untagged flag different from the
@@ -1536,7 +1561,7 @@ int ksz8_port_vlan_del(struct ksz_device *dev, int port,
	u16 data, pvid;
	u8 fid, member, valid;

	if (ksz_is_ksz88x3(dev))
	if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))
		return -ENOTSUPP;

	ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid);
@@ -1566,19 +1591,23 @@ int ksz8_port_mirror_add(struct ksz_device *dev, int port,
			 struct dsa_mall_mirror_tc_entry *mirror,
			 bool ingress, struct netlink_ext_ack *extack)
{
	int offset = P_MIRROR_CTRL;

	if (ksz_is_ksz8463(dev))
		offset = P1CR2;
	if (ingress) {
		ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true);
		ksz_port_cfg(dev, port, offset, PORT_MIRROR_RX, true);
		dev->mirror_rx |= BIT(port);
	} else {
		ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true);
		ksz_port_cfg(dev, port, offset, PORT_MIRROR_TX, true);
		dev->mirror_tx |= BIT(port);
	}

	ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false);
	ksz_port_cfg(dev, port, offset, PORT_MIRROR_SNIFFER, false);

	/* configure mirror port */
	if (dev->mirror_rx || dev->mirror_tx)
		ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
		ksz_port_cfg(dev, mirror->to_local_port, offset,
			     PORT_MIRROR_SNIFFER, true);

	return 0;
@@ -1587,20 +1616,23 @@ int ksz8_port_mirror_add(struct ksz_device *dev, int port,
void ksz8_port_mirror_del(struct ksz_device *dev, int port,
			  struct dsa_mall_mirror_tc_entry *mirror)
{
	int offset = P_MIRROR_CTRL;
	u8 data;

	if (ksz_is_ksz8463(dev))
		offset = P1CR2;
	if (mirror->ingress) {
		ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false);
		ksz_port_cfg(dev, port, offset, PORT_MIRROR_RX, false);
		dev->mirror_rx &= ~BIT(port);
	} else {
		ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false);
		ksz_port_cfg(dev, port, offset, PORT_MIRROR_TX, false);
		dev->mirror_tx &= ~BIT(port);
	}

	ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
	ksz_pread8(dev, port, offset, &data);

	if (!dev->mirror_rx && !dev->mirror_tx)
		ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
		ksz_port_cfg(dev, mirror->to_local_port, offset,
			     PORT_MIRROR_SNIFFER, false);
}

@@ -1625,17 +1657,24 @@ void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port)
	const u16 *regs = dev->info->regs;
	struct dsa_switch *ds = dev->ds;
	const u32 *masks;
	int offset;
	u8 member;

	masks = dev->info->masks;

	/* enable broadcast storm limit */
	ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
	offset = P_BCAST_STORM_CTRL;
	if (ksz_is_ksz8463(dev))
		offset = P1CR1;
	ksz_port_cfg(dev, port, offset, PORT_BROADCAST_STORM, true);

	ksz8_port_queue_split(dev, port, dev->info->num_tx_queues);

	/* replace priority */
	ksz_port_cfg(dev, port, P_802_1P_CTRL,
	offset = P_802_1P_CTRL;
	if (ksz_is_ksz8463(dev))
		offset = P1CR2;
	ksz_port_cfg(dev, port, offset,
		     masks[PORT_802_1P_REMAPPING], false);

	if (cpu_port)
@@ -1675,6 +1714,7 @@ void ksz8_config_cpu_port(struct dsa_switch *ds)
	const u32 *masks;
	const u16 *regs;
	u8 remote;
	u8 fiber_ports = 0;
	int i;

	masks = dev->info->masks;
@@ -1705,6 +1745,32 @@ void ksz8_config_cpu_port(struct dsa_switch *ds)
		else
			ksz_port_cfg(dev, i, regs[P_STP_CTRL],
				     PORT_FORCE_FLOW_CTRL, false);
		if (p->fiber)
			fiber_ports |= (1 << i);
	}
	if (ksz_is_ksz8463(dev)) {
		/* Setup fiber ports. */
		if (fiber_ports) {
			fiber_ports &= 3;
			regmap_update_bits(ksz_regmap_16(dev),
					   KSZ8463_REG_CFG_CTRL,
					   fiber_ports << PORT_COPPER_MODE_S,
					   0);
			regmap_update_bits(ksz_regmap_16(dev),
					   KSZ8463_REG_DSP_CTRL_6,
					   COPPER_RECEIVE_ADJUSTMENT, 0);
		}

		/* Turn off PTP function as the switch's proprietary way of
		 * handling timestamp is not supported in current Linux PTP
		 * stack implementation.
		 */
		regmap_update_bits(ksz_regmap_16(dev),
				   KSZ8463_PTP_MSG_CONF1,
				   PTP_ENABLE, 0);
		regmap_update_bits(ksz_regmap_16(dev),
				   KSZ8463_PTP_CLK_CTRL,
				   PTP_CLK_ENABLE, 0);
	}
}

@@ -1901,7 +1967,7 @@ int ksz8_setup(struct dsa_switch *ds)

	ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);

	if (!ksz_is_ksz88x3(dev))
	if (!ksz_is_ksz88x3(dev) && !ksz_is_ksz8463(dev))
		ksz_cfg(dev, REG_SW_CTRL_19, SW_INS_TAG_ENABLE, true);

	for (i = 0; i < (dev->info->num_vlans / 4); i++)
@@ -1947,6 +2013,84 @@ u32 ksz8_get_port_addr(int port, int offset)
	return PORT_CTRL_ADDR(port, offset);
}

u32 ksz8463_get_port_addr(int port, int offset)
{
	return offset + 0x18 * port;
}

static u16 ksz8463_get_phy_addr(u16 phy, u16 reg, u16 offset)
{
	return offset + reg * 2 + phy * (P2MBCR - P1MBCR);
}

int ksz8463_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val)
{
	u16 sw_reg = 0;
	u16 data = 0;
	int ret;

	if (phy > 1)
		return -ENOSPC;
	switch (reg) {
	case MII_PHYSID1:
		sw_reg = ksz8463_get_phy_addr(phy, 0, PHY1IHR);
		break;
	case MII_PHYSID2:
		sw_reg = ksz8463_get_phy_addr(phy, 0, PHY1ILR);
		break;
	case MII_BMCR:
	case MII_BMSR:
	case MII_ADVERTISE:
	case MII_LPA:
		sw_reg = ksz8463_get_phy_addr(phy, reg, P1MBCR);
		break;
	case MII_TPISTATUS:
		/* This register holds the PHY interrupt status for simulated
		 * Micrel KSZ PHY.
		 */
		data = 0x0505;
		break;
	default:
		break;
	}
	if (sw_reg) {
		ret = ksz_read16(dev, sw_reg, &data);
		if (ret)
			return ret;
	}
	*val = data;

	return 0;
}

int ksz8463_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val)
{
	u16 sw_reg = 0;
	int ret;

	if (phy > 1)
		return -ENOSPC;

	/* No write to fiber port. */
	if (dev->ports[phy].fiber)
		return 0;
	switch (reg) {
	case MII_BMCR:
	case MII_ADVERTISE:
		sw_reg = ksz8463_get_phy_addr(phy, reg, P1MBCR);
		break;
	default:
		break;
	}
	if (sw_reg) {
		ret = ksz_write16(dev, sw_reg, val);
		if (ret)
			return ret;
	}

	return 0;
}

int ksz8_switch_init(struct ksz_device *dev)
{
	dev->cpu_port = fls(dev->info->cpu_ports) - 1;
+4 −0
Original line number Diff line number Diff line
@@ -63,4 +63,8 @@ void ksz8_phylink_mac_link_up(struct phylink_config *config,
			      bool tx_pause, bool rx_pause);
int ksz8_all_queues_split(struct ksz_device *dev, int queues);

u32 ksz8463_get_port_addr(int port, int offset);
int ksz8463_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val);
int ksz8463_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val);

#endif
+49 −0
Original line number Diff line number Diff line
@@ -729,6 +729,55 @@
#define PHY_POWER_SAVING_ENABLE		BIT(2)
#define PHY_REMOTE_LOOPBACK		BIT(1)

/* KSZ8463 specific registers. */
#define P1MBCR				0x4C
#define P1MBSR				0x4E
#define PHY1ILR				0x50
#define PHY1IHR				0x52
#define P1ANAR				0x54
#define P1ANLPR				0x56
#define P2MBCR				0x58
#define P2MBSR				0x5A
#define PHY2ILR				0x5C
#define PHY2IHR				0x5E
#define P2ANAR				0x60
#define P2ANLPR				0x62

#define P1CR1				0x6C
#define P1CR2				0x6E
#define P1CR3				0x72
#define P1CR4				0x7E
#define P1SR				0x80

#define KSZ8463_FLUSH_TABLE_CTRL	0xAD

#define KSZ8463_FLUSH_DYN_MAC_TABLE	BIT(2)
#define KSZ8463_FLUSH_STA_MAC_TABLE	BIT(1)

#define KSZ8463_REG_SW_CTRL_9		0xAE

#define KSZ8463_REG_CFG_CTRL		0xD8

#define PORT_2_COPPER_MODE		BIT(7)
#define PORT_1_COPPER_MODE		BIT(6)
#define PORT_COPPER_MODE_S		6

#define KSZ8463_REG_SW_RESET		0x126

#define KSZ8463_GLOBAL_SOFTWARE_RESET	BIT(0)

#define KSZ8463_PTP_CLK_CTRL		0x600

#define PTP_CLK_ENABLE			BIT(1)

#define KSZ8463_PTP_MSG_CONF1		0x620

#define PTP_ENABLE			BIT(6)

#define KSZ8463_REG_DSP_CTRL_6		0x734

#define COPPER_RECEIVE_ADJUSTMENT	BIT(13)

/* Chip resource */

#define PRIO_QUEUES			4
+153 −7
Original line number Diff line number Diff line
@@ -331,6 +331,38 @@ static const struct phylink_mac_ops ksz8_phylink_mac_ops = {
	.mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi,
};

static const struct ksz_dev_ops ksz8463_dev_ops = {
	.setup = ksz8_setup,
	.get_port_addr = ksz8463_get_port_addr,
	.cfg_port_member = ksz8_cfg_port_member,
	.flush_dyn_mac_table = ksz8_flush_dyn_mac_table,
	.port_setup = ksz8_port_setup,
	.r_phy = ksz8463_r_phy,
	.w_phy = ksz8463_w_phy,
	.r_mib_cnt = ksz8_r_mib_cnt,
	.r_mib_pkt = ksz8_r_mib_pkt,
	.r_mib_stat64 = ksz88xx_r_mib_stats64,
	.freeze_mib = ksz8_freeze_mib,
	.port_init_cnt = ksz8_port_init_cnt,
	.fdb_dump = ksz8_fdb_dump,
	.fdb_add = ksz8_fdb_add,
	.fdb_del = ksz8_fdb_del,
	.mdb_add = ksz8_mdb_add,
	.mdb_del = ksz8_mdb_del,
	.vlan_filtering = ksz8_port_vlan_filtering,
	.vlan_add = ksz8_port_vlan_add,
	.vlan_del = ksz8_port_vlan_del,
	.mirror_add = ksz8_port_mirror_add,
	.mirror_del = ksz8_port_mirror_del,
	.get_caps = ksz8_get_caps,
	.config_cpu_port = ksz8_config_cpu_port,
	.enable_stp_addr = ksz8_enable_stp_addr,
	.reset = ksz8_reset_switch,
	.init = ksz8_switch_init,
	.exit = ksz8_switch_exit,
	.change_mtu = ksz8_change_mtu,
};

static const struct ksz_dev_ops ksz88xx_dev_ops = {
	.setup = ksz8_setup,
	.get_port_addr = ksz8_get_port_addr,
@@ -517,6 +549,60 @@ static const struct ksz_dev_ops lan937x_dev_ops = {
	.exit = lan937x_switch_exit,
};

static const u16 ksz8463_regs[] = {
	[REG_SW_MAC_ADDR]		= 0x10,
	[REG_IND_CTRL_0]		= 0x30,
	[REG_IND_DATA_8]		= 0x26,
	[REG_IND_DATA_CHECK]		= 0x26,
	[REG_IND_DATA_HI]		= 0x28,
	[REG_IND_DATA_LO]		= 0x2C,
	[REG_IND_MIB_CHECK]		= 0x2F,
	[P_FORCE_CTRL]			= 0x0C,
	[P_LINK_STATUS]			= 0x0E,
	[P_LOCAL_CTRL]			= 0x0C,
	[P_NEG_RESTART_CTRL]		= 0x0D,
	[P_REMOTE_STATUS]		= 0x0E,
	[P_SPEED_STATUS]		= 0x0F,
	[S_TAIL_TAG_CTRL]		= 0xAD,
	[P_STP_CTRL]			= 0x6F,
	[S_START_CTRL]			= 0x01,
	[S_BROADCAST_CTRL]		= 0x06,
	[S_MULTICAST_CTRL]		= 0x04,
};

static const u32 ksz8463_masks[] = {
	[PORT_802_1P_REMAPPING]		= BIT(3),
	[SW_TAIL_TAG_ENABLE]		= BIT(0),
	[MIB_COUNTER_OVERFLOW]		= BIT(7),
	[MIB_COUNTER_VALID]		= BIT(6),
	[VLAN_TABLE_FID]		= GENMASK(15, 12),
	[VLAN_TABLE_MEMBERSHIP]		= GENMASK(18, 16),
	[VLAN_TABLE_VALID]		= BIT(19),
	[STATIC_MAC_TABLE_VALID]	= BIT(19),
	[STATIC_MAC_TABLE_USE_FID]	= BIT(21),
	[STATIC_MAC_TABLE_FID]		= GENMASK(25, 22),
	[STATIC_MAC_TABLE_OVERRIDE]	= BIT(20),
	[STATIC_MAC_TABLE_FWD_PORTS]	= GENMASK(18, 16),
	[DYNAMIC_MAC_TABLE_ENTRIES_H]	= GENMASK(1, 0),
	[DYNAMIC_MAC_TABLE_MAC_EMPTY]	= BIT(2),
	[DYNAMIC_MAC_TABLE_NOT_READY]	= BIT(7),
	[DYNAMIC_MAC_TABLE_ENTRIES]	= GENMASK(31, 24),
	[DYNAMIC_MAC_TABLE_FID]		= GENMASK(19, 16),
	[DYNAMIC_MAC_TABLE_SRC_PORT]	= GENMASK(21, 20),
	[DYNAMIC_MAC_TABLE_TIMESTAMP]	= GENMASK(23, 22),
};

static u8 ksz8463_shifts[] = {
	[VLAN_TABLE_MEMBERSHIP_S]	= 16,
	[STATIC_MAC_FWD_PORTS]		= 16,
	[STATIC_MAC_FID]		= 22,
	[DYNAMIC_MAC_ENTRIES_H]		= 8,
	[DYNAMIC_MAC_ENTRIES]		= 24,
	[DYNAMIC_MAC_FID]		= 16,
	[DYNAMIC_MAC_TIMESTAMP]		= 22,
	[DYNAMIC_MAC_SRC_PORT]		= 20,
};

static const u16 ksz8795_regs[] = {
	[REG_SW_MAC_ADDR]		= 0x68,
	[REG_IND_CTRL_0]		= 0x6E,
@@ -1387,6 +1473,29 @@ static const struct regmap_access_table ksz8873_register_set = {
};

const struct ksz_chip_data ksz_switch_chips[] = {
	[KSZ8463] = {
		.chip_id = KSZ8463_CHIP_ID,
		.dev_name = "KSZ8463",
		.num_vlans = 16,
		.num_alus = 0,
		.num_statics = 8,
		.cpu_ports = 0x4,	/* can be configured as cpu port */
		.port_cnt = 3,
		.num_tx_queues = 4,
		.num_ipms = 4,
		.ops = &ksz8463_dev_ops,
		.phylink_mac_ops = &ksz88x3_phylink_mac_ops,
		.mib_names = ksz88xx_mib_names,
		.mib_cnt = ARRAY_SIZE(ksz88xx_mib_names),
		.reg_mib_cnt = MIB_COUNTER_NUM,
		.regs = ksz8463_regs,
		.masks = ksz8463_masks,
		.shifts = ksz8463_shifts,
		.supports_mii = {false, false, true},
		.supports_rmii = {false, false, true},
		.internal_phy = {true, true, false},
	},

	[KSZ8563] = {
		.chip_id = KSZ8563_CHIP_ID,
		.dev_name = "KSZ8563",
@@ -2842,6 +2951,7 @@ static int ksz_parse_drive_strength(struct ksz_device *dev);
static int ksz_setup(struct dsa_switch *ds)
{
	struct ksz_device *dev = ds->priv;
	u16 storm_mask, storm_rate;
	struct dsa_port *dp;
	struct ksz_port *p;
	const u16 *regs;
@@ -2871,10 +2981,14 @@ static int ksz_setup(struct dsa_switch *ds)
	}

	/* set broadcast storm protection 10% rate */
	storm_mask = BROADCAST_STORM_RATE;
	storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
	if (ksz_is_ksz8463(dev)) {
		storm_mask = swab16(storm_mask);
		storm_rate = swab16(storm_rate);
	}
	regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL],
			   BROADCAST_STORM_RATE,
			   (BROADCAST_STORM_VALUE *
			   BROADCAST_STORM_PROT_RATE) / 100);
			   storm_mask, storm_rate);

	dev->dev_ops->config_cpu_port(ds);

@@ -3400,6 +3514,7 @@ static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds,
		proto = DSA_TAG_PROTO_KSZ8795;

	if (dev->chip_id == KSZ88X3_CHIP_ID ||
	    dev->chip_id == KSZ8463_CHIP_ID ||
	    dev->chip_id == KSZ8563_CHIP_ID ||
	    dev->chip_id == KSZ9893_CHIP_ID ||
	    dev->chip_id == KSZ9563_CHIP_ID)
@@ -3512,6 +3627,7 @@ static int ksz_max_mtu(struct dsa_switch *ds, int port)
	case KSZ8794_CHIP_ID:
	case KSZ8765_CHIP_ID:
		return KSZ8795_HUGE_PACKET_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN;
	case KSZ8463_CHIP_ID:
	case KSZ88X3_CHIP_ID:
	case KSZ8864_CHIP_ID:
	case KSZ8895_CHIP_ID:
@@ -3866,6 +3982,9 @@ static int ksz_switch_detect(struct ksz_device *dev)
	id2 = FIELD_GET(SW_CHIP_ID_M, id16);

	switch (id1) {
	case KSZ84_FAMILY_ID:
		dev->chip_id = KSZ8463_CHIP_ID;
		break;
	case KSZ87_FAMILY_ID:
		if (id2 == KSZ87_CHIP_ID_95) {
			u8 val;
@@ -4107,6 +4226,17 @@ static int ksz_ets_band_to_queue(struct tc_ets_qopt_offload_replace_params *p,
	return p->bands - 1 - band;
}

static u8 ksz8463_tc_ctrl(int port, int queue)
{
	u8 reg;

	reg = 0xC8 + port * 4;
	reg += ((3 - queue) / 2) * 2;
	reg++;
	reg -= (queue & 1);
	return reg;
}

/**
 * ksz88x3_tc_ets_add - Configure ETS (Enhanced Transmission Selection)
 *                      for a port on KSZ88x3 switch
@@ -4142,6 +4272,8 @@ static int ksz88x3_tc_ets_add(struct ksz_device *dev, int port,
		 * port/queue
		 */
		reg = KSZ8873_TXQ_SPLIT_CTRL_REG(port, queue);
		if (ksz_is_ksz8463(dev))
			reg = ksz8463_tc_ctrl(port, queue);

		/* Clear WFQ enable bit to select strict priority scheduling */
		ret = ksz_rmw8(dev, reg, KSZ8873_TXQ_WFQ_ENABLE, 0);
@@ -4177,6 +4309,8 @@ static int ksz88x3_tc_ets_del(struct ksz_device *dev, int port)
		 * port/queue
		 */
		reg = KSZ8873_TXQ_SPLIT_CTRL_REG(port, queue);
		if (ksz_is_ksz8463(dev))
			reg = ksz8463_tc_ctrl(port, queue);

		/* Set WFQ enable bit to revert back to default scheduling
		 * mode
@@ -4324,7 +4458,7 @@ static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port,
	struct ksz_device *dev = ds->priv;
	int ret;

	if (is_ksz8(dev) && !ksz_is_ksz88x3(dev))
	if (is_ksz8(dev) && !(ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)))
		return -EOPNOTSUPP;

	if (qopt->parent != TC_H_ROOT) {
@@ -4338,13 +4472,13 @@ static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port,
		if (ret)
			return ret;

		if (ksz_is_ksz88x3(dev))
		if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))
			return ksz88x3_tc_ets_add(dev, port,
						  &qopt->replace_params);
		else
			return ksz_tc_ets_add(dev, port, &qopt->replace_params);
	case TC_ETS_DESTROY:
		if (ksz_is_ksz88x3(dev))
		if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))
			return ksz88x3_tc_ets_del(dev, port);
		else
			return ksz_tc_ets_del(dev, port);
@@ -4687,7 +4821,16 @@ int ksz_switch_macaddr_get(struct dsa_switch *ds, int port,

	/* Program the switch MAC address to hardware */
	for (i = 0; i < ETH_ALEN; i++) {
		ret = ksz_write8(dev, regs[REG_SW_MAC_ADDR] + i, addr[i]);
		if (ksz_is_ksz8463(dev)) {
			u16 addr16 = ((u16)addr[i] << 8) | addr[i + 1];

			ret = ksz_write16(dev, regs[REG_SW_MAC_ADDR] + i,
					  addr16);
			i++;
		} else {
			ret = ksz_write8(dev, regs[REG_SW_MAC_ADDR] + i,
					 addr[i]);
		}
		if (ret)
			goto macaddr_drop;
	}
@@ -5296,6 +5439,9 @@ int ksz_switch_register(struct ksz_device *dev)
						&dev->ports[port_num].interface);

				ksz_parse_rgmii_delay(dev, port_num, port);
				dev->ports[port_num].fiber =
					of_property_read_bool(port,
							      "micrel,fiber-mode");
			}
			of_node_put(ports);
		}
Loading