diff options
Diffstat (limited to 'drivers/net/dsa')
41 files changed, 2136 insertions, 981 deletions
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index f6f3b43dfb06..3ed5391bb18d 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -38,10 +38,34 @@ config NET_DSA_MT7530  	tristate "MediaTek MT7530 and MT7531 Ethernet switch support"  	select NET_DSA_TAG_MTK  	select MEDIATEK_GE_PHY +	imply NET_DSA_MT7530_MDIO +	imply NET_DSA_MT7530_MMIO  	help  	  This enables support for the MediaTek MT7530 and MT7531 Ethernet  	  switch chips. Multi-chip module MT7530 in MT7621AT, MT7621DAT, -	  MT7621ST and MT7623AI SoCs is supported. +	  MT7621ST and MT7623AI SoCs, and built-in switch in MT7988 SoC are +	  supported as well. + +config NET_DSA_MT7530_MDIO +	tristate "MediaTek MT7530 MDIO interface driver" +	depends on NET_DSA_MT7530 +	select PCS_MTK_LYNXI +	help +	  This enables support for the MediaTek MT7530 and MT7531 switch +	  chips which are connected via MDIO, as well as multi-chip +	  module MT7530 which can be found in the MT7621AT, MT7621DAT, +	  MT7621ST and MT7623AI SoCs. + +config NET_DSA_MT7530_MMIO +	tristate "MediaTek MT7530 MMIO interface driver" +	depends on NET_DSA_MT7530 +	depends on HAS_IOMEM +	help +	  This enables support for the built-in Ethernet switch found +	  in the MediaTek MT7988 SoC. +	  The switch is a similar design as MT7531, but the switch registers +	  are directly mapped into the SoCs register space rather than being +	  accessible via MDIO.  config NET_DSA_MV88E6060  	tristate "Marvell 88E6060 ethernet switch chip support" diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index 16eb879e0cb4..cb9a97340e58 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -7,6 +7,8 @@ obj-$(CONFIG_FIXED_PHY)		+= dsa_loop_bdinfo.o  endif  obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o  obj-$(CONFIG_NET_DSA_MT7530)	+= mt7530.o +obj-$(CONFIG_NET_DSA_MT7530_MDIO) += mt7530-mdio.o +obj-$(CONFIG_NET_DSA_MT7530_MMIO) += mt7530-mmio.o  obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o  obj-$(CONFIG_NET_DSA_RZN1_A5PSW) += rzn1_a5psw.o  obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 59cdfc51ce06..3464ce5e7470 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1209,6 +1209,50 @@ static void b53_force_port_config(struct b53_device *dev, int port,  	b53_write8(dev, B53_CTRL_PAGE, off, reg);  } +static void b53_adjust_63xx_rgmii(struct dsa_switch *ds, int port, +				  phy_interface_t interface) +{ +	struct b53_device *dev = ds->priv; +	u8 rgmii_ctrl = 0, off; + +	if (port == dev->imp_port) +		off = B53_RGMII_CTRL_IMP; +	else +		off = B53_RGMII_CTRL_P(port); + +	b53_read8(dev, B53_CTRL_PAGE, off, &rgmii_ctrl); + +	switch (interface) { +	case PHY_INTERFACE_MODE_RGMII_ID: +		rgmii_ctrl |= (RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC); +		break; +	case PHY_INTERFACE_MODE_RGMII_RXID: +		rgmii_ctrl &= ~(RGMII_CTRL_DLL_TXC); +		rgmii_ctrl |= RGMII_CTRL_DLL_RXC; +		break; +	case PHY_INTERFACE_MODE_RGMII_TXID: +		rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC); +		rgmii_ctrl |= RGMII_CTRL_DLL_TXC; +		break; +	case PHY_INTERFACE_MODE_RGMII: +	default: +		rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC); +		break; +	} + +	if (port != dev->imp_port) { +		if (is63268(dev)) +			rgmii_ctrl |= RGMII_CTRL_MII_OVERRIDE; + +		rgmii_ctrl |= RGMII_CTRL_ENABLE_GMII; +	} + +	b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl); + +	dev_dbg(ds->dev, "Configured port %d for %s\n", port, +		phy_modes(interface)); +} +  static void b53_adjust_link(struct dsa_switch *ds, int port,  			    struct phy_device *phydev)  { @@ -1235,6 +1279,9 @@ static void b53_adjust_link(struct dsa_switch *ds, int port,  			      tx_pause, rx_pause);  	b53_force_link(dev, port, phydev->link); +	if (is63xx(dev) && port >= B53_63XX_RGMII0) +		b53_adjust_63xx_rgmii(ds, port, phydev->interface); +  	if (is531x5(dev) && phy_interface_is_rgmii(phydev)) {  		if (port == dev->imp_port)  			off = B53_RGMII_CTRL_IMP; @@ -1402,6 +1449,9 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,  {  	struct b53_device *dev = ds->priv; +	if (is63xx(dev) && port >= B53_63XX_RGMII0) +		b53_adjust_63xx_rgmii(ds, port, interface); +  	if (mode == MLO_AN_PHY)  		return; @@ -2420,6 +2470,19 @@ static const struct b53_chip_data b53_switch_chips[] = {  		.jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX,  	},  	{ +		.chip_id = BCM63268_DEVICE_ID, +		.dev_name = "BCM63268", +		.vlans = 4096, +		.enabled_ports = 0, /* pdata must provide them */ +		.arl_bins = 4, +		.arl_buckets = 1024, +		.imp_port = 8, +		.vta_regs = B53_VTA_REGS_63XX, +		.duplex_reg = B53_DUPLEX_STAT_63XX, +		.jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX, +		.jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX, +	}, +	{  		.chip_id = BCM53010_DEVICE_ID,  		.dev_name = "BCM53010",  		.vlans = 4096, @@ -2550,6 +2613,20 @@ static const struct b53_chip_data b53_switch_chips[] = {  		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,  		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,  	}, +	{ +		.chip_id = BCM53134_DEVICE_ID, +		.dev_name = "BCM53134", +		.vlans = 4096, +		.enabled_ports = 0x12f, +		.imp_port = 8, +		.cpu_port = B53_CPU_PORT, +		.vta_regs = B53_VTA_REGS, +		.arl_bins = 4, +		.arl_buckets = 1024, +		.duplex_reg = B53_DUPLEX_STAT_GE, +		.jumbo_pm_reg = B53_JUMBO_PORT_MASK, +		.jumbo_size_reg = B53_JUMBO_MAX_SIZE, +	},  };  static int b53_switch_init(struct b53_device *dev) @@ -2727,6 +2804,7 @@ int b53_switch_detect(struct b53_device *dev)  		case BCM53012_DEVICE_ID:  		case BCM53018_DEVICE_ID:  		case BCM53019_DEVICE_ID: +		case BCM53134_DEVICE_ID:  			dev->chip_id = id32;  			break;  		default: diff --git a/drivers/net/dsa/b53/b53_mdio.c b/drivers/net/dsa/b53/b53_mdio.c index 6ddc03b58b28..8b422b298cd5 100644 --- a/drivers/net/dsa/b53/b53_mdio.c +++ b/drivers/net/dsa/b53/b53_mdio.c @@ -286,6 +286,7 @@ static const struct b53_io_ops b53_mdio_ops = {  #define B53_BRCM_OUI_2	0x03625c00  #define B53_BRCM_OUI_3	0x00406000  #define B53_BRCM_OUI_4	0x01410c00 +#define B53_BRCM_OUI_5	0xae025000  static int b53_mdio_probe(struct mdio_device *mdiodev)  { @@ -313,7 +314,8 @@ static int b53_mdio_probe(struct mdio_device *mdiodev)  	if ((phy_id & 0xfffffc00) != B53_BRCM_OUI_1 &&  	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_2 &&  	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_3 && -	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_4) { +	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_4 && +	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_5) {  		dev_err(&mdiodev->dev, "Unsupported device: 0x%08x\n", phy_id);  		return -ENODEV;  	} @@ -375,6 +377,7 @@ static const struct of_device_id b53_of_match[] = {  	{ .compatible = "brcm,bcm53115" },  	{ .compatible = "brcm,bcm53125" },  	{ .compatible = "brcm,bcm53128" }, +	{ .compatible = "brcm,bcm53134" },  	{ .compatible = "brcm,bcm5365" },  	{ .compatible = "brcm,bcm5389" },  	{ .compatible = "brcm,bcm5395" }, diff --git a/drivers/net/dsa/b53/b53_mmap.c b/drivers/net/dsa/b53/b53_mmap.c index e968322dfbf0..5db1ed26f03a 100644 --- a/drivers/net/dsa/b53/b53_mmap.c +++ b/drivers/net/dsa/b53/b53_mmap.c @@ -216,6 +216,18 @@ static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg,  	return 0;  } +static int b53_mmap_phy_read16(struct b53_device *dev, int addr, int reg, +			       u16 *value) +{ +	return -EIO; +} + +static int b53_mmap_phy_write16(struct b53_device *dev, int addr, int reg, +				u16 value) +{ +	return -EIO; +} +  static const struct b53_io_ops b53_mmap_ops = {  	.read8 = b53_mmap_read8,  	.read16 = b53_mmap_read16, @@ -227,6 +239,8 @@ static const struct b53_io_ops b53_mmap_ops = {  	.write32 = b53_mmap_write32,  	.write48 = b53_mmap_write48,  	.write64 = b53_mmap_write64, +	.phy_read16 = b53_mmap_phy_read16, +	.phy_write16 = b53_mmap_phy_write16,  };  static int b53_mmap_probe_of(struct platform_device *pdev, @@ -248,7 +262,7 @@ static int b53_mmap_probe_of(struct platform_device *pdev,  		return -ENOMEM;  	pdata->regs = mem; -	pdata->chip_id = BCM63XX_DEVICE_ID; +	pdata->chip_id = (u32)(unsigned long)device_get_match_data(dev);  	pdata->big_endian = of_property_read_bool(np, "big-endian");  	of_ports = of_get_child_by_name(np, "ports"); @@ -263,7 +277,7 @@ static int b53_mmap_probe_of(struct platform_device *pdev,  		if (of_property_read_u32(of_port, "reg", ®))  			continue; -		if (reg < B53_CPU_PORT) +		if (reg < B53_N_PORTS)  			pdata->enabled_ports |= BIT(reg);  	} @@ -330,11 +344,28 @@ static void b53_mmap_shutdown(struct platform_device *pdev)  }  static const struct of_device_id b53_mmap_of_table[] = { -	{ .compatible = "brcm,bcm3384-switch" }, -	{ .compatible = "brcm,bcm6328-switch" }, -	{ .compatible = "brcm,bcm6368-switch" }, -	{ .compatible = "brcm,bcm63xx-switch" }, -	{ /* sentinel */ }, +	{ +		.compatible = "brcm,bcm3384-switch", +		.data = (void *)BCM63XX_DEVICE_ID, +	}, { +		.compatible = "brcm,bcm6318-switch", +		.data = (void *)BCM63268_DEVICE_ID, +	}, { +		.compatible = "brcm,bcm6328-switch", +		.data = (void *)BCM63XX_DEVICE_ID, +	}, { +		.compatible = "brcm,bcm6362-switch", +		.data = (void *)BCM63XX_DEVICE_ID, +	}, { +		.compatible = "brcm,bcm6368-switch", +		.data = (void *)BCM63XX_DEVICE_ID, +	}, { +		.compatible = "brcm,bcm63268-switch", +		.data = (void *)BCM63268_DEVICE_ID, +	}, { +		.compatible = "brcm,bcm63xx-switch", +		.data = (void *)BCM63XX_DEVICE_ID, +	}, { /* sentinel */ }  };  MODULE_DEVICE_TABLE(of, b53_mmap_of_table); diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 795cbffd5c2b..fdcfd5081c28 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -70,6 +70,7 @@ enum {  	BCM53125_DEVICE_ID = 0x53125,  	BCM53128_DEVICE_ID = 0x53128,  	BCM63XX_DEVICE_ID = 0x6300, +	BCM63268_DEVICE_ID = 0x63268,  	BCM53010_DEVICE_ID = 0x53010,  	BCM53011_DEVICE_ID = 0x53011,  	BCM53012_DEVICE_ID = 0x53012, @@ -79,6 +80,7 @@ enum {  	BCM583XX_DEVICE_ID = 0x58300,  	BCM7445_DEVICE_ID = 0x7445,  	BCM7278_DEVICE_ID = 0x7278, +	BCM53134_DEVICE_ID = 0x5075,  };  struct b53_pcs { @@ -186,12 +188,19 @@ static inline int is531x5(struct b53_device *dev)  {  	return dev->chip_id == BCM53115_DEVICE_ID ||  		dev->chip_id == BCM53125_DEVICE_ID || -		dev->chip_id == BCM53128_DEVICE_ID; +		dev->chip_id == BCM53128_DEVICE_ID || +		dev->chip_id == BCM53134_DEVICE_ID;  }  static inline int is63xx(struct b53_device *dev)  { -	return dev->chip_id == BCM63XX_DEVICE_ID; +	return dev->chip_id == BCM63XX_DEVICE_ID || +		dev->chip_id == BCM63268_DEVICE_ID; +} + +static inline int is63268(struct b53_device *dev) +{ +	return dev->chip_id == BCM63268_DEVICE_ID;  }  static inline int is5301x(struct b53_device *dev) @@ -208,9 +217,11 @@ static inline int is58xx(struct b53_device *dev)  	return dev->chip_id == BCM58XX_DEVICE_ID ||  		dev->chip_id == BCM583XX_DEVICE_ID ||  		dev->chip_id == BCM7445_DEVICE_ID || -		dev->chip_id == BCM7278_DEVICE_ID; +		dev->chip_id == BCM7278_DEVICE_ID || +		dev->chip_id == BCM53134_DEVICE_ID;  } +#define B53_63XX_RGMII0	4  #define B53_CPU_PORT_25	5  #define B53_CPU_PORT	8 diff --git a/drivers/net/dsa/b53/b53_regs.h b/drivers/net/dsa/b53/b53_regs.h index b2c539a42154..bfbcb66bef66 100644 --- a/drivers/net/dsa/b53/b53_regs.h +++ b/drivers/net/dsa/b53/b53_regs.h @@ -138,6 +138,7 @@  #define B53_RGMII_CTRL_IMP		0x60  #define   RGMII_CTRL_ENABLE_GMII	BIT(7) +#define   RGMII_CTRL_MII_OVERRIDE	BIT(6)  #define   RGMII_CTRL_TIMING_SEL		BIT(2)  #define   RGMII_CTRL_DLL_RXC		BIT(1)  #define   RGMII_CTRL_DLL_TXC		BIT(0) diff --git a/drivers/net/dsa/hirschmann/hellcreek_ptp.c b/drivers/net/dsa/hirschmann/hellcreek_ptp.c index b28baab6d56a..3e44ccb7db84 100644 --- a/drivers/net/dsa/hirschmann/hellcreek_ptp.c +++ b/drivers/net/dsa/hirschmann/hellcreek_ptp.c @@ -297,7 +297,8 @@ static enum led_brightness hellcreek_led_is_gm_get(struct led_classdev *ldev)  static int hellcreek_led_setup(struct hellcreek *hellcreek)  {  	struct device_node *leds, *led = NULL; -	const char *label, *state; +	enum led_default_state state; +	const char *label;  	int ret = -EINVAL;  	of_node_get(hellcreek->dev->of_node); @@ -318,16 +319,17 @@ static int hellcreek_led_setup(struct hellcreek *hellcreek)  	ret = of_property_read_string(led, "label", &label);  	hellcreek->led_sync_good.name = ret ? "sync_good" : label; -	ret = of_property_read_string(led, "default-state", &state); -	if (!ret) { -		if (!strcmp(state, "on")) -			hellcreek->led_sync_good.brightness = 1; -		else if (!strcmp(state, "off")) -			hellcreek->led_sync_good.brightness = 0; -		else if (!strcmp(state, "keep")) -			hellcreek->led_sync_good.brightness = -				hellcreek_get_brightness(hellcreek, -							 STATUS_OUT_SYNC_GOOD); +	state = led_init_default_state_get(of_fwnode_handle(led)); +	switch (state) { +	case LEDS_DEFSTATE_ON: +		hellcreek->led_sync_good.brightness = 1; +		break; +	case LEDS_DEFSTATE_KEEP: +		hellcreek->led_sync_good.brightness = +			hellcreek_get_brightness(hellcreek, STATUS_OUT_SYNC_GOOD); +		break; +	default: +		hellcreek->led_sync_good.brightness = 0;  	}  	hellcreek->led_sync_good.max_brightness = 1; @@ -344,16 +346,17 @@ static int hellcreek_led_setup(struct hellcreek *hellcreek)  	ret = of_property_read_string(led, "label", &label);  	hellcreek->led_is_gm.name = ret ? "is_gm" : label; -	ret = of_property_read_string(led, "default-state", &state); -	if (!ret) { -		if (!strcmp(state, "on")) -			hellcreek->led_is_gm.brightness = 1; -		else if (!strcmp(state, "off")) -			hellcreek->led_is_gm.brightness = 0; -		else if (!strcmp(state, "keep")) -			hellcreek->led_is_gm.brightness = -				hellcreek_get_brightness(hellcreek, -							 STATUS_OUT_IS_GM); +	state = led_init_default_state_get(of_fwnode_handle(led)); +	switch (state) { +	case LEDS_DEFSTATE_ON: +		hellcreek->led_is_gm.brightness = 1; +		break; +	case LEDS_DEFSTATE_KEEP: +		hellcreek->led_is_gm.brightness = +			hellcreek_get_brightness(hellcreek, STATUS_OUT_IS_GM); +		break; +	default: +		hellcreek->led_is_gm.brightness = 0;  	}  	hellcreek->led_is_gm.max_brightness = 1; diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index cbe831875347..c0215a8770f4 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -1188,8 +1188,6 @@ static int lan9303_port_fdb_add(struct dsa_switch *ds, int port,  	struct lan9303 *chip = ds->priv;  	dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, addr, vid); -	if (vid) -		return -EOPNOTSUPP;  	return lan9303_alr_add_port(chip, addr, port, false);  } @@ -1201,8 +1199,6 @@ static int lan9303_port_fdb_del(struct dsa_switch *ds, int port,  	struct lan9303 *chip = ds->priv;  	dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, addr, vid); -	if (vid) -		return -EOPNOTSUPP;  	lan9303_alr_del_port(chip, addr, port);  	return 0; diff --git a/drivers/net/dsa/lan9303_i2c.c b/drivers/net/dsa/lan9303_i2c.c index 1cb41c36bd47..e8844820c3a9 100644 --- a/drivers/net/dsa/lan9303_i2c.c +++ b/drivers/net/dsa/lan9303_i2c.c @@ -103,7 +103,7 @@ MODULE_DEVICE_TABLE(of, lan9303_i2c_of_match);  static struct i2c_driver lan9303_i2c_driver = {  	.driver = {  		.name = "LAN9303_I2C", -		.of_match_table = of_match_ptr(lan9303_i2c_of_match), +		.of_match_table = lan9303_i2c_of_match,  	},  	.probe_new = lan9303_i2c_probe,  	.remove = lan9303_i2c_remove, diff --git a/drivers/net/dsa/lan9303_mdio.c b/drivers/net/dsa/lan9303_mdio.c index 4f33369a2de5..d8ab2b77d201 100644 --- a/drivers/net/dsa/lan9303_mdio.c +++ b/drivers/net/dsa/lan9303_mdio.c @@ -164,7 +164,7 @@ MODULE_DEVICE_TABLE(of, lan9303_mdio_of_match);  static struct mdio_driver lan9303_mdio_driver = {  	.mdiodrv.driver = {  		.name = "LAN9303_MDIO", -		.of_match_table = of_match_ptr(lan9303_mdio_of_match), +		.of_match_table = lan9303_mdio_of_match,  	},  	.probe  = lan9303_mdio_probe,  	.remove = lan9303_mdio_remove, diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 05ecaa007ab1..3c76a1a14aee 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -1885,7 +1885,7 @@ static const struct xway_gphy_match_data xrx300_gphy_data = {  	.ge_firmware_name = "lantiq/xrx300_phy11g_a21.bin",  }; -static const struct of_device_id xway_gphy_match[] = { +static const struct of_device_id xway_gphy_match[] __maybe_unused = {  	{ .compatible = "lantiq,xrx200-gphy-fw", .data = NULL },  	{ .compatible = "lantiq,xrx200a1x-gphy-fw", .data = &xrx200a1x_gphy_data },  	{ .compatible = "lantiq,xrx200a2x-gphy-fw", .data = &xrx200a2x_gphy_data }, diff --git a/drivers/net/dsa/microchip/ksz8.h b/drivers/net/dsa/microchip/ksz8.h index ea05abfbd51d..e68465fdf6b9 100644 --- a/drivers/net/dsa/microchip/ksz8.h +++ b/drivers/net/dsa/microchip/ksz8.h @@ -21,10 +21,6 @@ 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); -int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr, -			 struct alu_struct *alu); -void ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr, -			  struct alu_struct *alu);  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); @@ -32,6 +28,10 @@ void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze);  void ksz8_port_init_cnt(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 ksz8_fdb_add(struct ksz_device *dev, int port, const unsigned char *addr, +		 u16 vid, struct dsa_db db); +int ksz8_fdb_del(struct ksz_device *dev, int port, const unsigned char *addr, +		 u16 vid, struct dsa_db db);  int ksz8_mdb_add(struct ksz_device *dev, int port,  		 const struct switchdev_obj_port_mdb *mdb, struct dsa_db db);  int ksz8_mdb_del(struct ksz_device *dev, int port, diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index 003b0ac2854c..f56fca1b1a22 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -96,7 +96,7 @@ static int ksz8795_change_mtu(struct ksz_device *dev, int frame_size)  	if (frame_size > KSZ8_LEGAL_PACKET_SIZE)  		ctrl2 |= SW_LEGAL_PACKET_DISABLE; -	else if (frame_size > KSZ8863_NORMAL_PACKET_SIZE) +	if (frame_size > KSZ8863_NORMAL_PACKET_SIZE)  		ctrl1 |= SW_HUGE_PACKET;  	ret = ksz_rmw8(dev, REG_SW_CTRL_1, SW_HUGE_PACKET, ctrl1); @@ -336,34 +336,48 @@ void ksz8_port_init_cnt(struct ksz_device *dev, int port)  	}  } -static void ksz8_r_table(struct ksz_device *dev, int table, u16 addr, u64 *data) +static int ksz8_r_table(struct ksz_device *dev, int table, u16 addr, u64 *data)  {  	const u16 *regs;  	u16 ctrl_addr; +	int ret;  	regs = dev->info->regs;  	ctrl_addr = IND_ACC_TABLE(table | TABLE_READ) | addr;  	mutex_lock(&dev->alu_mutex); -	ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); -	ksz_read64(dev, regs[REG_IND_DATA_HI], data); +	ret = ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); +	if (ret) +		goto unlock_alu; + +	ret = ksz_read64(dev, regs[REG_IND_DATA_HI], data); +unlock_alu:  	mutex_unlock(&dev->alu_mutex); + +	return ret;  } -static void ksz8_w_table(struct ksz_device *dev, int table, u16 addr, u64 data) +static int ksz8_w_table(struct ksz_device *dev, int table, u16 addr, u64 data)  {  	const u16 *regs;  	u16 ctrl_addr; +	int ret;  	regs = dev->info->regs;  	ctrl_addr = IND_ACC_TABLE(table) | addr;  	mutex_lock(&dev->alu_mutex); -	ksz_write64(dev, regs[REG_IND_DATA_HI], data); -	ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); +	ret = ksz_write64(dev, regs[REG_IND_DATA_HI], data); +	if (ret) +		goto unlock_alu; + +	ret = ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); +unlock_alu:  	mutex_unlock(&dev->alu_mutex); + +	return ret;  }  static int ksz8_valid_dyn_entry(struct ksz_device *dev, u8 *data) @@ -457,46 +471,54 @@ int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr,  	return rc;  } -int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr, -			 struct alu_struct *alu) +static int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr, +				struct alu_struct *alu, bool *valid)  {  	u32 data_hi, data_lo;  	const u8 *shifts;  	const u32 *masks;  	u64 data; +	int ret;  	shifts = dev->info->shifts;  	masks = dev->info->masks; -	ksz8_r_table(dev, TABLE_STATIC_MAC, addr, &data); +	ret = ksz8_r_table(dev, TABLE_STATIC_MAC, addr, &data); +	if (ret) +		return ret; +  	data_hi = data >> 32;  	data_lo = (u32)data; -	if (data_hi & (masks[STATIC_MAC_TABLE_VALID] | -			masks[STATIC_MAC_TABLE_OVERRIDE])) { -		alu->mac[5] = (u8)data_lo; -		alu->mac[4] = (u8)(data_lo >> 8); -		alu->mac[3] = (u8)(data_lo >> 16); -		alu->mac[2] = (u8)(data_lo >> 24); -		alu->mac[1] = (u8)data_hi; -		alu->mac[0] = (u8)(data_hi >> 8); -		alu->port_forward = -			(data_hi & masks[STATIC_MAC_TABLE_FWD_PORTS]) >> -				shifts[STATIC_MAC_FWD_PORTS]; -		alu->is_override = -			(data_hi & masks[STATIC_MAC_TABLE_OVERRIDE]) ? 1 : 0; -		data_hi >>= 1; -		alu->is_static = true; -		alu->is_use_fid = -			(data_hi & masks[STATIC_MAC_TABLE_USE_FID]) ? 1 : 0; -		alu->fid = (data_hi & masks[STATIC_MAC_TABLE_FID]) >> -				shifts[STATIC_MAC_FID]; + +	if (!(data_hi & (masks[STATIC_MAC_TABLE_VALID] | +			 masks[STATIC_MAC_TABLE_OVERRIDE]))) { +		*valid = false;  		return 0;  	} -	return -ENXIO; + +	alu->mac[5] = (u8)data_lo; +	alu->mac[4] = (u8)(data_lo >> 8); +	alu->mac[3] = (u8)(data_lo >> 16); +	alu->mac[2] = (u8)(data_lo >> 24); +	alu->mac[1] = (u8)data_hi; +	alu->mac[0] = (u8)(data_hi >> 8); +	alu->port_forward = +		(data_hi & masks[STATIC_MAC_TABLE_FWD_PORTS]) >> +			shifts[STATIC_MAC_FWD_PORTS]; +	alu->is_override = (data_hi & masks[STATIC_MAC_TABLE_OVERRIDE]) ? 1 : 0; +	data_hi >>= 1; +	alu->is_static = true; +	alu->is_use_fid = (data_hi & masks[STATIC_MAC_TABLE_USE_FID]) ? 1 : 0; +	alu->fid = (data_hi & masks[STATIC_MAC_TABLE_FID]) >> +		shifts[STATIC_MAC_FID]; + +	*valid = true; + +	return 0;  } -void ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr, -			  struct alu_struct *alu) +static int ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr, +				struct alu_struct *alu)  {  	u32 data_hi, data_lo;  	const u8 *shifts; @@ -524,7 +546,8 @@ void ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr,  		data_hi &= ~masks[STATIC_MAC_TABLE_OVERRIDE];  	data = (u64)data_hi << 32 | data_lo; -	ksz8_w_table(dev, TABLE_STATIC_MAC, addr, data); + +	return ksz8_w_table(dev, TABLE_STATIC_MAC, addr, data);  }  static void ksz8_from_vlan(struct ksz_device *dev, u32 vlan, u8 *fid, @@ -958,15 +981,14 @@ int ksz8_fdb_dump(struct ksz_device *dev, int port,  	u16 entries = 0;  	u8 timestamp = 0;  	u8 fid; -	u8 member; -	struct alu_struct alu; +	u8 src_port; +	u8 mac[ETH_ALEN];  	do { -		alu.is_static = false; -		ret = ksz8_r_dyn_mac_table(dev, i, alu.mac, &fid, &member, +		ret = ksz8_r_dyn_mac_table(dev, i, mac, &fid, &src_port,  					   ×tamp, &entries); -		if (!ret && (member & BIT(port))) { -			ret = cb(alu.mac, alu.fid, alu.is_static, data); +		if (!ret && port == src_port) { +			ret = cb(mac, fid, false, data);  			if (ret)  				break;  		} @@ -978,24 +1000,29 @@ int ksz8_fdb_dump(struct ksz_device *dev, int port,  	return ret;  } -int ksz8_mdb_add(struct ksz_device *dev, int port, -		 const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) +static int ksz8_add_sta_mac(struct ksz_device *dev, int port, +			    const unsigned char *addr, u16 vid)  {  	struct alu_struct alu; -	int index; +	int index, ret;  	int empty = 0;  	alu.port_forward = 0;  	for (index = 0; index < dev->info->num_statics; index++) { -		if (!ksz8_r_sta_mac_table(dev, index, &alu)) { -			/* Found one already in static MAC table. */ -			if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && -			    alu.fid == mdb->vid) -				break; -		/* Remember the first empty entry. */ -		} else if (!empty) { -			empty = index + 1; +		bool valid; + +		ret = ksz8_r_sta_mac_table(dev, index, &alu, &valid); +		if (ret) +			return ret; +		if (!valid) { +			/* Remember the first empty entry. */ +			if (!empty) +				empty = index + 1; +			continue;  		} + +		if (!memcmp(alu.mac, addr, ETH_ALEN) && alu.fid == vid) +			break;  	}  	/* no available entry */ @@ -1006,48 +1033,73 @@ int ksz8_mdb_add(struct ksz_device *dev, int port,  	if (index == dev->info->num_statics) {  		index = empty - 1;  		memset(&alu, 0, sizeof(alu)); -		memcpy(alu.mac, mdb->addr, ETH_ALEN); +		memcpy(alu.mac, addr, ETH_ALEN);  		alu.is_static = true;  	}  	alu.port_forward |= BIT(port); -	if (mdb->vid) { +	if (vid) {  		alu.is_use_fid = true;  		/* Need a way to map VID to FID. */ -		alu.fid = mdb->vid; +		alu.fid = vid;  	} -	ksz8_w_sta_mac_table(dev, index, &alu); -	return 0; +	return ksz8_w_sta_mac_table(dev, index, &alu);  } -int ksz8_mdb_del(struct ksz_device *dev, int port, -		 const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) +static int ksz8_del_sta_mac(struct ksz_device *dev, int port, +			    const unsigned char *addr, u16 vid)  {  	struct alu_struct alu; -	int index; +	int index, ret;  	for (index = 0; index < dev->info->num_statics; index++) { -		if (!ksz8_r_sta_mac_table(dev, index, &alu)) { -			/* Found one already in static MAC table. */ -			if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && -			    alu.fid == mdb->vid) -				break; -		} +		bool valid; + +		ret = ksz8_r_sta_mac_table(dev, index, &alu, &valid); +		if (ret) +			return ret; +		if (!valid) +			continue; + +		if (!memcmp(alu.mac, addr, ETH_ALEN) && alu.fid == vid) +			break;  	}  	/* no available entry */  	if (index == dev->info->num_statics) -		goto exit; +		return 0;  	/* clear port */  	alu.port_forward &= ~BIT(port);  	if (!alu.port_forward)  		alu.is_static = false; -	ksz8_w_sta_mac_table(dev, index, &alu); -exit: -	return 0; +	return ksz8_w_sta_mac_table(dev, index, &alu); +} + +int ksz8_mdb_add(struct ksz_device *dev, int port, +		 const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) +{ +	return ksz8_add_sta_mac(dev, port, mdb->addr, mdb->vid); +} + +int ksz8_mdb_del(struct ksz_device *dev, int port, +		 const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) +{ +	return ksz8_del_sta_mac(dev, port, mdb->addr, mdb->vid); +} + +int ksz8_fdb_add(struct ksz_device *dev, int port, const unsigned char *addr, +		 u16 vid, struct dsa_db db) +{ +	return ksz8_add_sta_mac(dev, port, addr, vid); +} + +int ksz8_fdb_del(struct ksz_device *dev, int port, const unsigned char *addr, +		 u16 vid, struct dsa_db db) +{ +	return ksz8_del_sta_mac(dev, port, addr, vid);  }  int ksz8_port_vlan_filtering(struct ksz_device *dev, int port, bool flag, @@ -1347,9 +1399,7 @@ int ksz8_enable_stp_addr(struct ksz_device *dev)  	alu.is_override = true;  	alu.port_forward = dev->info->cpu_ports; -	ksz8_w_sta_mac_table(dev, 0, &alu); - -	return 0; +	return ksz8_w_sta_mac_table(dev, 0, &alu);  }  int ksz8_setup(struct dsa_switch *ds) diff --git a/drivers/net/dsa/microchip/ksz8863_smi.c b/drivers/net/dsa/microchip/ksz8863_smi.c index 2f4623f3bd85..3698112138b7 100644 --- a/drivers/net/dsa/microchip/ksz8863_smi.c +++ b/drivers/net/dsa/microchip/ksz8863_smi.c @@ -82,22 +82,16 @@ static const struct regmap_bus regmap_smi[] = {  	{  		.read = ksz8863_mdio_read,  		.write = ksz8863_mdio_write, -		.max_raw_read = 1, -		.max_raw_write = 1,  	},  	{  		.read = ksz8863_mdio_read,  		.write = ksz8863_mdio_write,  		.val_format_endian_default = REGMAP_ENDIAN_BIG, -		.max_raw_read = 2, -		.max_raw_write = 2,  	},  	{  		.read = ksz8863_mdio_read,  		.write = ksz8863_mdio_write,  		.val_format_endian_default = REGMAP_ENDIAN_BIG, -		.max_raw_read = 4, -		.max_raw_write = 4,  	}  }; @@ -108,7 +102,6 @@ static const struct regmap_config ksz8863_regmap_config[] = {  		.pad_bits = 24,  		.val_bits = 8,  		.cache_type = REGCACHE_NONE, -		.use_single_read = 1,  		.lock = ksz_regmap_lock,  		.unlock = ksz_regmap_unlock,  	}, @@ -118,7 +111,6 @@ static const struct regmap_config ksz8863_regmap_config[] = {  		.pad_bits = 24,  		.val_bits = 16,  		.cache_type = REGCACHE_NONE, -		.use_single_read = 1,  		.lock = ksz_regmap_lock,  		.unlock = ksz_regmap_unlock,  	}, @@ -128,7 +120,6 @@ static const struct regmap_config ksz8863_regmap_config[] = {  		.pad_bits = 24,  		.val_bits = 32,  		.cache_type = REGCACHE_NONE, -		.use_single_read = 1,  		.lock = ksz_regmap_lock,  		.unlock = ksz_regmap_unlock,  	} diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index e315f669ec06..97a317263a2f 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -117,7 +117,7 @@ MODULE_DEVICE_TABLE(of, ksz9477_dt_ids);  static struct i2c_driver ksz9477_i2c_driver = {  	.driver = {  		.name	= "ksz9477-switch", -		.of_match_table = of_match_ptr(ksz9477_dt_ids), +		.of_match_table = ksz9477_dt_ids,  	},  	.probe_new = ksz9477_i2c_probe,  	.remove	= ksz9477_i2c_remove, diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 729b36eeb2c4..a4428be5f483 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -32,10 +32,6 @@  #include "ksz9477.h"  #include "lan937x.h" -#define KSZ_CBS_ENABLE ((MTI_SCHEDULE_STRICT_PRIO << MTI_SCHEDULE_MODE_S) | \ -			(MTI_SHAPING_SRP << MTI_SHAPING_S)) -#define KSZ_CBS_DISABLE ((MTI_SCHEDULE_WRR << MTI_SCHEDULE_MODE_S) |\ -			 (MTI_SHAPING_OFF << MTI_SHAPING_S))  #define MIB_COUNTER_NUM 0x20  struct ksz_stats_raw { @@ -204,6 +200,8 @@ static const struct ksz_dev_ops ksz8_dev_ops = {  	.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, @@ -319,7 +317,7 @@ static const u16 ksz8795_regs[] = {  	[S_BROADCAST_CTRL]		= 0x06,  	[S_MULTICAST_CTRL]		= 0x04,  	[P_XMII_CTRL_0]			= 0x06, -	[P_XMII_CTRL_1]			= 0x56, +	[P_XMII_CTRL_1]			= 0x06,  };  static const u32 ksz8795_masks[] = { @@ -404,13 +402,13 @@ static const u32 ksz8863_masks[] = {  	[VLAN_TABLE_VALID]		= BIT(19),  	[STATIC_MAC_TABLE_VALID]	= BIT(19),  	[STATIC_MAC_TABLE_USE_FID]	= BIT(21), -	[STATIC_MAC_TABLE_FID]		= GENMASK(29, 26), +	[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(5, 0), -	[DYNAMIC_MAC_TABLE_MAC_EMPTY]	= BIT(7), +	[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, 28), +	[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), @@ -420,10 +418,10 @@ static u8 ksz8863_shifts[] = {  	[VLAN_TABLE_MEMBERSHIP_S]	= 16,  	[STATIC_MAC_FWD_PORTS]		= 16,  	[STATIC_MAC_FID]		= 22, -	[DYNAMIC_MAC_ENTRIES_H]		= 3, +	[DYNAMIC_MAC_ENTRIES_H]		= 8,  	[DYNAMIC_MAC_ENTRIES]		= 24,  	[DYNAMIC_MAC_FID]		= 16, -	[DYNAMIC_MAC_TIMESTAMP]		= 24, +	[DYNAMIC_MAC_TIMESTAMP]		= 22,  	[DYNAMIC_MAC_SRC_PORT]		= 20,  }; @@ -1089,6 +1087,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_nirqs = 3,  		.num_tx_queues = 4,  		.tc_cbs_supported = true, +		.tc_ets_supported = true,  		.ops = &ksz9477_dev_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1228,6 +1227,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_nirqs = 4,  		.num_tx_queues = 4,  		.tc_cbs_supported = true, +		.tc_ets_supported = true,  		.ops = &ksz9477_dev_ops,  		.phy_errata_9477 = true,  		.mib_names = ksz9477_mib_names, @@ -1352,6 +1352,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_nirqs = 3,  		.num_tx_queues = 4,  		.tc_cbs_supported = true, +		.tc_ets_supported = true,  		.ops = &ksz9477_dev_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1379,6 +1380,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_nirqs = 3,  		.num_tx_queues = 4,  		.tc_cbs_supported = true, +		.tc_ets_supported = true,  		.ops = &ksz9477_dev_ops,  		.phy_errata_9477 = true,  		.mib_names = ksz9477_mib_names, @@ -1411,6 +1413,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_nirqs = 6,  		.num_tx_queues = 8,  		.tc_cbs_supported = true, +		.tc_ets_supported = true,  		.ops = &lan937x_dev_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1437,6 +1440,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_nirqs = 6,  		.num_tx_queues = 8,  		.tc_cbs_supported = true, +		.tc_ets_supported = true,  		.ops = &lan937x_dev_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1463,6 +1467,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_nirqs = 6,  		.num_tx_queues = 8,  		.tc_cbs_supported = true, +		.tc_ets_supported = true,  		.ops = &lan937x_dev_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1493,6 +1498,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_nirqs = 6,  		.num_tx_queues = 8,  		.tc_cbs_supported = true, +		.tc_ets_supported = true,  		.ops = &lan937x_dev_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1523,6 +1529,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {  		.port_nirqs = 6,  		.num_tx_queues = 8,  		.tc_cbs_supported = true, +		.tc_ets_supported = true,  		.ops = &lan937x_dev_ops,  		.mib_names = ksz9477_mib_names,  		.mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -3091,6 +3098,14 @@ static int cinc_cal(s32 idle_slope, s32 send_slope, u32 *bw)  	return 0;  } +static int ksz_setup_tc_mode(struct ksz_device *dev, int port, u8 scheduler, +			     u8 shaper) +{ +	return ksz_pwrite8(dev, port, REG_PORT_MTI_QUEUE_CTRL_0, +			   FIELD_PREP(MTI_SCHEDULE_MODE_M, scheduler) | +			   FIELD_PREP(MTI_SHAPING_M, shaper)); +} +  static int ksz_setup_tc_cbs(struct dsa_switch *ds, int port,  			    struct tc_cbs_qopt_offload *qopt)  { @@ -3110,8 +3125,8 @@ static int ksz_setup_tc_cbs(struct dsa_switch *ds, int port,  		return ret;  	if (!qopt->enable) -		return ksz_pwrite8(dev, port, REG_PORT_MTI_QUEUE_CTRL_0, -				   KSZ_CBS_DISABLE); +		return ksz_setup_tc_mode(dev, port, MTI_SCHEDULE_WRR, +					 MTI_SHAPING_OFF);  	/* High Credit */  	ret = ksz_pwrite16(dev, port, REG_PORT_MTI_HI_WATER_MARK, @@ -3136,8 +3151,215 @@ static int ksz_setup_tc_cbs(struct dsa_switch *ds, int port,  			return ret;  	} -	return ksz_pwrite8(dev, port, REG_PORT_MTI_QUEUE_CTRL_0, -			   KSZ_CBS_ENABLE); +	return ksz_setup_tc_mode(dev, port, MTI_SCHEDULE_STRICT_PRIO, +				 MTI_SHAPING_SRP); +} + +static int ksz_disable_egress_rate_limit(struct ksz_device *dev, int port) +{ +	int queue, ret; + +	/* Configuration will not take effect until the last Port Queue X +	 * Egress Limit Control Register is written. +	 */ +	for (queue = 0; queue < dev->info->num_tx_queues; queue++) { +		ret = ksz_pwrite8(dev, port, KSZ9477_REG_PORT_OUT_RATE_0 + queue, +				  KSZ9477_OUT_RATE_NO_LIMIT); +		if (ret) +			return ret; +	} + +	return 0; +} + +static int ksz_ets_band_to_queue(struct tc_ets_qopt_offload_replace_params *p, +				 int band) +{ +	/* Compared to queues, bands prioritize packets differently. In strict +	 * priority mode, the lowest priority is assigned to Queue 0 while the +	 * highest priority is given to Band 0. +	 */ +	return p->bands - 1 - band; +} + +static int ksz_queue_set_strict(struct ksz_device *dev, int port, int queue) +{ +	int ret; + +	ret = ksz_pwrite32(dev, port, REG_PORT_MTI_QUEUE_INDEX__4, queue); +	if (ret) +		return ret; + +	return ksz_setup_tc_mode(dev, port, MTI_SCHEDULE_STRICT_PRIO, +				 MTI_SHAPING_OFF); +} + +static int ksz_queue_set_wrr(struct ksz_device *dev, int port, int queue, +			     int weight) +{ +	int ret; + +	ret = ksz_pwrite32(dev, port, REG_PORT_MTI_QUEUE_INDEX__4, queue); +	if (ret) +		return ret; + +	ret = ksz_setup_tc_mode(dev, port, MTI_SCHEDULE_WRR, +				MTI_SHAPING_OFF); +	if (ret) +		return ret; + +	return ksz_pwrite8(dev, port, KSZ9477_PORT_MTI_QUEUE_CTRL_1, weight); +} + +static int ksz_tc_ets_add(struct ksz_device *dev, int port, +			  struct tc_ets_qopt_offload_replace_params *p) +{ +	int ret, band, tc_prio; +	u32 queue_map = 0; + +	/* In order to ensure proper prioritization, it is necessary to set the +	 * rate limit for the related queue to zero. Otherwise strict priority +	 * or WRR mode will not work. This is a hardware limitation. +	 */ +	ret = ksz_disable_egress_rate_limit(dev, port); +	if (ret) +		return ret; + +	/* Configure queue scheduling mode for all bands. Currently only strict +	 * prio mode is supported. +	 */ +	for (band = 0; band < p->bands; band++) { +		int queue = ksz_ets_band_to_queue(p, band); + +		ret = ksz_queue_set_strict(dev, port, queue); +		if (ret) +			return ret; +	} + +	/* Configure the mapping between traffic classes and queues. Note: +	 * priomap variable support 16 traffic classes, but the chip can handle +	 * only 8 classes. +	 */ +	for (tc_prio = 0; tc_prio < ARRAY_SIZE(p->priomap); tc_prio++) { +		int queue; + +		if (tc_prio > KSZ9477_MAX_TC_PRIO) +			break; + +		queue = ksz_ets_band_to_queue(p, p->priomap[tc_prio]); +		queue_map |= queue << (tc_prio * KSZ9477_PORT_TC_MAP_S); +	} + +	return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map); +} + +static int ksz_tc_ets_del(struct ksz_device *dev, int port) +{ +	int ret, queue, tc_prio, s; +	u32 queue_map = 0; + +	/* To restore the default chip configuration, set all queues to use the +	 * WRR scheduler with a weight of 1. +	 */ +	for (queue = 0; queue < dev->info->num_tx_queues; queue++) { +		ret = ksz_queue_set_wrr(dev, port, queue, +					KSZ9477_DEFAULT_WRR_WEIGHT); +		if (ret) +			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); +} + +static int ksz_tc_ets_validate(struct ksz_device *dev, int port, +			       struct tc_ets_qopt_offload_replace_params *p) +{ +	int band; + +	/* Since it is not feasible to share one port among multiple qdisc, +	 * the user must configure all available queues appropriately. +	 */ +	if (p->bands != dev->info->num_tx_queues) { +		dev_err(dev->dev, "Not supported amount of bands. It should be %d\n", +			dev->info->num_tx_queues); +		return -EOPNOTSUPP; +	} + +	for (band = 0; band < p->bands; ++band) { +		/* The KSZ switches utilize a weighted round robin configuration +		 * where a certain number of packets can be transmitted from a +		 * queue before the next queue is serviced. For more information +		 * on this, refer to section 5.2.8.4 of the KSZ8565R +		 * documentation on the Port Transmit Queue Control 1 Register. +		 * However, the current ETS Qdisc implementation (as of February +		 * 2023) assigns a weight to each queue based on the number of +		 * bytes or extrapolated bandwidth in percentages. Since this +		 * differs from the KSZ switches' method and we don't want to +		 * fake support by converting bytes to packets, it is better to +		 * return an error instead. +		 */ +		if (p->quanta[band]) { +			dev_err(dev->dev, "Quanta/weights configuration is not supported.\n"); +			return -EOPNOTSUPP; +		} +	} + +	return 0; +} + +static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port, +				  struct tc_ets_qopt_offload *qopt) +{ +	struct ksz_device *dev = ds->priv; +	int ret; + +	if (!dev->info->tc_ets_supported) +		return -EOPNOTSUPP; + +	if (qopt->parent != TC_H_ROOT) { +		dev_err(dev->dev, "Parent should be \"root\"\n"); +		return -EOPNOTSUPP; +	} + +	switch (qopt->command) { +	case TC_ETS_REPLACE: +		ret = ksz_tc_ets_validate(dev, port, &qopt->replace_params); +		if (ret) +			return ret; + +		return ksz_tc_ets_add(dev, port, &qopt->replace_params); +	case TC_ETS_DESTROY: +		return ksz_tc_ets_del(dev, port); +	case TC_ETS_STATS: +	case TC_ETS_GRAFT: +		return -EOPNOTSUPP; +	} + +	return -EOPNOTSUPP;  }  static int ksz_setup_tc(struct dsa_switch *ds, int port, @@ -3146,6 +3368,8 @@ static int ksz_setup_tc(struct dsa_switch *ds, int port,  	switch (type) {  	case TC_SETUP_QDISC_CBS:  		return ksz_setup_tc_cbs(ds, port, type_data); +	case TC_SETUP_QDISC_ETS: +		return ksz_tc_setup_qdisc_ets(ds, port, type_data);  	default:  		return -EOPNOTSUPP;  	} diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index d2d5761d58e9..8abecaf6089e 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -51,6 +51,7 @@ struct ksz_chip_data {  	u8 port_nirqs;  	u8 num_tx_queues;  	bool tc_cbs_supported; +	bool tc_ets_supported;  	const struct ksz_dev_ops *ops;  	bool phy_errata_9477;  	bool ksz87xx_eee_link_erratum; @@ -649,21 +650,30 @@ static inline int is_lan937x(struct ksz_device *dev)  #define KSZ8_LEGAL_PACKET_SIZE		1518  #define KSZ9477_MAX_FRAME_SIZE		9000 +#define KSZ9477_REG_PORT_OUT_RATE_0	0x0420 +#define KSZ9477_OUT_RATE_NO_LIMIT	0 + +#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  #define REG_PORT_MTI_QUEUE_CTRL_0	0x0914 -#define MTI_SCHEDULE_MODE_M		0x3 -#define MTI_SCHEDULE_MODE_S		6 +#define MTI_SCHEDULE_MODE_M		GENMASK(7, 6)  #define MTI_SCHEDULE_STRICT_PRIO	0  #define MTI_SCHEDULE_WRR		2 -#define MTI_SHAPING_M			0x3 -#define MTI_SHAPING_S			4 +#define MTI_SHAPING_M			GENMASK(5, 4)  #define MTI_SHAPING_OFF			0  #define MTI_SHAPING_SRP			1  #define MTI_SHAPING_TIME_AWARE		2 +#define KSZ9477_PORT_MTI_QUEUE_CTRL_1	0x0915 +#define KSZ9477_DEFAULT_WRR_WEIGHT	1 +  #define REG_PORT_MTI_HI_WATER_MARK	0x0916  #define REG_PORT_MTI_LO_WATER_MARK	0x0918 diff --git a/drivers/net/dsa/mt7530-mdio.c b/drivers/net/dsa/mt7530-mdio.c new file mode 100644 index 000000000000..088533663b83 --- /dev/null +++ b/drivers/net/dsa/mt7530-mdio.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/gpio/consumer.h> +#include <linux/mdio.h> +#include <linux/module.h> +#include <linux/pcs/pcs-mtk-lynxi.h> +#include <linux/of_irq.h> +#include <linux/of_mdio.h> +#include <linux/of_net.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/regulator/consumer.h> +#include <net/dsa.h> + +#include "mt7530.h" + +static int +mt7530_regmap_write(void *context, unsigned int reg, unsigned int val) +{ +	struct mii_bus *bus = context; +	u16 page, r, lo, hi; +	int ret; + +	page = (reg >> 6) & 0x3ff; +	r  = (reg >> 2) & 0xf; +	lo = val & 0xffff; +	hi = val >> 16; + +	/* MT7530 uses 31 as the pseudo port */ +	ret = bus->write(bus, 0x1f, 0x1f, page); +	if (ret < 0) +		return ret; + +	ret = bus->write(bus, 0x1f, r,  lo); +	if (ret < 0) +		return ret; + +	ret = bus->write(bus, 0x1f, 0x10, hi); +	return ret; +} + +static int +mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) +{ +	struct mii_bus *bus = context; +	u16 page, r, lo, hi; +	int ret; + +	page = (reg >> 6) & 0x3ff; +	r = (reg >> 2) & 0xf; + +	/* MT7530 uses 31 as the pseudo port */ +	ret = bus->write(bus, 0x1f, 0x1f, page); +	if (ret < 0) +		return ret; + +	lo = bus->read(bus, 0x1f, r); +	hi = bus->read(bus, 0x1f, 0x10); + +	*val = (hi << 16) | (lo & 0xffff); + +	return 0; +} + +static void +mt7530_mdio_regmap_lock(void *mdio_lock) +{ +	mutex_lock_nested(mdio_lock, MDIO_MUTEX_NESTED); +} + +static void +mt7530_mdio_regmap_unlock(void *mdio_lock) +{ +	mutex_unlock(mdio_lock); +} + +static const struct regmap_bus mt7530_regmap_bus = { +	.reg_write = mt7530_regmap_write, +	.reg_read = mt7530_regmap_read, +}; + +static int +mt7531_create_sgmii(struct mt7530_priv *priv, bool dual_sgmii) +{ +	struct regmap_config *mt7531_pcs_config[2] = {}; +	struct phylink_pcs *pcs; +	struct regmap *regmap; +	int i, ret = 0; + +	/* MT7531AE has two SGMII units for port 5 and port 6 +	 * MT7531BE has only one SGMII unit for port 6 +	 */ +	for (i = dual_sgmii ? 0 : 1; i < 2; i++) { +		mt7531_pcs_config[i] = devm_kzalloc(priv->dev, +						    sizeof(struct regmap_config), +						    GFP_KERNEL); +		if (!mt7531_pcs_config[i]) { +			ret = -ENOMEM; +			break; +		} + +		mt7531_pcs_config[i]->name = i ? "port6" : "port5"; +		mt7531_pcs_config[i]->reg_bits = 16; +		mt7531_pcs_config[i]->val_bits = 32; +		mt7531_pcs_config[i]->reg_stride = 4; +		mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i); +		mt7531_pcs_config[i]->max_register = 0x17c; +		mt7531_pcs_config[i]->lock = mt7530_mdio_regmap_lock; +		mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock; +		mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock; + +		regmap = devm_regmap_init(priv->dev, +					  &mt7530_regmap_bus, priv->bus, +					  mt7531_pcs_config[i]); +		if (IS_ERR(regmap)) { +			ret = PTR_ERR(regmap); +			break; +		} +		pcs = mtk_pcs_lynxi_create(priv->dev, regmap, +					   MT7531_PHYA_CTRL_SIGNAL3, 0); +		if (!pcs) { +			ret = -ENXIO; +			break; +		} +		priv->ports[5 + i].sgmii_pcs = pcs; +	} + +	if (ret && i) +		mtk_pcs_lynxi_destroy(priv->ports[5].sgmii_pcs); + +	return ret; +} + +static const struct of_device_id mt7530_of_match[] = { +	{ .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], }, +	{ .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], }, +	{ .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], }, +	{ /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, mt7530_of_match); + +static int +mt7530_probe(struct mdio_device *mdiodev) +{ +	static struct regmap_config *regmap_config; +	struct mt7530_priv *priv; +	struct device_node *dn; +	int ret; + +	dn = mdiodev->dev.of_node; + +	priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); +	if (!priv) +		return -ENOMEM; + +	priv->bus = mdiodev->bus; +	priv->dev = &mdiodev->dev; + +	ret = mt7530_probe_common(priv); +	if (ret) +		return ret; + +	/* Use medatek,mcm property to distinguish hardware type that would +	 * cause a little bit differences on power-on sequence. +	 * Not MCM that indicates switch works as the remote standalone +	 * integrated circuit so the GPIO pin would be used to complete +	 * the reset, otherwise memory-mapped register accessing used +	 * through syscon provides in the case of MCM. +	 */ +	priv->mcm = of_property_read_bool(dn, "mediatek,mcm"); +	if (priv->mcm) { +		dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n"); + +		priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm"); +		if (IS_ERR(priv->rstc)) { +			dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); +			return PTR_ERR(priv->rstc); +		} +	} else { +		priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset", +						      GPIOD_OUT_LOW); +		if (IS_ERR(priv->reset)) { +			dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); +			return PTR_ERR(priv->reset); +		} +	} + +	if (priv->id == ID_MT7530) { +		priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core"); +		if (IS_ERR(priv->core_pwr)) +			return PTR_ERR(priv->core_pwr); + +		priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io"); +		if (IS_ERR(priv->io_pwr)) +			return PTR_ERR(priv->io_pwr); +	} + +	regmap_config = devm_kzalloc(&mdiodev->dev, sizeof(*regmap_config), +				     GFP_KERNEL); +	if (!regmap_config) +		return -ENOMEM; + +	regmap_config->reg_bits = 16; +	regmap_config->val_bits = 32; +	regmap_config->reg_stride = 4; +	regmap_config->max_register = MT7530_CREV; +	regmap_config->disable_locking = true; +	priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, +					priv->bus, regmap_config); +	if (IS_ERR(priv->regmap)) +		return PTR_ERR(priv->regmap); + +	if (priv->id == ID_MT7531) +		priv->create_sgmii = mt7531_create_sgmii; + +	return dsa_register_switch(priv->ds); +} + +static void +mt7530_remove(struct mdio_device *mdiodev) +{ +	struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); +	int ret = 0, i; + +	if (!priv) +		return; + +	ret = regulator_disable(priv->core_pwr); +	if (ret < 0) +		dev_err(priv->dev, +			"Failed to disable core power: %d\n", ret); + +	ret = regulator_disable(priv->io_pwr); +	if (ret < 0) +		dev_err(priv->dev, "Failed to disable io pwr: %d\n", +			ret); + +	mt7530_remove_common(priv); + +	for (i = 0; i < 2; ++i) +		mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs); +} + +static void mt7530_shutdown(struct mdio_device *mdiodev) +{ +	struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); + +	if (!priv) +		return; + +	dsa_switch_shutdown(priv->ds); + +	dev_set_drvdata(&mdiodev->dev, NULL); +} + +static struct mdio_driver mt7530_mdio_driver = { +	.probe  = mt7530_probe, +	.remove = mt7530_remove, +	.shutdown = mt7530_shutdown, +	.mdiodrv.driver = { +		.name = "mt7530-mdio", +		.of_match_table = mt7530_of_match, +	}, +}; + +mdio_module_driver(mt7530_mdio_driver); + +MODULE_AUTHOR("Sean Wang <[email protected]>"); +MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MDIO)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/mt7530-mmio.c b/drivers/net/dsa/mt7530-mmio.c new file mode 100644 index 000000000000..1a3d4b692f34 --- /dev/null +++ b/drivers/net/dsa/mt7530-mmio.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <net/dsa.h> + +#include "mt7530.h" + +static const struct of_device_id mt7988_of_match[] = { +	{ .compatible = "mediatek,mt7988-switch", .data = &mt753x_table[ID_MT7988], }, +	{ /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, mt7988_of_match); + +static int +mt7988_probe(struct platform_device *pdev) +{ +	static struct regmap_config *sw_regmap_config; +	struct mt7530_priv *priv; +	void __iomem *base_addr; +	int ret; + +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); +	if (!priv) +		return -ENOMEM; + +	priv->bus = NULL; +	priv->dev = &pdev->dev; + +	ret = mt7530_probe_common(priv); +	if (ret) +		return ret; + +	priv->rstc = devm_reset_control_get(&pdev->dev, NULL); +	if (IS_ERR(priv->rstc)) { +		dev_err(&pdev->dev, "Couldn't get our reset line\n"); +		return PTR_ERR(priv->rstc); +	} + +	base_addr = devm_platform_ioremap_resource(pdev, 0); +	if (IS_ERR(base_addr)) { +		dev_err(&pdev->dev, "cannot request I/O memory space\n"); +		return -ENXIO; +	} + +	sw_regmap_config = devm_kzalloc(&pdev->dev, sizeof(*sw_regmap_config), GFP_KERNEL); +	if (!sw_regmap_config) +		return -ENOMEM; + +	sw_regmap_config->name = "switch"; +	sw_regmap_config->reg_bits = 16; +	sw_regmap_config->val_bits = 32; +	sw_regmap_config->reg_stride = 4; +	sw_regmap_config->max_register = MT7530_CREV; +	priv->regmap = devm_regmap_init_mmio(&pdev->dev, base_addr, sw_regmap_config); +	if (IS_ERR(priv->regmap)) +		return PTR_ERR(priv->regmap); + +	return dsa_register_switch(priv->ds); +} + +static int +mt7988_remove(struct platform_device *pdev) +{ +	struct mt7530_priv *priv = platform_get_drvdata(pdev); + +	if (priv) +		mt7530_remove_common(priv); + +	return 0; +} + +static void mt7988_shutdown(struct platform_device *pdev) +{ +	struct mt7530_priv *priv = platform_get_drvdata(pdev); + +	if (!priv) +		return; + +	dsa_switch_shutdown(priv->ds); + +	dev_set_drvdata(&pdev->dev, NULL); +} + +static struct platform_driver mt7988_platform_driver = { +	.probe  = mt7988_probe, +	.remove = mt7988_remove, +	.shutdown = mt7988_shutdown, +	.driver = { +		.name = "mt7530-mmio", +		.of_match_table = mt7988_of_match, +	}, +}; +module_platform_driver(mt7988_platform_driver); + +MODULE_AUTHOR("Daniel Golle <[email protected]>"); +MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MMIO)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index a508402c4ecb..7e773c4ba046 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -142,31 +142,42 @@ err:  }  static void -core_write(struct mt7530_priv *priv, u32 reg, u32 val) +mt7530_mutex_lock(struct mt7530_priv *priv)  { -	struct mii_bus *bus = priv->bus; +	if (priv->bus) +		mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); +} -	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +static void +mt7530_mutex_unlock(struct mt7530_priv *priv) +{ +	if (priv->bus) +		mutex_unlock(&priv->bus->mdio_lock); +} + +static void +core_write(struct mt7530_priv *priv, u32 reg, u32 val) +{ +	mt7530_mutex_lock(priv);  	core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val); -	mutex_unlock(&bus->mdio_lock); +	mt7530_mutex_unlock(priv);  }  static void  core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set)  { -	struct mii_bus *bus = priv->bus;  	u32 val; -	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +	mt7530_mutex_lock(priv);  	val = core_read_mmd_indirect(priv, reg, MDIO_MMD_VEND2);  	val &= ~mask;  	val |= set;  	core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val); -	mutex_unlock(&bus->mdio_lock); +	mt7530_mutex_unlock(priv);  }  static void @@ -184,66 +195,42 @@ core_clear(struct mt7530_priv *priv, u32 reg, u32 val)  static int  mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val)  { -	struct mii_bus *bus = priv->bus; -	u16 page, r, lo, hi;  	int ret; -	page = (reg >> 6) & 0x3ff; -	r  = (reg >> 2) & 0xf; -	lo = val & 0xffff; -	hi = val >> 16; +	ret = regmap_write(priv->regmap, reg, val); -	/* MT7530 uses 31 as the pseudo port */ -	ret = bus->write(bus, 0x1f, 0x1f, page);  	if (ret < 0) -		goto err; - -	ret = bus->write(bus, 0x1f, r,  lo); -	if (ret < 0) -		goto err; - -	ret = bus->write(bus, 0x1f, 0x10, hi); -err: -	if (ret < 0) -		dev_err(&bus->dev, +		dev_err(priv->dev,  			"failed to write mt7530 register\n"); +  	return ret;  }  static u32  mt7530_mii_read(struct mt7530_priv *priv, u32 reg)  { -	struct mii_bus *bus = priv->bus; -	u16 page, r, lo, hi;  	int ret; +	u32 val; -	page = (reg >> 6) & 0x3ff; -	r = (reg >> 2) & 0xf; - -	/* MT7530 uses 31 as the pseudo port */ -	ret = bus->write(bus, 0x1f, 0x1f, page); -	if (ret < 0) { -		dev_err(&bus->dev, +	ret = regmap_read(priv->regmap, reg, &val); +	if (ret) { +		WARN_ON_ONCE(1); +		dev_err(priv->dev,  			"failed to read mt7530 register\n"); -		return ret; +		return 0;  	} -	lo = bus->read(bus, 0x1f, r); -	hi = bus->read(bus, 0x1f, 0x10); - -	return (hi << 16) | (lo & 0xffff); +	return val;  }  static void  mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val)  { -	struct mii_bus *bus = priv->bus; - -	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +	mt7530_mutex_lock(priv);  	mt7530_mii_write(priv, reg, val); -	mutex_unlock(&bus->mdio_lock); +	mt7530_mutex_unlock(priv);  }  static u32 @@ -255,14 +242,13 @@ _mt7530_unlocked_read(struct mt7530_dummy_poll *p)  static u32  _mt7530_read(struct mt7530_dummy_poll *p)  { -	struct mii_bus		*bus = p->priv->bus;  	u32 val; -	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +	mt7530_mutex_lock(p->priv);  	val = mt7530_mii_read(p->priv, p->reg); -	mutex_unlock(&bus->mdio_lock); +	mt7530_mutex_unlock(p->priv);  	return val;  } @@ -280,23 +266,17 @@ static void  mt7530_rmw(struct mt7530_priv *priv, u32 reg,  	   u32 mask, u32 set)  { -	struct mii_bus *bus = priv->bus; -	u32 val; +	mt7530_mutex_lock(priv); -	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +	regmap_update_bits(priv->regmap, reg, mask, set); -	val = mt7530_mii_read(priv, reg); -	val &= ~mask; -	val |= set; -	mt7530_mii_write(priv, reg, val); - -	mutex_unlock(&bus->mdio_lock); +	mt7530_mutex_unlock(priv);  }  static void  mt7530_set(struct mt7530_priv *priv, u32 reg, u32 val)  { -	mt7530_rmw(priv, reg, 0, val); +	mt7530_rmw(priv, reg, val, val);  }  static void @@ -396,6 +376,9 @@ mt7530_fdb_write(struct mt7530_priv *priv, u16 vid,  /* Set up switch core clock for MT7530 */  static void mt7530_pll_setup(struct mt7530_priv *priv)  { +	/* Disable core clock */ +	core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN); +  	/* Disable PLL */  	core_write(priv, CORE_GSWPLL_GRP1, 0); @@ -409,14 +392,33 @@ static void mt7530_pll_setup(struct mt7530_priv *priv)  		   RG_GSWPLL_EN_PRE |  		   RG_GSWPLL_POSDIV_200M(2) |  		   RG_GSWPLL_FBKDIV_200M(32)); + +	udelay(20); + +	/* Enable core clock */ +	core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN); +} + +/* If port 6 is available as a CPU port, always prefer that as the default, + * otherwise don't care. + */ +static struct dsa_port * +mt753x_preferred_default_local_cpu_port(struct dsa_switch *ds) +{ +	struct dsa_port *cpu_dp = dsa_to_port(ds, 6); + +	if (dsa_port_is_cpu(cpu_dp)) +		return cpu_dp; + +	return NULL;  } -/* Setup TX circuit including relevant PAD and driving */ +/* Setup port 6 interface mode and TRGMII TX circuit */  static int  mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)  {  	struct mt7530_priv *priv = ds->priv; -	u32 ncpo1, ssc_delta, trgint, i, xtal; +	u32 ncpo1, ssc_delta, trgint, xtal;  	xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK; @@ -430,15 +432,17 @@ mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)  	switch (interface) {  	case PHY_INTERFACE_MODE_RGMII:  		trgint = 0; -		/* PLL frequency: 125MHz */ -		ncpo1 = 0x0c80;  		break;  	case PHY_INTERFACE_MODE_TRGMII:  		trgint = 1; +		if (xtal == HWTRAP_XTAL_25MHZ) +			ssc_delta = 0x57; +		else +			ssc_delta = 0x87;  		if (priv->id == ID_MT7621) { -			/* PLL frequency: 150MHz: 1.2GBit */ +			/* PLL frequency: 125MHz: 1.0GBit */  			if (xtal == HWTRAP_XTAL_40MHZ) -				ncpo1 = 0x0780; +				ncpo1 = 0x0640;  			if (xtal == HWTRAP_XTAL_25MHZ)  				ncpo1 = 0x0a00;  		} else { /* PLL frequency: 250MHz: 2.0Gbit */ @@ -454,46 +458,32 @@ mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)  		return -EINVAL;  	} -	if (xtal == HWTRAP_XTAL_25MHZ) -		ssc_delta = 0x57; -	else -		ssc_delta = 0x87; -  	mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK,  		   P6_INTF_MODE(trgint)); -	/* Lower Tx Driving for TRGMII path */ -	for (i = 0 ; i < NUM_TRGMII_CTRL ; i++) -		mt7530_write(priv, MT7530_TRGMII_TD_ODT(i), -			     TD_DM_DRVP(8) | TD_DM_DRVN(8)); +	if (trgint) { +		/* Disable the MT7530 TRGMII clocks */ +		core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN); + +		/* Setup the MT7530 TRGMII Tx Clock */ +		core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1)); +		core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0)); +		core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta)); +		core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta)); +		core_write(priv, CORE_PLL_GROUP4, +			   RG_SYSPLL_DDSFBK_EN | RG_SYSPLL_BIAS_EN | +			   RG_SYSPLL_BIAS_LPF_EN); +		core_write(priv, CORE_PLL_GROUP2, +			   RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN | +			   RG_SYSPLL_POSDIV(1)); +		core_write(priv, CORE_PLL_GROUP7, +			   RG_LCDDS_PCW_NCPO_CHG | RG_LCCDS_C(3) | +			   RG_LCDDS_PWDB | RG_LCDDS_ISO_EN); + +		/* Enable the MT7530 TRGMII clocks */ +		core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN); +	} -	/* Disable MT7530 core and TRGMII Tx clocks */ -	core_clear(priv, CORE_TRGMII_GSW_CLK_CG, -		   REG_GSWCK_EN | REG_TRGMIICK_EN); - -	/* Setup the MT7530 TRGMII Tx Clock */ -	core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1)); -	core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0)); -	core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta)); -	core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta)); -	core_write(priv, CORE_PLL_GROUP4, -		   RG_SYSPLL_DDSFBK_EN | RG_SYSPLL_BIAS_EN | -		   RG_SYSPLL_BIAS_LPF_EN); -	core_write(priv, CORE_PLL_GROUP2, -		   RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN | -		   RG_SYSPLL_POSDIV(1)); -	core_write(priv, CORE_PLL_GROUP7, -		   RG_LCDDS_PCW_NCPO_CHG | RG_LCCDS_C(3) | -		   RG_LCDDS_PWDB | RG_LCDDS_ISO_EN); - -	/* Enable MT7530 core and TRGMII Tx clocks */ -	core_set(priv, CORE_TRGMII_GSW_CLK_CG, -		 REG_GSWCK_EN | REG_TRGMIICK_EN); - -	if (!trgint) -		for (i = 0 ; i < NUM_TRGMII_CTRL; i++) -			mt7530_rmw(priv, MT7530_TRGMII_RD(i), -				   RD_TAP_MASK, RD_TAP(16));  	return 0;  } @@ -638,14 +628,13 @@ static int  mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,  			int regnum)  { -	struct mii_bus *bus = priv->bus;  	struct mt7530_dummy_poll p;  	u32 reg, val;  	int ret;  	INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); -	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +	mt7530_mutex_lock(priv);  	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,  				 !(val & MT7531_PHY_ACS_ST), 20, 100000); @@ -678,7 +667,7 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,  	ret = val & MT7531_MDIO_RW_DATA_MASK;  out: -	mutex_unlock(&bus->mdio_lock); +	mt7530_mutex_unlock(priv);  	return ret;  } @@ -687,14 +676,13 @@ static int  mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,  			 int regnum, u16 data)  { -	struct mii_bus *bus = priv->bus;  	struct mt7530_dummy_poll p;  	u32 val, reg;  	int ret;  	INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); -	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +	mt7530_mutex_lock(priv);  	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,  				 !(val & MT7531_PHY_ACS_ST), 20, 100000); @@ -726,7 +714,7 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,  	}  out: -	mutex_unlock(&bus->mdio_lock); +	mt7530_mutex_unlock(priv);  	return ret;  } @@ -734,14 +722,13 @@ out:  static int  mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)  { -	struct mii_bus *bus = priv->bus;  	struct mt7530_dummy_poll p;  	int ret;  	u32 val;  	INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); -	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +	mt7530_mutex_lock(priv);  	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,  				 !(val & MT7531_PHY_ACS_ST), 20, 100000); @@ -764,7 +751,7 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)  	ret = val & MT7531_MDIO_RW_DATA_MASK;  out: -	mutex_unlock(&bus->mdio_lock); +	mt7530_mutex_unlock(priv);  	return ret;  } @@ -773,14 +760,13 @@ static int  mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,  			 u16 data)  { -	struct mii_bus *bus = priv->bus;  	struct mt7530_dummy_poll p;  	int ret;  	u32 reg;  	INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); -	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +	mt7530_mutex_lock(priv);  	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,  				 !(reg & MT7531_PHY_ACS_ST), 20, 100000); @@ -802,7 +788,7 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,  	}  out: -	mutex_unlock(&bus->mdio_lock); +	mt7530_mutex_unlock(priv);  	return ret;  } @@ -924,6 +910,24 @@ mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)  	return 0;  } +static const char *p5_intf_modes(unsigned int p5_interface) +{ +	switch (p5_interface) { +	case P5_DISABLED: +		return "DISABLED"; +	case P5_INTF_SEL_PHY_P0: +		return "PHY P0"; +	case P5_INTF_SEL_PHY_P4: +		return "PHY P4"; +	case P5_INTF_SEL_GMAC5: +		return "GMAC5"; +	case P5_INTF_SEL_GMAC5_SGMII: +		return "GMAC5_SGMII"; +	default: +		return "unknown"; +	} +} +  static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface)  {  	struct mt7530_priv *priv = ds->priv; @@ -995,6 +999,18 @@ unlock_exit:  	mutex_unlock(&priv->reg_mutex);  } +static void +mt753x_trap_frames(struct mt7530_priv *priv) +{ +	/* Trap BPDUs to the CPU port(s) */ +	mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK, +		   MT753X_BPDU_CPU_ONLY); + +	/* Trap LLDP frames with :0E MAC DA to the CPU port(s) */ +	mt7530_rmw(priv, MT753X_RGAC2, MT753X_R0E_PORT_FW_MASK, +		   MT753X_R0E_PORT_FW(MT753X_BPDU_CPU_ONLY)); +} +  static int  mt753x_cpu_port_enable(struct dsa_switch *ds, int port)  { @@ -1012,14 +1028,21 @@ mt753x_cpu_port_enable(struct dsa_switch *ds, int port)  	mt7530_write(priv, MT7530_PVC_P(port),  		     PORT_SPEC_TAG); -	/* Disable flooding by default */ -	mt7530_rmw(priv, MT7530_MFC, BC_FFP_MASK | UNM_FFP_MASK | UNU_FFP_MASK, -		   BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) | UNU_FFP(BIT(port))); +	/* Enable flooding on the CPU port */ +	mt7530_set(priv, MT7530_MFC, BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) | +		   UNU_FFP(BIT(port)));  	/* Set CPU port number */ -	if (priv->id == ID_MT7621) +	if (priv->id == ID_MT7530 || priv->id == ID_MT7621)  		mt7530_rmw(priv, MT7530_MFC, CPU_MASK, CPU_EN | CPU_PORT(port)); +	/* Add the CPU port to the CPU port bitmap for MT7531 and the switch on +	 * the MT7988 SoC. Trapped frames will be forwarded to the CPU port that +	 * is affine to the inbound user port. +	 */ +	if (priv->id == ID_MT7531 || priv->id == ID_MT7988) +		mt7530_set(priv, MT7531_CFC, MT7531_CPU_PMAP(BIT(port))); +  	/* CPU port gets connected to all user ports of  	 * the switch.  	 */ @@ -1083,7 +1106,6 @@ static int  mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)  {  	struct mt7530_priv *priv = ds->priv; -	struct mii_bus *bus = priv->bus;  	int length;  	u32 val; @@ -1094,7 +1116,7 @@ mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)  	if (!dsa_is_cpu_port(ds, port))  		return 0; -	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +	mt7530_mutex_lock(priv);  	val = mt7530_mii_read(priv, MT7530_GMACCR);  	val &= ~MAX_RX_PKT_LEN_MASK; @@ -1115,7 +1137,7 @@ mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)  	mt7530_mii_write(priv, MT7530_GMACCR, val); -	mutex_unlock(&bus->mdio_lock); +	mt7530_mutex_unlock(priv);  	return 0;  } @@ -1916,10 +1938,10 @@ mt7530_irq_thread_fn(int irq, void *dev_id)  	u32 val;  	int p; -	mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); +	mt7530_mutex_lock(priv);  	val = mt7530_mii_read(priv, MT7530_SYS_INT_STS);  	mt7530_mii_write(priv, MT7530_SYS_INT_STS, val); -	mutex_unlock(&priv->bus->mdio_lock); +	mt7530_mutex_unlock(priv);  	for (p = 0; p < MT7530_NUM_PHYS; p++) {  		if (BIT(p) & val) { @@ -1955,7 +1977,7 @@ mt7530_irq_bus_lock(struct irq_data *d)  {  	struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); -	mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); +	mt7530_mutex_lock(priv);  }  static void @@ -1964,7 +1986,7 @@ mt7530_irq_bus_sync_unlock(struct irq_data *d)  	struct mt7530_priv *priv = irq_data_get_irq_chip_data(d);  	mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable); -	mutex_unlock(&priv->bus->mdio_lock); +	mt7530_mutex_unlock(priv);  }  static struct irq_chip mt7530_irq_chip = { @@ -1993,6 +2015,47 @@ static const struct irq_domain_ops mt7530_irq_domain_ops = {  };  static void +mt7988_irq_mask(struct irq_data *d) +{ +	struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); + +	priv->irq_enable &= ~BIT(d->hwirq); +	mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable); +} + +static void +mt7988_irq_unmask(struct irq_data *d) +{ +	struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); + +	priv->irq_enable |= BIT(d->hwirq); +	mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable); +} + +static struct irq_chip mt7988_irq_chip = { +	.name = KBUILD_MODNAME, +	.irq_mask = mt7988_irq_mask, +	.irq_unmask = mt7988_irq_unmask, +}; + +static int +mt7988_irq_map(struct irq_domain *domain, unsigned int irq, +	       irq_hw_number_t hwirq) +{ +	irq_set_chip_data(irq, domain->host_data); +	irq_set_chip_and_handler(irq, &mt7988_irq_chip, handle_simple_irq); +	irq_set_nested_thread(irq, true); +	irq_set_noprobe(irq); + +	return 0; +} + +static const struct irq_domain_ops mt7988_irq_domain_ops = { +	.map = mt7988_irq_map, +	.xlate = irq_domain_xlate_onecell, +}; + +static void  mt7530_setup_mdio_irq(struct mt7530_priv *priv)  {  	struct dsa_switch *ds = priv->ds; @@ -2026,8 +2089,15 @@ mt7530_setup_irq(struct mt7530_priv *priv)  		return priv->irq ? : -EINVAL;  	} -	priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS, -						 &mt7530_irq_domain_ops, priv); +	if (priv->id == ID_MT7988) +		priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS, +							 &mt7988_irq_domain_ops, +							 priv); +	else +		priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS, +							 &mt7530_irq_domain_ops, +							 priv); +  	if (!priv->irq_domain) {  		dev_err(dev, "failed to create IRQ domain\n");  		return -ENOMEM; @@ -2201,7 +2271,16 @@ mt7530_setup(struct dsa_switch *ds)  	mt7530_pll_setup(priv); -	/* Enable Port 6 only; P5 as GMAC5 which currently is not supported */ +	/* Lower Tx driving for TRGMII path */ +	for (i = 0; i < NUM_TRGMII_CTRL; i++) +		mt7530_write(priv, MT7530_TRGMII_TD_ODT(i), +			     TD_DM_DRVP(8) | TD_DM_DRVN(8)); + +	for (i = 0; i < NUM_TRGMII_CTRL; i++) +		mt7530_rmw(priv, MT7530_TRGMII_RD(i), +			   RD_TAP_MASK, RD_TAP(16)); + +	/* Enable port 6 */  	val = mt7530_read(priv, MT7530_MHWTRAP);  	val &= ~MHWTRAP_P6_DIS & ~MHWTRAP_PHY_ACCESS;  	val |= MHWTRAP_MANUAL; @@ -2209,6 +2288,8 @@ mt7530_setup(struct dsa_switch *ds)  	priv->p6_interface = PHY_INTERFACE_MODE_NA; +	mt753x_trap_frames(priv); +  	/* Enable and reset MIB counters */  	mt7530_mib_reset(ds); @@ -2303,11 +2384,60 @@ mt7530_setup(struct dsa_switch *ds)  }  static int +mt7531_setup_common(struct dsa_switch *ds) +{ +	struct mt7530_priv *priv = ds->priv; +	int ret, i; + +	mt753x_trap_frames(priv); + +	/* Enable and reset MIB counters */ +	mt7530_mib_reset(ds); + +	/* Disable flooding on all ports */ +	mt7530_clear(priv, MT7530_MFC, BC_FFP_MASK | UNM_FFP_MASK | +		     UNU_FFP_MASK); + +	for (i = 0; i < MT7530_NUM_PORTS; i++) { +		/* Disable forwarding by default on all ports */ +		mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, +			   PCR_MATRIX_CLR); + +		/* Disable learning by default on all ports */ +		mt7530_set(priv, MT7530_PSC_P(i), SA_DIS); + +		mt7530_set(priv, MT7531_DBG_CNT(i), MT7531_DIS_CLR); + +		if (dsa_is_cpu_port(ds, i)) { +			ret = mt753x_cpu_port_enable(ds, i); +			if (ret) +				return ret; +		} else { +			mt7530_port_disable(ds, i); + +			/* Set default PVID to 0 on all user ports */ +			mt7530_rmw(priv, MT7530_PPBV1_P(i), G0_PORT_VID_MASK, +				   G0_PORT_VID_DEF); +		} + +		/* Enable consistent egress tag */ +		mt7530_rmw(priv, MT7530_PVC_P(i), PVC_EG_TAG_MASK, +			   PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); +	} + +	/* Flush the FDB table */ +	ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL); +	if (ret < 0) +		return ret; + +	return 0; +} + +static int  mt7531_setup(struct dsa_switch *ds)  {  	struct mt7530_priv *priv = ds->priv;  	struct mt7530_dummy_poll p; -	struct dsa_port *cpu_dp;  	u32 val, id;  	int ret, i; @@ -2385,44 +2515,7 @@ mt7531_setup(struct dsa_switch *ds)  	mt7531_ind_c45_phy_write(priv, MT753X_CTRL_PHY_ADDR, MDIO_MMD_VEND2,  				 CORE_PLL_GROUP4, val); -	/* BPDU to CPU port */ -	dsa_switch_for_each_cpu_port(cpu_dp, ds) { -		mt7530_rmw(priv, MT7531_CFC, MT7531_CPU_PMAP_MASK, -			   BIT(cpu_dp->index)); -		break; -	} -	mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK, -		   MT753X_BPDU_CPU_ONLY); - -	/* Enable and reset MIB counters */ -	mt7530_mib_reset(ds); - -	for (i = 0; i < MT7530_NUM_PORTS; i++) { -		/* Disable forwarding by default on all ports */ -		mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, -			   PCR_MATRIX_CLR); - -		/* Disable learning by default on all ports */ -		mt7530_set(priv, MT7530_PSC_P(i), SA_DIS); - -		mt7530_set(priv, MT7531_DBG_CNT(i), MT7531_DIS_CLR); - -		if (dsa_is_cpu_port(ds, i)) { -			ret = mt753x_cpu_port_enable(ds, i); -			if (ret) -				return ret; -		} else { -			mt7530_port_disable(ds, i); - -			/* Set default PVID to 0 on all user ports */ -			mt7530_rmw(priv, MT7530_PPBV1_P(i), G0_PORT_VID_MASK, -				   G0_PORT_VID_DEF); -		} - -		/* Enable consistent egress tag */ -		mt7530_rmw(priv, MT7530_PVC_P(i), PVC_EG_TAG_MASK, -			   PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); -	} +	mt7531_setup_common(ds);  	/* Setup VLAN ID 0 for VLAN-unaware bridges */  	ret = mt7530_setup_vlan0(priv); @@ -2432,11 +2525,6 @@ mt7531_setup(struct dsa_switch *ds)  	ds->assisted_learning_on_cpu_port = true;  	ds->mtu_enforcement_ingress = true; -	/* Flush the FDB table */ -	ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL); -	if (ret < 0) -		return ret; -  	return 0;  } @@ -2502,6 +2590,25 @@ static void mt7531_mac_port_get_caps(struct dsa_switch *ds, int port,  	}  } +static void mt7988_mac_port_get_caps(struct dsa_switch *ds, int port, +				     struct phylink_config *config) +{ +	phy_interface_zero(config->supported_interfaces); + +	switch (port) { +	case 0 ... 4: /* Internal phy */ +		__set_bit(PHY_INTERFACE_MODE_INTERNAL, +			  config->supported_interfaces); +		break; + +	case 6: +		__set_bit(PHY_INTERFACE_MODE_INTERNAL, +			  config->supported_interfaces); +		config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | +					   MAC_10000FD; +	} +} +  static int  mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state)  { @@ -2572,126 +2679,20 @@ static int mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port,  	return 0;  } -static void mt7531_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, -			       phy_interface_t interface, int speed, int duplex) -{ -	struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; -	int port = pcs_to_mt753x_pcs(pcs)->port; -	unsigned int val; - -	/* For adjusting speed and duplex of SGMII force mode. */ -	if (interface != PHY_INTERFACE_MODE_SGMII || -	    phylink_autoneg_inband(mode)) -		return; - -	/* SGMII force mode setting */ -	val = mt7530_read(priv, MT7531_SGMII_MODE(port)); -	val &= ~MT7531_SGMII_IF_MODE_MASK; - -	switch (speed) { -	case SPEED_10: -		val |= MT7531_SGMII_FORCE_SPEED_10; -		break; -	case SPEED_100: -		val |= MT7531_SGMII_FORCE_SPEED_100; -		break; -	case SPEED_1000: -		val |= MT7531_SGMII_FORCE_SPEED_1000; -		break; -	} - -	/* MT7531 SGMII 1G force mode can only work in full duplex mode, -	 * no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not. -	 * -	 * The speed check is unnecessary as the MAC capabilities apply -	 * this restriction. --rmk -	 */ -	if ((speed == SPEED_10 || speed == SPEED_100) && -	    duplex != DUPLEX_FULL) -		val |= MT7531_SGMII_FORCE_HALF_DUPLEX; - -	mt7530_write(priv, MT7531_SGMII_MODE(port), val); -} -  static bool mt753x_is_mac_port(u32 port)  {  	return (port == 5 || port == 6);  } -static int mt7531_sgmii_setup_mode_force(struct mt7530_priv *priv, u32 port, -					 phy_interface_t interface) -{ -	u32 val; - -	if (!mt753x_is_mac_port(port)) -		return -EINVAL; - -	mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port), -		   MT7531_SGMII_PHYA_PWD); - -	val = mt7530_read(priv, MT7531_PHYA_CTRL_SIGNAL3(port)); -	val &= ~MT7531_RG_TPHY_SPEED_MASK; -	/* Setup 2.5 times faster clock for 2.5Gbps data speeds with 10B/8B -	 * encoding. -	 */ -	val |= (interface == PHY_INTERFACE_MODE_2500BASEX) ? -		MT7531_RG_TPHY_SPEED_3_125G : MT7531_RG_TPHY_SPEED_1_25G; -	mt7530_write(priv, MT7531_PHYA_CTRL_SIGNAL3(port), val); - -	mt7530_clear(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE); - -	/* MT7531 SGMII 1G and 2.5G force mode can only work in full duplex -	 * mode, no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not. -	 */ -	mt7530_rmw(priv, MT7531_SGMII_MODE(port), -		   MT7531_SGMII_IF_MODE_MASK | MT7531_SGMII_REMOTE_FAULT_DIS, -		   MT7531_SGMII_FORCE_SPEED_1000); - -	mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0); - -	return 0; -} - -static int mt7531_sgmii_setup_mode_an(struct mt7530_priv *priv, int port, -				      phy_interface_t interface) -{ -	if (!mt753x_is_mac_port(port)) -		return -EINVAL; - -	mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port), -		   MT7531_SGMII_PHYA_PWD); - -	mt7530_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port), -		   MT7531_RG_TPHY_SPEED_MASK, MT7531_RG_TPHY_SPEED_1_25G); - -	mt7530_set(priv, MT7531_SGMII_MODE(port), -		   MT7531_SGMII_REMOTE_FAULT_DIS | -		   MT7531_SGMII_SPEED_DUPLEX_AN); - -	mt7530_rmw(priv, MT7531_PCS_SPEED_ABILITY(port), -		   MT7531_SGMII_TX_CONFIG_MASK, 1); - -	mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE); - -	mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_RESTART); - -	mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0); - -	return 0; -} - -static void mt7531_pcs_an_restart(struct phylink_pcs *pcs) +static int +mt7988_mac_config(struct dsa_switch *ds, int port, unsigned int mode, +		  phy_interface_t interface)  { -	struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; -	int port = pcs_to_mt753x_pcs(pcs)->port; -	u32 val; +	if (dsa_is_cpu_port(ds, port) && +	    interface == PHY_INTERFACE_MODE_INTERNAL) +		return 0; -	/* Only restart AN when AN is enabled */ -	val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port)); -	if (val & MT7531_SGMII_AN_ENABLE) { -		val |= MT7531_SGMII_AN_RESTART; -		mt7530_write(priv, MT7531_PCS_CONTROL_1(port), val); -	} +	return -EINVAL;  }  static int @@ -2716,11 +2717,11 @@ mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode,  		phydev = dp->slave->phydev;  		return mt7531_rgmii_setup(priv, port, interface, phydev);  	case PHY_INTERFACE_MODE_SGMII: -		return mt7531_sgmii_setup_mode_an(priv, port, interface);  	case PHY_INTERFACE_MODE_NA:  	case PHY_INTERFACE_MODE_1000BASEX:  	case PHY_INTERFACE_MODE_2500BASEX: -		return mt7531_sgmii_setup_mode_force(priv, port, interface); +		/* handled in SGMII PCS driver */ +		return 0;  	default:  		return -EINVAL;  	} @@ -2745,11 +2746,11 @@ mt753x_phylink_mac_select_pcs(struct dsa_switch *ds, int port,  	switch (interface) {  	case PHY_INTERFACE_MODE_TRGMII: +		return &priv->pcs[port].pcs;  	case PHY_INTERFACE_MODE_SGMII:  	case PHY_INTERFACE_MODE_1000BASEX:  	case PHY_INTERFACE_MODE_2500BASEX: -		return &priv->pcs[port].pcs; - +		return priv->ports[port].sgmii_pcs;  	default:  		return NULL;  	} @@ -2764,7 +2765,8 @@ mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,  	switch (port) {  	case 0 ... 4: /* Internal phy */ -		if (state->interface != PHY_INTERFACE_MODE_GMII) +		if (state->interface != PHY_INTERFACE_MODE_GMII && +		    state->interface != PHY_INTERFACE_MODE_INTERNAL)  			goto unsupported;  		break;  	case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ @@ -2842,7 +2844,8 @@ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port,  	/* MT753x MAC works in 1G full duplex mode for all up-clocked  	 * variants.  	 */ -	if (interface == PHY_INTERFACE_MODE_TRGMII || +	if (interface == PHY_INTERFACE_MODE_INTERNAL || +	    interface == PHY_INTERFACE_MODE_TRGMII ||  	    (phy_interface_mode_is_8023z(interface))) {  		speed = SPEED_1000;  		duplex = DUPLEX_FULL; @@ -2922,6 +2925,21 @@ mt7531_cpu_port_config(struct dsa_switch *ds, int port)  	return 0;  } +static int +mt7988_cpu_port_config(struct dsa_switch *ds, int port) +{ +	struct mt7530_priv *priv = ds->priv; + +	mt7530_write(priv, MT7530_PMCR_P(port), +		     PMCR_CPU_PORT_SETTING(priv->id)); + +	mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, +				   PHY_INTERFACE_MODE_INTERNAL, NULL, +				   SPEED_10000, DUPLEX_FULL, true, true); + +	return 0; +} +  static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,  				    struct phylink_config *config)  { @@ -2987,86 +3005,6 @@ static void mt7530_pcs_get_state(struct phylink_pcs *pcs,  		state->pause |= MLO_PAUSE_TX;  } -static int -mt7531_sgmii_pcs_get_state_an(struct mt7530_priv *priv, int port, -			      struct phylink_link_state *state) -{ -	u32 status, val; -	u16 config_reg; - -	status = mt7530_read(priv, MT7531_PCS_CONTROL_1(port)); -	state->link = !!(status & MT7531_SGMII_LINK_STATUS); -	state->an_complete = !!(status & MT7531_SGMII_AN_COMPLETE); -	if (state->interface == PHY_INTERFACE_MODE_SGMII && -	    (status & MT7531_SGMII_AN_ENABLE)) { -		val = mt7530_read(priv, MT7531_PCS_SPEED_ABILITY(port)); -		config_reg = val >> 16; - -		switch (config_reg & LPA_SGMII_SPD_MASK) { -		case LPA_SGMII_1000: -			state->speed = SPEED_1000; -			break; -		case LPA_SGMII_100: -			state->speed = SPEED_100; -			break; -		case LPA_SGMII_10: -			state->speed = SPEED_10; -			break; -		default: -			dev_err(priv->dev, "invalid sgmii PHY speed\n"); -			state->link = false; -			return -EINVAL; -		} - -		if (config_reg & LPA_SGMII_FULL_DUPLEX) -			state->duplex = DUPLEX_FULL; -		else -			state->duplex = DUPLEX_HALF; -	} - -	return 0; -} - -static void -mt7531_sgmii_pcs_get_state_inband(struct mt7530_priv *priv, int port, -				  struct phylink_link_state *state) -{ -	unsigned int val; - -	val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port)); -	state->link = !!(val & MT7531_SGMII_LINK_STATUS); -	if (!state->link) -		return; - -	state->an_complete = state->link; - -	if (state->interface == PHY_INTERFACE_MODE_2500BASEX) -		state->speed = SPEED_2500; -	else -		state->speed = SPEED_1000; - -	state->duplex = DUPLEX_FULL; -	state->pause = MLO_PAUSE_NONE; -} - -static void mt7531_pcs_get_state(struct phylink_pcs *pcs, -				 struct phylink_link_state *state) -{ -	struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; -	int port = pcs_to_mt753x_pcs(pcs)->port; - -	if (state->interface == PHY_INTERFACE_MODE_SGMII) { -		mt7531_sgmii_pcs_get_state_an(priv, port, state); -		return; -	} else if ((state->interface == PHY_INTERFACE_MODE_1000BASEX) || -		   (state->interface == PHY_INTERFACE_MODE_2500BASEX)) { -		mt7531_sgmii_pcs_get_state_inband(priv, port, state); -		return; -	} - -	state->link = false; -} -  static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode,  			     phy_interface_t interface,  			     const unsigned long *advertising, @@ -3086,14 +3024,6 @@ static const struct phylink_pcs_ops mt7530_pcs_ops = {  	.pcs_an_restart = mt7530_pcs_an_restart,  }; -static const struct phylink_pcs_ops mt7531_pcs_ops = { -	.pcs_validate = mt753x_pcs_validate, -	.pcs_get_state = mt7531_pcs_get_state, -	.pcs_config = mt753x_pcs_config, -	.pcs_an_restart = mt7531_pcs_an_restart, -	.pcs_link_up = mt7531_pcs_link_up, -}; -  static int  mt753x_setup(struct dsa_switch *ds)  { @@ -3105,8 +3035,6 @@ mt753x_setup(struct dsa_switch *ds)  		priv->pcs[i].pcs.ops = priv->info->pcs_ops;  		priv->pcs[i].priv = priv;  		priv->pcs[i].port = i; -		if (mt753x_is_mac_port(i)) -			priv->pcs[i].pcs.poll = 1;  	}  	ret = priv->info->sw_setup(ds); @@ -3121,6 +3049,12 @@ mt753x_setup(struct dsa_switch *ds)  	if (ret && priv->irq)  		mt7530_free_irq_common(priv); +	if (priv->create_sgmii) { +		ret = priv->create_sgmii(priv, mt7531_dual_sgmii_supported(priv)); +		if (ret && priv->irq) +			mt7530_free_irq(priv); +	} +  	return ret;  } @@ -3154,9 +3088,31 @@ static int mt753x_set_mac_eee(struct dsa_switch *ds, int port,  	return 0;  } -static const struct dsa_switch_ops mt7530_switch_ops = { +static int mt7988_pad_setup(struct dsa_switch *ds, phy_interface_t interface) +{ +	return 0; +} + +static int mt7988_setup(struct dsa_switch *ds) +{ +	struct mt7530_priv *priv = ds->priv; + +	/* Reset the switch */ +	reset_control_assert(priv->rstc); +	usleep_range(20, 50); +	reset_control_deassert(priv->rstc); +	usleep_range(20, 50); + +	/* Reset the switch PHYs */ +	mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_PHY_RST); + +	return mt7531_setup_common(ds); +} + +const struct dsa_switch_ops mt7530_switch_ops = {  	.get_tag_protocol	= mtk_get_tag_protocol,  	.setup			= mt753x_setup, +	.preferred_default_local_cpu_port = mt753x_preferred_default_local_cpu_port,  	.get_strings		= mt7530_get_strings,  	.get_ethtool_stats	= mt7530_get_ethtool_stats,  	.get_sset_count		= mt7530_get_sset_count, @@ -3188,8 +3144,9 @@ static const struct dsa_switch_ops mt7530_switch_ops = {  	.get_mac_eee		= mt753x_get_mac_eee,  	.set_mac_eee		= mt753x_set_mac_eee,  }; +EXPORT_SYMBOL_GPL(mt7530_switch_ops); -static const struct mt753x_info mt753x_table[] = { +const struct mt753x_info mt753x_table[] = {  	[ID_MT7621] = {  		.id = ID_MT7621,  		.pcs_ops = &mt7530_pcs_ops, @@ -3216,7 +3173,7 @@ static const struct mt753x_info mt753x_table[] = {  	},  	[ID_MT7531] = {  		.id = ID_MT7531, -		.pcs_ops = &mt7531_pcs_ops, +		.pcs_ops = &mt7530_pcs_ops,  		.sw_setup = mt7531_setup,  		.phy_read_c22 = mt7531_ind_c22_phy_read,  		.phy_write_c22 = mt7531_ind_c22_phy_write, @@ -3227,53 +3184,38 @@ static const struct mt753x_info mt753x_table[] = {  		.mac_port_get_caps = mt7531_mac_port_get_caps,  		.mac_port_config = mt7531_mac_config,  	}, +	[ID_MT7988] = { +		.id = ID_MT7988, +		.pcs_ops = &mt7530_pcs_ops, +		.sw_setup = mt7988_setup, +		.phy_read_c22 = mt7531_ind_c22_phy_read, +		.phy_write_c22 = mt7531_ind_c22_phy_write, +		.phy_read_c45 = mt7531_ind_c45_phy_read, +		.phy_write_c45 = mt7531_ind_c45_phy_write, +		.pad_setup = mt7988_pad_setup, +		.cpu_port_config = mt7988_cpu_port_config, +		.mac_port_get_caps = mt7988_mac_port_get_caps, +		.mac_port_config = mt7988_mac_config, +	},  }; +EXPORT_SYMBOL_GPL(mt753x_table); -static const struct of_device_id mt7530_of_match[] = { -	{ .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], }, -	{ .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], }, -	{ .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], }, -	{ /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, mt7530_of_match); - -static int -mt7530_probe(struct mdio_device *mdiodev) +int +mt7530_probe_common(struct mt7530_priv *priv)  { -	struct mt7530_priv *priv; -	struct device_node *dn; - -	dn = mdiodev->dev.of_node; - -	priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); -	if (!priv) -		return -ENOMEM; +	struct device *dev = priv->dev; -	priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); +	priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL);  	if (!priv->ds)  		return -ENOMEM; -	priv->ds->dev = &mdiodev->dev; +	priv->ds->dev = dev;  	priv->ds->num_ports = MT7530_NUM_PORTS; -	/* Use medatek,mcm property to distinguish hardware type that would -	 * casues a little bit differences on power-on sequence. -	 */ -	priv->mcm = of_property_read_bool(dn, "mediatek,mcm"); -	if (priv->mcm) { -		dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n"); - -		priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm"); -		if (IS_ERR(priv->rstc)) { -			dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); -			return PTR_ERR(priv->rstc); -		} -	} -  	/* Get the hardware identifier from the devicetree node.  	 * We will need it for some of the clock and regulator setup.  	 */ -	priv->info = of_device_get_match_data(&mdiodev->dev); +	priv->info = of_device_get_match_data(dev);  	if (!priv->info)  		return -EINVAL; @@ -3287,90 +3229,27 @@ mt7530_probe(struct mdio_device *mdiodev)  		return -EINVAL;  	priv->id = priv->info->id; - -	if (priv->id == ID_MT7530) { -		priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core"); -		if (IS_ERR(priv->core_pwr)) -			return PTR_ERR(priv->core_pwr); - -		priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io"); -		if (IS_ERR(priv->io_pwr)) -			return PTR_ERR(priv->io_pwr); -	} - -	/* Not MCM that indicates switch works as the remote standalone -	 * integrated circuit so the GPIO pin would be used to complete -	 * the reset, otherwise memory-mapped register accessing used -	 * through syscon provides in the case of MCM. -	 */ -	if (!priv->mcm) { -		priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset", -						      GPIOD_OUT_LOW); -		if (IS_ERR(priv->reset)) { -			dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); -			return PTR_ERR(priv->reset); -		} -	} - -	priv->bus = mdiodev->bus; -	priv->dev = &mdiodev->dev; +	priv->dev = dev;  	priv->ds->priv = priv;  	priv->ds->ops = &mt7530_switch_ops;  	mutex_init(&priv->reg_mutex); -	dev_set_drvdata(&mdiodev->dev, priv); +	dev_set_drvdata(dev, priv); -	return dsa_register_switch(priv->ds); +	return 0;  } +EXPORT_SYMBOL_GPL(mt7530_probe_common); -static void -mt7530_remove(struct mdio_device *mdiodev) +void +mt7530_remove_common(struct mt7530_priv *priv)  { -	struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); -	int ret = 0; - -	if (!priv) -		return; - -	ret = regulator_disable(priv->core_pwr); -	if (ret < 0) -		dev_err(priv->dev, -			"Failed to disable core power: %d\n", ret); - -	ret = regulator_disable(priv->io_pwr); -	if (ret < 0) -		dev_err(priv->dev, "Failed to disable io pwr: %d\n", -			ret); -  	if (priv->irq)  		mt7530_free_irq(priv);  	dsa_unregister_switch(priv->ds); -	mutex_destroy(&priv->reg_mutex); -} - -static void mt7530_shutdown(struct mdio_device *mdiodev) -{ -	struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); - -	if (!priv) -		return; -	dsa_switch_shutdown(priv->ds); - -	dev_set_drvdata(&mdiodev->dev, NULL); +	mutex_destroy(&priv->reg_mutex);  } - -static struct mdio_driver mt7530_mdio_driver = { -	.probe  = mt7530_probe, -	.remove = mt7530_remove, -	.shutdown = mt7530_shutdown, -	.mdiodrv.driver = { -		.name = "mt7530", -		.of_match_table = mt7530_of_match, -	}, -}; - -mdio_module_driver(mt7530_mdio_driver); +EXPORT_SYMBOL_GPL(mt7530_remove_common);  MODULE_AUTHOR("Sean Wang <[email protected]>");  MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch"); diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index 6b2fc6290ea8..08045b035e6a 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -18,6 +18,7 @@ enum mt753x_id {  	ID_MT7530 = 0,  	ID_MT7621 = 1,  	ID_MT7531 = 2, +	ID_MT7988 = 3,  };  #define	NUM_TRGMII_CTRL			5 @@ -53,18 +54,24 @@ enum mt753x_id {  #define  MT7531_MIRROR_PORT_GET(x)	(((x) >> 16) & MIRROR_MASK)  #define  MT7531_MIRROR_PORT_SET(x)	(((x) & MIRROR_MASK) << 16)  #define  MT7531_CPU_PMAP_MASK		GENMASK(7, 0) +#define  MT7531_CPU_PMAP(x)		FIELD_PREP(MT7531_CPU_PMAP_MASK, x) -#define MT753X_MIRROR_REG(id)		(((id) == ID_MT7531) ? \ +#define MT753X_MIRROR_REG(id)		((((id) == ID_MT7531) || ((id) == ID_MT7988)) ?	\  					 MT7531_CFC : MT7530_MFC) -#define MT753X_MIRROR_EN(id)		(((id) == ID_MT7531) ? \ +#define MT753X_MIRROR_EN(id)		((((id) == ID_MT7531) || ((id) == ID_MT7988)) ?	\  					 MT7531_MIRROR_EN : MIRROR_EN) -#define MT753X_MIRROR_MASK(id)		(((id) == ID_MT7531) ? \ +#define MT753X_MIRROR_MASK(id)		((((id) == ID_MT7531) || ((id) == ID_MT7988)) ?	\  					 MT7531_MIRROR_MASK : MIRROR_MASK)  /* Registers for BPDU and PAE frame control*/  #define MT753X_BPC			0x24  #define  MT753X_BPDU_PORT_FW_MASK	GENMASK(2, 0) +/* Register for :03 and :0E MAC DA frame control */ +#define MT753X_RGAC2			0x2c +#define  MT753X_R0E_PORT_FW_MASK	GENMASK(18, 16) +#define  MT753X_R0E_PORT_FW(x)		FIELD_PREP(MT753X_R0E_PORT_FW_MASK, x) +  enum mt753x_bpdu_port_fw {  	MT753X_BPDU_FOLLOW_MFC,  	MT753X_BPDU_CPU_EXCLUDE = 4, @@ -295,9 +302,8 @@ enum mt7530_vlan_port_acc_frm {  					 MT7531_FORCE_DPX | \  					 MT7531_FORCE_RX_FC | \  					 MT7531_FORCE_TX_FC) -#define  PMCR_FORCE_MODE_ID(id)		(((id) == ID_MT7531) ? \ -					 MT7531_FORCE_MODE : \ -					 PMCR_FORCE_MODE) +#define  PMCR_FORCE_MODE_ID(id)		((((id) == ID_MT7531) || ((id) == ID_MT7988)) ?	\ +					 MT7531_FORCE_MODE : PMCR_FORCE_MODE)  #define  PMCR_LINK_SETTINGS_MASK	(PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \  					 PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \  					 PMCR_TX_FC_EN | PMCR_RX_FC_EN | \ @@ -364,47 +370,8 @@ enum mt7530_vlan_port_acc_frm {  					 CCR_TX_OCT_CNT_BAD)  /* MT7531 SGMII register group */ -#define MT7531_SGMII_REG_BASE		0x5000 -#define MT7531_SGMII_REG(p, r)		(MT7531_SGMII_REG_BASE + \ -					((p) - 5) * 0x1000 + (r)) - -/* Register forSGMII PCS_CONTROL_1 */ -#define MT7531_PCS_CONTROL_1(p)		MT7531_SGMII_REG(p, 0x00) -#define  MT7531_SGMII_LINK_STATUS	BIT(18) -#define  MT7531_SGMII_AN_ENABLE		BIT(12) -#define  MT7531_SGMII_AN_RESTART	BIT(9) -#define  MT7531_SGMII_AN_COMPLETE	BIT(21) - -/* Register for SGMII PCS_SPPED_ABILITY */ -#define MT7531_PCS_SPEED_ABILITY(p)	MT7531_SGMII_REG(p, 0x08) -#define  MT7531_SGMII_TX_CONFIG_MASK	GENMASK(15, 0) -#define  MT7531_SGMII_TX_CONFIG		BIT(0) - -/* Register for SGMII_MODE */ -#define MT7531_SGMII_MODE(p)		MT7531_SGMII_REG(p, 0x20) -#define  MT7531_SGMII_REMOTE_FAULT_DIS	BIT(8) -#define  MT7531_SGMII_IF_MODE_MASK	GENMASK(5, 1) -#define  MT7531_SGMII_FORCE_DUPLEX	BIT(4) -#define  MT7531_SGMII_FORCE_SPEED_MASK	GENMASK(3, 2) -#define  MT7531_SGMII_FORCE_SPEED_1000	BIT(3) -#define  MT7531_SGMII_FORCE_SPEED_100	BIT(2) -#define  MT7531_SGMII_FORCE_SPEED_10	0 -#define  MT7531_SGMII_SPEED_DUPLEX_AN	BIT(1) - -enum mt7531_sgmii_force_duplex { -	MT7531_SGMII_FORCE_FULL_DUPLEX = 0, -	MT7531_SGMII_FORCE_HALF_DUPLEX = 0x10, -}; - -/* Fields of QPHY_PWR_STATE_CTRL */ -#define MT7531_QPHY_PWR_STATE_CTRL(p)	MT7531_SGMII_REG(p, 0xe8) -#define  MT7531_SGMII_PHYA_PWD		BIT(4) - -/* Values of SGMII SPEED */ -#define MT7531_PHYA_CTRL_SIGNAL3(p)	MT7531_SGMII_REG(p, 0x128) -#define  MT7531_RG_TPHY_SPEED_MASK	(BIT(2) | BIT(3)) -#define  MT7531_RG_TPHY_SPEED_1_25G	0x0 -#define  MT7531_RG_TPHY_SPEED_3_125G	BIT(2) +#define MT7531_SGMII_REG_BASE(p)	(0x5000 + ((p) - 5) * 0x1000) +#define MT7531_PHYA_CTRL_SIGNAL3	0x128  /* Register for system reset */  #define MT7530_SYS_CTRL			0x7000 @@ -703,13 +670,13 @@ struct mt7530_fdb {   * @pm:		The matrix used to show all connections with the port.   * @pvid:	The VLAN specified is to be considered a PVID at ingress.  Any   *		untagged frames will be assigned to the related VLAN. - * @vlan_filtering: The flags indicating whether the port that can recognize - *		    VLAN-tagged frames. + * @sgmii_pcs:	Pointer to PCS instance for SerDes ports   */  struct mt7530_port {  	bool enable;  	u32 pm;  	u16 pvid; +	struct phylink_pcs *sgmii_pcs;  };  /* Port 5 interface select definitions */ @@ -721,24 +688,6 @@ enum p5_interface_select {  	P5_INTF_SEL_GMAC5_SGMII,  }; -static const char *p5_intf_modes(unsigned int p5_interface) -{ -	switch (p5_interface) { -	case P5_DISABLED: -		return "DISABLED"; -	case P5_INTF_SEL_PHY_P0: -		return "PHY P0"; -	case P5_INTF_SEL_PHY_P4: -		return "PHY P4"; -	case P5_INTF_SEL_GMAC5: -		return "GMAC5"; -	case P5_INTF_SEL_GMAC5_SGMII: -		return "GMAC5_SGMII"; -	default: -		return "unknown"; -	} -} -  struct mt7530_priv;  struct mt753x_pcs { @@ -793,6 +742,7 @@ struct mt753x_info {   * @dev:		The device pointer   * @ds:			The pointer to the dsa core structure   * @bus:		The bus used for the device and built-in PHY + * @regmap:		The regmap instance representing all switch registers   * @rstc:		The pointer to reset control used by MCM   * @core_pwr:		The power supplied into the core   * @io_pwr:		The power supplied into the I/O @@ -804,15 +754,16 @@ struct mt753x_info {   *			registers   * @p6_interface	Holding the current port 6 interface   * @p5_intf_sel:	Holding the current port 5 interface select - *   * @irq:		IRQ number of the switch   * @irq_domain:		IRQ domain of the switch irq_chip   * @irq_enable:		IRQ enable bits, synced to SYS_INT_EN + * @create_sgmii:	Pointer to function creating SGMII PCS instance(s)   */  struct mt7530_priv {  	struct device		*dev;  	struct dsa_switch	*ds;  	struct mii_bus		*bus; +	struct regmap		*regmap;  	struct reset_control	*rstc;  	struct regulator	*core_pwr;  	struct regulator	*io_pwr; @@ -825,7 +776,6 @@ struct mt7530_priv {  	unsigned int		p5_intf_sel;  	u8			mirror_rx;  	u8			mirror_tx; -  	struct mt7530_port	ports[MT7530_NUM_PORTS];  	struct mt753x_pcs	pcs[MT7530_NUM_PORTS];  	/* protect among processes for registers access*/ @@ -833,6 +783,7 @@ struct mt7530_priv {  	int irq;  	struct irq_domain *irq_domain;  	u32 irq_enable; +	int (*create_sgmii)(struct mt7530_priv *priv, bool dual_sgmii);  };  struct mt7530_hw_vlan_entry { @@ -869,4 +820,10 @@ static inline void INIT_MT7530_DUMMY_POLL(struct mt7530_dummy_poll *p,  	p->reg = reg;  } +int mt7530_probe_common(struct mt7530_priv *priv); +void mt7530_remove_common(struct mt7530_priv *priv); + +extern const struct dsa_switch_ops mt7530_switch_ops; +extern const struct mt753x_info mt753x_table[]; +  #endif /* __MT7530_H */ diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 0a5d6c7bb128..08a46ffd53af 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -611,10 +611,10 @@ static void mv88e6185_phylink_get_caps(struct mv88e6xxx_chip *chip, int port,  }  static const u8 mv88e6xxx_phy_interface_modes[] = { -	[MV88E6XXX_PORT_STS_CMODE_MII_PHY]	= PHY_INTERFACE_MODE_MII, +	[MV88E6XXX_PORT_STS_CMODE_MII_PHY]	= PHY_INTERFACE_MODE_REVMII,  	[MV88E6XXX_PORT_STS_CMODE_MII]		= PHY_INTERFACE_MODE_MII,  	[MV88E6XXX_PORT_STS_CMODE_GMII]		= PHY_INTERFACE_MODE_GMII, -	[MV88E6XXX_PORT_STS_CMODE_RMII_PHY]	= PHY_INTERFACE_MODE_RMII, +	[MV88E6XXX_PORT_STS_CMODE_RMII_PHY]	= PHY_INTERFACE_MODE_REVRMII,  	[MV88E6XXX_PORT_STS_CMODE_RMII]		= PHY_INTERFACE_MODE_RMII,  	[MV88E6XXX_PORT_STS_CMODE_100BASEX]	= PHY_INTERFACE_MODE_100BASEX,  	[MV88E6XXX_PORT_STS_CMODE_1000BASEX]	= PHY_INTERFACE_MODE_1000BASEX, @@ -3354,9 +3354,14 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)  	 * If this is the upstream port for this switch, enable  	 * forwarding of unknown unicasts and multicasts.  	 */ -	reg = MV88E6XXX_PORT_CTL0_IGMP_MLD_SNOOP | -		MV88E6185_PORT_CTL0_USE_TAG | MV88E6185_PORT_CTL0_USE_IP | +	reg = MV88E6185_PORT_CTL0_USE_TAG | MV88E6185_PORT_CTL0_USE_IP |  		MV88E6XXX_PORT_CTL0_STATE_FORWARDING; +	/* Forward any IPv4 IGMP or IPv6 MLD frames received +	 * by a USER port to the CPU port to allow snooping. +	 */ +	if (dsa_is_user_port(ds, port)) +		reg |= MV88E6XXX_PORT_CTL0_IGMP_MLD_SNOOP; +  	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);  	if (err)  		return err; @@ -3549,7 +3554,7 @@ static int mv88e6xxx_get_max_mtu(struct dsa_switch *ds, int port)  		return 10240 - VLAN_ETH_HLEN - EDSA_HLEN - ETH_FCS_LEN;  	else if (chip->info->ops->set_max_frame_size)  		return 1632 - VLAN_ETH_HLEN - EDSA_HLEN - ETH_FCS_LEN; -	return 1522 - VLAN_ETH_HLEN - EDSA_HLEN - ETH_FCS_LEN; +	return ETH_DATA_LEN;  }  static int mv88e6xxx_change_mtu(struct dsa_switch *ds, int port, int new_mtu) @@ -3557,6 +3562,17 @@ static int mv88e6xxx_change_mtu(struct dsa_switch *ds, int port, int new_mtu)  	struct mv88e6xxx_chip *chip = ds->priv;  	int ret = 0; +	/* For families where we don't know how to alter the MTU, +	 * just accept any value up to ETH_DATA_LEN +	 */ +	if (!chip->info->ops->port_set_jumbo_size && +	    !chip->info->ops->set_max_frame_size) { +		if (new_mtu > ETH_DATA_LEN) +			return -EINVAL; + +		return 0; +	} +  	if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))  		new_mtu += EDSA_HLEN; @@ -3565,9 +3581,6 @@ static int mv88e6xxx_change_mtu(struct dsa_switch *ds, int port, int new_mtu)  		ret = chip->info->ops->port_set_jumbo_size(chip, port, new_mtu);  	else if (chip->info->ops->set_max_frame_size)  		ret = chip->info->ops->set_max_frame_size(chip, new_mtu); -	else -		if (new_mtu > 1522) -			ret = -EINVAL;  	mv88e6xxx_reg_unlock(chip);  	return ret; @@ -3672,185 +3685,6 @@ static int mv88e6390_setup_errata(struct mv88e6xxx_chip *chip)  	return mv88e6xxx_software_reset(chip);  } -static void mv88e6xxx_teardown(struct dsa_switch *ds) -{ -	mv88e6xxx_teardown_devlink_params(ds); -	dsa_devlink_resources_unregister(ds); -	mv88e6xxx_teardown_devlink_regions_global(ds); -} - -static int mv88e6xxx_setup(struct dsa_switch *ds) -{ -	struct mv88e6xxx_chip *chip = ds->priv; -	u8 cmode; -	int err; -	int i; - -	chip->ds = ds; -	ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip); - -	/* Since virtual bridges are mapped in the PVT, the number we support -	 * depends on the physical switch topology. We need to let DSA figure -	 * that out and therefore we cannot set this at dsa_register_switch() -	 * time. -	 */ -	if (mv88e6xxx_has_pvt(chip)) -		ds->max_num_bridges = MV88E6XXX_MAX_PVT_SWITCHES - -				      ds->dst->last_switch - 1; - -	mv88e6xxx_reg_lock(chip); - -	if (chip->info->ops->setup_errata) { -		err = chip->info->ops->setup_errata(chip); -		if (err) -			goto unlock; -	} - -	/* Cache the cmode of each port. */ -	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { -		if (chip->info->ops->port_get_cmode) { -			err = chip->info->ops->port_get_cmode(chip, i, &cmode); -			if (err) -				goto unlock; - -			chip->ports[i].cmode = cmode; -		} -	} - -	err = mv88e6xxx_vtu_setup(chip); -	if (err) -		goto unlock; - -	/* Must be called after mv88e6xxx_vtu_setup (which flushes the -	 * VTU, thereby also flushing the STU). -	 */ -	err = mv88e6xxx_stu_setup(chip); -	if (err) -		goto unlock; - -	/* Setup Switch Port Registers */ -	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { -		if (dsa_is_unused_port(ds, i)) -			continue; - -		/* Prevent the use of an invalid port. */ -		if (mv88e6xxx_is_invalid_port(chip, i)) { -			dev_err(chip->dev, "port %d is invalid\n", i); -			err = -EINVAL; -			goto unlock; -		} - -		err = mv88e6xxx_setup_port(chip, i); -		if (err) -			goto unlock; -	} - -	err = mv88e6xxx_irl_setup(chip); -	if (err) -		goto unlock; - -	err = mv88e6xxx_mac_setup(chip); -	if (err) -		goto unlock; - -	err = mv88e6xxx_phy_setup(chip); -	if (err) -		goto unlock; - -	err = mv88e6xxx_pvt_setup(chip); -	if (err) -		goto unlock; - -	err = mv88e6xxx_atu_setup(chip); -	if (err) -		goto unlock; - -	err = mv88e6xxx_broadcast_setup(chip, 0); -	if (err) -		goto unlock; - -	err = mv88e6xxx_pot_setup(chip); -	if (err) -		goto unlock; - -	err = mv88e6xxx_rmu_setup(chip); -	if (err) -		goto unlock; - -	err = mv88e6xxx_rsvd2cpu_setup(chip); -	if (err) -		goto unlock; - -	err = mv88e6xxx_trunk_setup(chip); -	if (err) -		goto unlock; - -	err = mv88e6xxx_devmap_setup(chip); -	if (err) -		goto unlock; - -	err = mv88e6xxx_pri_setup(chip); -	if (err) -		goto unlock; - -	/* Setup PTP Hardware Clock and timestamping */ -	if (chip->info->ptp_support) { -		err = mv88e6xxx_ptp_setup(chip); -		if (err) -			goto unlock; - -		err = mv88e6xxx_hwtstamp_setup(chip); -		if (err) -			goto unlock; -	} - -	err = mv88e6xxx_stats_setup(chip); -	if (err) -		goto unlock; - -unlock: -	mv88e6xxx_reg_unlock(chip); - -	if (err) -		return err; - -	/* Have to be called without holding the register lock, since -	 * they take the devlink lock, and we later take the locks in -	 * the reverse order when getting/setting parameters or -	 * resource occupancy. -	 */ -	err = mv88e6xxx_setup_devlink_resources(ds); -	if (err) -		return err; - -	err = mv88e6xxx_setup_devlink_params(ds); -	if (err) -		goto out_resources; - -	err = mv88e6xxx_setup_devlink_regions_global(ds); -	if (err) -		goto out_params; - -	return 0; - -out_params: -	mv88e6xxx_teardown_devlink_params(ds); -out_resources: -	dsa_devlink_resources_unregister(ds); - -	return err; -} - -static int mv88e6xxx_port_setup(struct dsa_switch *ds, int port) -{ -	return mv88e6xxx_setup_devlink_regions_port(ds, port); -} - -static void mv88e6xxx_port_teardown(struct dsa_switch *ds, int port) -{ -	mv88e6xxx_teardown_devlink_regions_port(ds, port); -} -  /* prod_id for switch families which do not have a PHY model number */  static const u16 family_prod_id_table[] = {  	[MV88E6XXX_FAMILY_6341] = MV88E6XXX_PORT_SWITCH_ID_PROD_6341, @@ -3976,6 +3810,9 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,  	bus->read_c45 = mv88e6xxx_mdio_read_c45;  	bus->write_c45 = mv88e6xxx_mdio_write_c45;  	bus->parent = chip->dev; +	bus->phy_mask = ~GENMASK(chip->info->phy_base_addr + +				 mv88e6xxx_num_ports(chip) - 1, +				 chip->info->phy_base_addr);  	if (!external) {  		err = mv88e6xxx_g2_irq_mdio_setup(chip, bus); @@ -4019,9 +3856,9 @@ static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip)  	}  } -static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip, -				    struct device_node *np) +static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip)  { +	struct device_node *np = chip->dev->of_node;  	struct device_node *child;  	int err; @@ -4054,6 +3891,194 @@ static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip,  	return 0;  } +static void mv88e6xxx_teardown(struct dsa_switch *ds) +{ +	struct mv88e6xxx_chip *chip = ds->priv; + +	mv88e6xxx_teardown_devlink_params(ds); +	dsa_devlink_resources_unregister(ds); +	mv88e6xxx_teardown_devlink_regions_global(ds); +	mv88e6xxx_mdios_unregister(chip); +} + +static int mv88e6xxx_setup(struct dsa_switch *ds) +{ +	struct mv88e6xxx_chip *chip = ds->priv; +	u8 cmode; +	int err; +	int i; + +	err = mv88e6xxx_mdios_register(chip); +	if (err) +		return err; + +	chip->ds = ds; +	ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip); + +	/* Since virtual bridges are mapped in the PVT, the number we support +	 * depends on the physical switch topology. We need to let DSA figure +	 * that out and therefore we cannot set this at dsa_register_switch() +	 * time. +	 */ +	if (mv88e6xxx_has_pvt(chip)) +		ds->max_num_bridges = MV88E6XXX_MAX_PVT_SWITCHES - +				      ds->dst->last_switch - 1; + +	mv88e6xxx_reg_lock(chip); + +	if (chip->info->ops->setup_errata) { +		err = chip->info->ops->setup_errata(chip); +		if (err) +			goto unlock; +	} + +	/* Cache the cmode of each port. */ +	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { +		if (chip->info->ops->port_get_cmode) { +			err = chip->info->ops->port_get_cmode(chip, i, &cmode); +			if (err) +				goto unlock; + +			chip->ports[i].cmode = cmode; +		} +	} + +	err = mv88e6xxx_vtu_setup(chip); +	if (err) +		goto unlock; + +	/* Must be called after mv88e6xxx_vtu_setup (which flushes the +	 * VTU, thereby also flushing the STU). +	 */ +	err = mv88e6xxx_stu_setup(chip); +	if (err) +		goto unlock; + +	/* Setup Switch Port Registers */ +	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { +		if (dsa_is_unused_port(ds, i)) +			continue; + +		/* Prevent the use of an invalid port. */ +		if (mv88e6xxx_is_invalid_port(chip, i)) { +			dev_err(chip->dev, "port %d is invalid\n", i); +			err = -EINVAL; +			goto unlock; +		} + +		err = mv88e6xxx_setup_port(chip, i); +		if (err) +			goto unlock; +	} + +	err = mv88e6xxx_irl_setup(chip); +	if (err) +		goto unlock; + +	err = mv88e6xxx_mac_setup(chip); +	if (err) +		goto unlock; + +	err = mv88e6xxx_phy_setup(chip); +	if (err) +		goto unlock; + +	err = mv88e6xxx_pvt_setup(chip); +	if (err) +		goto unlock; + +	err = mv88e6xxx_atu_setup(chip); +	if (err) +		goto unlock; + +	err = mv88e6xxx_broadcast_setup(chip, 0); +	if (err) +		goto unlock; + +	err = mv88e6xxx_pot_setup(chip); +	if (err) +		goto unlock; + +	err = mv88e6xxx_rmu_setup(chip); +	if (err) +		goto unlock; + +	err = mv88e6xxx_rsvd2cpu_setup(chip); +	if (err) +		goto unlock; + +	err = mv88e6xxx_trunk_setup(chip); +	if (err) +		goto unlock; + +	err = mv88e6xxx_devmap_setup(chip); +	if (err) +		goto unlock; + +	err = mv88e6xxx_pri_setup(chip); +	if (err) +		goto unlock; + +	/* Setup PTP Hardware Clock and timestamping */ +	if (chip->info->ptp_support) { +		err = mv88e6xxx_ptp_setup(chip); +		if (err) +			goto unlock; + +		err = mv88e6xxx_hwtstamp_setup(chip); +		if (err) +			goto unlock; +	} + +	err = mv88e6xxx_stats_setup(chip); +	if (err) +		goto unlock; + +unlock: +	mv88e6xxx_reg_unlock(chip); + +	if (err) +		goto out_mdios; + +	/* Have to be called without holding the register lock, since +	 * they take the devlink lock, and we later take the locks in +	 * the reverse order when getting/setting parameters or +	 * resource occupancy. +	 */ +	err = mv88e6xxx_setup_devlink_resources(ds); +	if (err) +		goto out_mdios; + +	err = mv88e6xxx_setup_devlink_params(ds); +	if (err) +		goto out_resources; + +	err = mv88e6xxx_setup_devlink_regions_global(ds); +	if (err) +		goto out_params; + +	return 0; + +out_params: +	mv88e6xxx_teardown_devlink_params(ds); +out_resources: +	dsa_devlink_resources_unregister(ds); +out_mdios: +	mv88e6xxx_mdios_unregister(chip); + +	return err; +} + +static int mv88e6xxx_port_setup(struct dsa_switch *ds, int port) +{ +	return mv88e6xxx_setup_devlink_regions_port(ds, port); +} + +static void mv88e6xxx_port_teardown(struct dsa_switch *ds, int port) +{ +	mv88e6xxx_teardown_devlink_regions_port(ds, port); +} +  static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)  {  	struct mv88e6xxx_chip *chip = ds->priv; @@ -5169,6 +5194,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {  	.set_cpu_port = mv88e6095_g1_set_cpu_port,  	.set_egress_port = mv88e6095_g1_set_egress_port,  	.watchdog_ops = &mv88e6390_watchdog_ops, +	.mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu,  	.reset = mv88e6352_g1_reset,  	.vtu_getnext = mv88e6185_g1_vtu_getnext,  	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, @@ -5588,7 +5614,7 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = {  	 * .port_set_upstream_port method.  	 */  	.set_egress_port = mv88e6393x_set_egress_port, -	.watchdog_ops = &mv88e6390_watchdog_ops, +	.watchdog_ops = &mv88e6393x_watchdog_ops,  	.mgmt_rsvd2cpu = mv88e6393x_port_mgmt_rsvd2cpu,  	.pot_clear = mv88e6xxx_g2_pot_clear,  	.reset = mv88e6352_g1_reset, @@ -7144,7 +7170,7 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)  		goto out;  	}  	if (chip->reset) -		usleep_range(1000, 2000); +		usleep_range(10000, 20000);  	/* Detect if the device is configured in single chip addressing mode,  	 * otherwise continue with address specific smi init/detection. @@ -7220,18 +7246,12 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)  	if (err)  		goto out_g1_atu_prob_irq; -	err = mv88e6xxx_mdios_register(chip, np); -	if (err) -		goto out_g1_vtu_prob_irq; -  	err = mv88e6xxx_register_switch(chip);  	if (err) -		goto out_mdio; +		goto out_g1_vtu_prob_irq;  	return 0; -out_mdio: -	mv88e6xxx_mdios_unregister(chip);  out_g1_vtu_prob_irq:  	mv88e6xxx_g1_vtu_prob_irq_free(chip);  out_g1_atu_prob_irq: @@ -7268,7 +7288,6 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)  	mv88e6xxx_phy_destroy(chip);  	mv88e6xxx_unregister_switch(chip); -	mv88e6xxx_mdios_unregister(chip);  	mv88e6xxx_g1_vtu_prob_irq_free(chip);  	mv88e6xxx_g1_atu_prob_irq_free(chip); diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index ed3b2f88e783..615896893076 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -943,6 +943,26 @@ const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {  	.irq_free = mv88e6390_watchdog_free,  }; +static int mv88e6393x_watchdog_action(struct mv88e6xxx_chip *chip, int irq) +{ +	mv88e6390_watchdog_action(chip, irq); + +	/* Fix for clearing the force WD event bit. +	 * Unreleased erratum on mv88e6393x. +	 */ +	mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL, +			   MV88E6390_G2_WDOG_CTL_UPDATE | +			   MV88E6390_G2_WDOG_CTL_PTR_EVENT); + +	return IRQ_HANDLED; +} + +const struct mv88e6xxx_irq_ops mv88e6393x_watchdog_ops = { +	.irq_action = mv88e6393x_watchdog_action, +	.irq_setup = mv88e6390_watchdog_setup, +	.irq_free = mv88e6390_watchdog_free, +}; +  static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id)  {  	struct mv88e6xxx_chip *chip = dev_id; @@ -1176,31 +1196,19 @@ out:  int mv88e6xxx_g2_irq_mdio_setup(struct mv88e6xxx_chip *chip,  				struct mii_bus *bus)  { -	int phy, irq, err, err_phy; +	int phy, irq;  	for (phy = 0; phy < chip->info->num_internal_phys; phy++) {  		irq = irq_find_mapping(chip->g2_irq.domain, phy); -		if (irq < 0) { -			err = irq; -			goto out; -		} +		if (irq < 0) +			return irq; +  		bus->irq[chip->info->phy_base_addr + phy] = irq;  	}  	return 0; -out: -	err_phy = phy; - -	for (phy = 0; phy < err_phy; phy++) -		irq_dispose_mapping(bus->irq[phy]); - -	return err;  }  void mv88e6xxx_g2_irq_mdio_free(struct mv88e6xxx_chip *chip,  				struct mii_bus *bus)  { -	int phy; - -	for (phy = 0; phy < chip->info->num_internal_phys; phy++) -		irq_dispose_mapping(bus->irq[phy]);  } diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index e973114d6890..7e091965582b 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -369,6 +369,7 @@ int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target,  extern const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops;  extern const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops;  extern const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops; +extern const struct mv88e6xxx_irq_ops mv88e6393x_watchdog_ops;  extern const struct mv88e6xxx_avb_ops mv88e6165_avb_ops;  extern const struct mv88e6xxx_avb_ops mv88e6352_avb_ops; diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index aec9d4fd20e3..d19b6303b91f 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -276,7 +276,7 @@  /* Offset 0x10: Extended Port Control Command */  #define MV88E6393X_PORT_EPC_CMD		0x10  #define MV88E6393X_PORT_EPC_CMD_BUSY	0x8000 -#define MV88E6393X_PORT_EPC_CMD_WRITE	0x0300 +#define MV88E6393X_PORT_EPC_CMD_WRITE	0x3000  #define MV88E6393X_PORT_EPC_INDEX_PORT_ETYPE	0x02  /* Offset 0x11: Extended Port Control Data */ diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index d4cc9e60f369..80861ac090ae 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1056,6 +1056,17 @@ static void felix_phylink_get_caps(struct dsa_switch *ds, int port,  		  config->supported_interfaces);  } +static void felix_phylink_mac_config(struct dsa_switch *ds, int port, +				     unsigned int mode, +				     const struct phylink_link_state *state) +{ +	struct ocelot *ocelot = ds->priv; +	struct felix *felix = ocelot_to_felix(ocelot); + +	if (felix->info->phylink_mac_config) +		felix->info->phylink_mac_config(ocelot, port, mode, state); +} +  static struct phylink_pcs *felix_phylink_mac_select_pcs(struct dsa_switch *ds,  							int port,  							phy_interface_t iface) @@ -1539,11 +1550,6 @@ static int felix_connect_tag_protocol(struct dsa_switch *ds,  	}  } -/* Hardware initialization done here so that we can allocate structures with - * devm without fear of dsa_register_switch returning -EPROBE_DEFER and causing - * us to allocate structures twice (leak memory) and map PCI memory twice - * (which will not work). - */  static int felix_setup(struct dsa_switch *ds)  {  	struct ocelot *ocelot = ds->priv; @@ -1555,6 +1561,9 @@ static int felix_setup(struct dsa_switch *ds)  	if (err)  		return err; +	if (ocelot->targets[HSIO]) +		ocelot_pll5_init(ocelot); +  	err = ocelot_init(ocelot);  	if (err)  		goto out_mdiobus_free; @@ -1571,6 +1580,10 @@ static int felix_setup(struct dsa_switch *ds)  	dsa_switch_for_each_available_port(dp, ds) {  		ocelot_init_port(ocelot, dp->index); +		if (felix->info->configure_serdes) +			felix->info->configure_serdes(ocelot, dp->index, +						      dp->dn); +  		/* Set the default QoS Classification based on PCP and DEI  		 * bits of vlan tag.  		 */ @@ -2085,6 +2098,7 @@ const struct dsa_switch_ops felix_switch_ops = {  	.get_sset_count			= felix_get_sset_count,  	.get_ts_info			= felix_get_ts_info,  	.phylink_get_caps		= felix_phylink_get_caps, +	.phylink_mac_config		= felix_phylink_mac_config,  	.phylink_mac_select_pcs		= felix_phylink_mac_select_pcs,  	.phylink_mac_link_down		= felix_phylink_mac_link_down,  	.phylink_mac_link_up		= felix_phylink_mac_link_up, diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index d5d0b30c0b75..96008c046da5 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -15,6 +15,8 @@  #define OCELOT_PORT_MODE_USXGMII	BIT(4)  #define OCELOT_PORT_MODE_1000BASEX	BIT(5) +struct device_node; +  /* Platform-specific information */  struct felix_info {  	/* Hardcoded resources provided by the hardware instantiation. */ @@ -58,6 +60,11 @@ struct felix_info {  	void	(*tas_guard_bands_update)(struct ocelot *ocelot, int port);  	void	(*port_sched_speed_set)(struct ocelot *ocelot, int port,  					u32 speed); +	void	(*phylink_mac_config)(struct ocelot *ocelot, int port, +				      unsigned int mode, +				      const struct phylink_link_state *state); +	int	(*configure_serdes)(struct ocelot *ocelot, int port, +				    struct device_node *portnp);  };  /* Methods for initializing the hardware resources specific to a tagging diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index dddb28984bdf..d172a3e9736c 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -1263,7 +1263,7 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port)  	/* Consider the standard Ethernet overhead of 8 octets preamble+SFD,  	 * 4 octets FCS, 12 octets IFG.  	 */ -	needed_bit_time_ps = (maxlen + 24) * picos_per_byte; +	needed_bit_time_ps = (u64)(maxlen + 24) * picos_per_byte;  	dev_dbg(ocelot->dev,  		"port %d: max frame size %d needs %llu ps at speed %d\n", @@ -1424,6 +1424,7 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port,  	mutex_lock(&ocelot->tas_lock);  	if (!taprio->enable) { +		ocelot_port_mqprio(ocelot, port, &taprio->mqprio);  		ocelot_rmw_rix(ocelot, 0, QSYS_TAG_CONFIG_ENABLE,  			       QSYS_TAG_CONFIG, port); @@ -1436,15 +1437,19 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port,  		return 0;  	} +	ret = ocelot_port_mqprio(ocelot, port, &taprio->mqprio); +	if (ret) +		goto err_unlock; +  	if (taprio->cycle_time > NSEC_PER_SEC ||  	    taprio->cycle_time_extension >= NSEC_PER_SEC) {  		ret = -EINVAL; -		goto err; +		goto err_reset_tc;  	}  	if (taprio->num_entries > VSC9959_TAS_GCL_ENTRY_MAX) {  		ret = -ERANGE; -		goto err; +		goto err_reset_tc;  	}  	/* Enable guard band. The switch will schedule frames without taking @@ -1468,7 +1473,7 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port,  	val = ocelot_read(ocelot, QSYS_PARAM_STATUS_REG_8);  	if (val & QSYS_PARAM_STATUS_REG_8_CONFIG_PENDING) {  		ret = -EBUSY; -		goto err; +		goto err_reset_tc;  	}  	ocelot_rmw_rix(ocelot, @@ -1503,12 +1508,19 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port,  				 !(val & QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE),  				 10, 100000);  	if (ret) -		goto err; +		goto err_reset_tc;  	ocelot_port->taprio = taprio_offload_get(taprio);  	vsc9959_tas_guard_bands_update(ocelot, port); -err: +	mutex_unlock(&ocelot->tas_lock); + +	return 0; + +err_reset_tc: +	taprio->mqprio.qopt.num_tc = 0; +	ocelot_port_mqprio(ocelot, port, &taprio->mqprio); +err_unlock:  	mutex_unlock(&ocelot->tas_lock);  	return ret; @@ -1612,6 +1624,13 @@ static int vsc9959_qos_port_cbs_set(struct dsa_switch *ds, int port,  static int vsc9959_qos_query_caps(struct tc_query_caps_base *base)  {  	switch (base->type) { +	case TC_SETUP_QDISC_MQPRIO: { +		struct tc_mqprio_caps *caps = base->caps; + +		caps->validate_queue_counts = true; + +		return 0; +	}  	case TC_SETUP_QDISC_TAPRIO: {  		struct tc_taprio_caps *caps = base->caps; @@ -1635,6 +1654,8 @@ static int vsc9959_port_setup_tc(struct dsa_switch *ds, int port,  		return vsc9959_qos_query_caps(type_data);  	case TC_SETUP_QDISC_TAPRIO:  		return vsc9959_qos_port_tas_set(ocelot, port, type_data); +	case TC_SETUP_QDISC_MQPRIO: +		return ocelot_port_mqprio(ocelot, port, type_data);  	case TC_SETUP_QDISC_CBS:  		return vsc9959_qos_port_cbs_set(ds, port, type_data);  	default: @@ -2498,6 +2519,7 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot)  	for (port = 0; port < ocelot->num_phys_ports; port++) {  		struct ocelot_port *ocelot_port = ocelot->ports[port]; +		struct ocelot_mm_state *mm = &ocelot->mm[port];  		int min_speed = ocelot_port->speed;  		unsigned long mask = 0;  		u32 tmp, val = 0; @@ -2538,10 +2560,12 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot)  		/* Enable cut-through forwarding for all traffic classes that  		 * don't have oversized dropping enabled, since this check is -		 * bypassed in cut-through mode. +		 * bypassed in cut-through mode. Also exclude preemptible +		 * traffic classes, since these would hang the port for some +		 * reason, if sent as cut-through.  		 */  		if (ocelot_port->speed == min_speed) { -			val = GENMASK(7, 0); +			val = GENMASK(7, 0) & ~mm->active_preemptible_tcs;  			for (tc = 0; tc < OCELOT_NUM_TC; tc++)  				if (vsc9959_port_qmaxsdu_get(ocelot, port, tc)) @@ -2610,12 +2634,9 @@ static const struct felix_info felix_info_vsc9959 = {  static irqreturn_t felix_irq_handler(int irq, void *data)  {  	struct ocelot *ocelot = (struct ocelot *)data; -	int port;  	ocelot_get_txtstamp(ocelot); - -	for (port = 0; port < ocelot->num_phys_ports; port++) -		ocelot_port_mm_irq(ocelot, port); +	ocelot_mm_irq(ocelot);  	return IRQ_HANDLED;  } diff --git a/drivers/net/dsa/ocelot/ocelot_ext.c b/drivers/net/dsa/ocelot/ocelot_ext.c index 063150659816..c29bee5a5c48 100644 --- a/drivers/net/dsa/ocelot/ocelot_ext.c +++ b/drivers/net/dsa/ocelot/ocelot_ext.c @@ -20,13 +20,13 @@ static const u32 vsc7512_port_modes[VSC7514_NUM_PORTS] = {  	OCELOT_PORT_MODE_INTERNAL,  	OCELOT_PORT_MODE_INTERNAL,  	OCELOT_PORT_MODE_INTERNAL, -	OCELOT_PORT_MODE_NONE, -	OCELOT_PORT_MODE_NONE, -	OCELOT_PORT_MODE_NONE, -	OCELOT_PORT_MODE_NONE, -	OCELOT_PORT_MODE_NONE, -	OCELOT_PORT_MODE_NONE, -	OCELOT_PORT_MODE_NONE, +	OCELOT_PORT_MODE_SERDES, +	OCELOT_PORT_MODE_SERDES, +	OCELOT_PORT_MODE_SERDES, +	OCELOT_PORT_MODE_SERDES, +	OCELOT_PORT_MODE_SERDES, +	OCELOT_PORT_MODE_SGMII, +	OCELOT_PORT_MODE_SERDES,  };  static const struct ocelot_ops ocelot_ext_ops = { @@ -59,6 +59,8 @@ static const struct felix_info vsc7512_info = {  	.num_ports			= VSC7514_NUM_PORTS,  	.num_tx_queues			= OCELOT_NUM_TC,  	.port_modes			= vsc7512_port_modes, +	.phylink_mac_config		= ocelot_phylink_mac_config, +	.configure_serdes		= ocelot_port_configure_serdes,  };  static int ocelot_ext_probe(struct platform_device *pdev) @@ -149,7 +151,7 @@ MODULE_DEVICE_TABLE(of, ocelot_ext_switch_of_match);  static struct platform_driver ocelot_ext_switch_driver = {  	.driver = {  		.name = "ocelot-ext-switch", -		.of_match_table = of_match_ptr(ocelot_ext_switch_of_match), +		.of_match_table = ocelot_ext_switch_of_match,  	},  	.probe = ocelot_ext_probe,  	.remove = ocelot_ext_remove, diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c index 563ad338da25..96d4972a62f0 100644 --- a/drivers/net/dsa/ocelot/seville_vsc9953.c +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -1079,7 +1079,7 @@ static struct platform_driver seville_vsc9953_driver = {  	.shutdown	= seville_shutdown,  	.driver = {  		.name		= "mscc_seville", -		.of_match_table	= of_match_ptr(seville_of_match), +		.of_match_table	= seville_of_match,  	},  };  module_platform_driver(seville_vsc9953_driver); diff --git a/drivers/net/dsa/qca/Kconfig b/drivers/net/dsa/qca/Kconfig index ba339747362c..de9da469908b 100644 --- a/drivers/net/dsa/qca/Kconfig +++ b/drivers/net/dsa/qca/Kconfig @@ -15,3 +15,12 @@ config NET_DSA_QCA8K  	help  	  This enables support for the Qualcomm Atheros QCA8K Ethernet  	  switch chips. + +config NET_DSA_QCA8K_LEDS_SUPPORT +	bool "Qualcomm Atheros QCA8K Ethernet switch family LEDs support" +	depends on NET_DSA_QCA8K +	depends on LEDS_CLASS=y || LEDS_CLASS=NET_DSA_QCA8K +	depends on LEDS_TRIGGERS +	help +	  This enabled support for LEDs present on the Qualcomm Atheros +	  QCA8K Ethernet switch chips. diff --git a/drivers/net/dsa/qca/Makefile b/drivers/net/dsa/qca/Makefile index 701f1d199e93..ce66b1984e5f 100644 --- a/drivers/net/dsa/qca/Makefile +++ b/drivers/net/dsa/qca/Makefile @@ -2,3 +2,6 @@  obj-$(CONFIG_NET_DSA_AR9331)	+= ar9331.o  obj-$(CONFIG_NET_DSA_QCA8K)	+= qca8k.o  qca8k-y 			+= qca8k-common.o qca8k-8xxx.o +ifdef CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT +qca8k-y				+= qca8k-leds.o +endif diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c index 55df4479ea30..6d5ac7588a69 100644 --- a/drivers/net/dsa/qca/qca8k-8xxx.c +++ b/drivers/net/dsa/qca/qca8k-8xxx.c @@ -22,6 +22,7 @@  #include <linux/dsa/tag_qca.h>  #include "qca8k.h" +#include "qca8k_leds.h"  static void  qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) @@ -772,21 +773,6 @@ err_clear_skb:  	return ret;  } -static u32 -qca8k_port_to_phy(int port) -{ -	/* From Andrew Lunn: -	 * Port 0 has no internal phy. -	 * Port 1 has an internal PHY at MDIO address 0. -	 * Port 2 has an internal PHY at MDIO address 1. -	 * ... -	 * Port 5 has an internal PHY at MDIO address 4. -	 * Port 6 has no internal PHY. -	 */ - -	return port - 1; -} -  static int  qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask)  { @@ -1483,7 +1469,6 @@ static void qca8k_pcs_get_state(struct phylink_pcs *pcs,  	state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP);  	state->an_complete = state->link; -	state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO);  	state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL :  							   DUPLEX_HALF; @@ -1798,6 +1783,10 @@ qca8k_setup(struct dsa_switch *ds)  	if (ret)  		return ret; +	ret = qca8k_setup_led_ctrl(priv); +	if (ret) +		return ret; +  	qca8k_setup_pcs(priv, &priv->pcs_port_0, 0);  	qca8k_setup_pcs(priv, &priv->pcs_port_6, 6); diff --git a/drivers/net/dsa/qca/qca8k-leds.c b/drivers/net/dsa/qca/qca8k-leds.c new file mode 100644 index 000000000000..b883692b7d86 --- /dev/null +++ b/drivers/net/dsa/qca/qca8k-leds.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/regmap.h> +#include <net/dsa.h> + +#include "qca8k.h" +#include "qca8k_leds.h" + +static int +qca8k_get_enable_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info) +{ +	switch (port_num) { +	case 0: +		reg_info->reg = QCA8K_LED_CTRL_REG(led_num); +		reg_info->shift = QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT; +		break; +	case 1: +	case 2: +	case 3: +		/* Port 123 are controlled on a different reg */ +		reg_info->reg = QCA8K_LED_CTRL3_REG; +		reg_info->shift = QCA8K_LED_PHY123_PATTERN_EN_SHIFT(port_num, led_num); +		break; +	case 4: +		reg_info->reg = QCA8K_LED_CTRL_REG(led_num); +		reg_info->shift = QCA8K_LED_PHY4_CONTROL_RULE_SHIFT; +		break; +	default: +		return -EINVAL; +	} + +	return 0; +} + +static int +qca8k_led_brightness_set(struct qca8k_led *led, +			 enum led_brightness brightness) +{ +	struct qca8k_led_pattern_en reg_info; +	struct qca8k_priv *priv = led->priv; +	u32 mask, val; + +	qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info); + +	val = QCA8K_LED_ALWAYS_OFF; +	if (brightness) +		val = QCA8K_LED_ALWAYS_ON; + +	/* HW regs to control brightness is special and port 1-2-3 +	 * are placed in a different reg. +	 * +	 * To control port 0 brightness: +	 * - the 2 bit (15, 14) of: +	 *   - QCA8K_LED_CTRL0_REG for led1 +	 *   - QCA8K_LED_CTRL1_REG for led2 +	 *   - QCA8K_LED_CTRL2_REG for led3 +	 * +	 * To control port 4: +	 * - the 2 bit (31, 30) of: +	 *   - QCA8K_LED_CTRL0_REG for led1 +	 *   - QCA8K_LED_CTRL1_REG for led2 +	 *   - QCA8K_LED_CTRL2_REG for led3 +	 * +	 * To control port 1: +	 *   - the 2 bit at (9, 8) of QCA8K_LED_CTRL3_REG are used for led1 +	 *   - the 2 bit at (11, 10) of QCA8K_LED_CTRL3_REG are used for led2 +	 *   - the 2 bit at (13, 12) of QCA8K_LED_CTRL3_REG are used for led3 +	 * +	 * To control port 2: +	 *   - the 2 bit at (15, 14) of QCA8K_LED_CTRL3_REG are used for led1 +	 *   - the 2 bit at (17, 16) of QCA8K_LED_CTRL3_REG are used for led2 +	 *   - the 2 bit at (19, 18) of QCA8K_LED_CTRL3_REG are used for led3 +	 * +	 * To control port 3: +	 *   - the 2 bit at (21, 20) of QCA8K_LED_CTRL3_REG are used for led1 +	 *   - the 2 bit at (23, 22) of QCA8K_LED_CTRL3_REG are used for led2 +	 *   - the 2 bit at (25, 24) of QCA8K_LED_CTRL3_REG are used for led3 +	 * +	 * To abstract this and have less code, we use the port and led numm +	 * to calculate the shift and the correct reg due to this problem of +	 * not having a 1:1 map of LED with the regs. +	 */ +	if (led->port_num == 0 || led->port_num == 4) { +		mask = QCA8K_LED_PATTERN_EN_MASK; +		val <<= QCA8K_LED_PATTERN_EN_SHIFT; +	} else { +		mask = QCA8K_LED_PHY123_PATTERN_EN_MASK; +	} + +	return regmap_update_bits(priv->regmap, reg_info.reg, +				  mask << reg_info.shift, +				  val << reg_info.shift); +} + +static int +qca8k_cled_brightness_set_blocking(struct led_classdev *ldev, +				   enum led_brightness brightness) +{ +	struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); + +	return qca8k_led_brightness_set(led, brightness); +} + +static enum led_brightness +qca8k_led_brightness_get(struct qca8k_led *led) +{ +	struct qca8k_led_pattern_en reg_info; +	struct qca8k_priv *priv = led->priv; +	u32 val; +	int ret; + +	qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info); + +	ret = regmap_read(priv->regmap, reg_info.reg, &val); +	if (ret) +		return 0; + +	val >>= reg_info.shift; + +	if (led->port_num == 0 || led->port_num == 4) { +		val &= QCA8K_LED_PATTERN_EN_MASK; +		val >>= QCA8K_LED_PATTERN_EN_SHIFT; +	} else { +		val &= QCA8K_LED_PHY123_PATTERN_EN_MASK; +	} + +	/* Assume brightness ON only when the LED is set to always ON */ +	return val == QCA8K_LED_ALWAYS_ON; +} + +static int +qca8k_cled_blink_set(struct led_classdev *ldev, +		     unsigned long *delay_on, +		     unsigned long *delay_off) +{ +	struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); +	u32 mask, val = QCA8K_LED_ALWAYS_BLINK_4HZ; +	struct qca8k_led_pattern_en reg_info; +	struct qca8k_priv *priv = led->priv; + +	if (*delay_on == 0 && *delay_off == 0) { +		*delay_on = 125; +		*delay_off = 125; +	} + +	if (*delay_on != 125 || *delay_off != 125) { +		/* The hardware only supports blinking at 4Hz. Fall back +		 * to software implementation in other cases. +		 */ +		return -EINVAL; +	} + +	qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info); + +	if (led->port_num == 0 || led->port_num == 4) { +		mask = QCA8K_LED_PATTERN_EN_MASK; +		val <<= QCA8K_LED_PATTERN_EN_SHIFT; +	} else { +		mask = QCA8K_LED_PHY123_PATTERN_EN_MASK; +	} + +	regmap_update_bits(priv->regmap, reg_info.reg, mask << reg_info.shift, +			   val << reg_info.shift); + +	return 0; +} + +static int +qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num) +{ +	struct fwnode_handle *led = NULL, *leds = NULL; +	struct led_init_data init_data = { }; +	struct dsa_switch *ds = priv->ds; +	enum led_default_state state; +	struct qca8k_led *port_led; +	int led_num, led_index; +	int ret; + +	leds = fwnode_get_named_child_node(port, "leds"); +	if (!leds) { +		dev_dbg(priv->dev, "No Leds node specified in device tree for port %d!\n", +			port_num); +		return 0; +	} + +	fwnode_for_each_child_node(leds, led) { +		/* Reg represent the led number of the port. +		 * Each port can have at most 3 leds attached +		 * Commonly: +		 * 1. is gigabit led +		 * 2. is mbit led +		 * 3. additional status led +		 */ +		if (fwnode_property_read_u32(led, "reg", &led_num)) +			continue; + +		if (led_num >= QCA8K_LED_PORT_COUNT) { +			dev_warn(priv->dev, "Invalid LED reg %d defined for port %d", +				 led_num, port_num); +			continue; +		} + +		led_index = QCA8K_LED_PORT_INDEX(port_num, led_num); + +		port_led = &priv->ports_led[led_index]; +		port_led->port_num = port_num; +		port_led->led_num = led_num; +		port_led->priv = priv; + +		state = led_init_default_state_get(led); +		switch (state) { +		case LEDS_DEFSTATE_ON: +			port_led->cdev.brightness = 1; +			qca8k_led_brightness_set(port_led, 1); +			break; +		case LEDS_DEFSTATE_KEEP: +			port_led->cdev.brightness = +					qca8k_led_brightness_get(port_led); +			break; +		default: +			port_led->cdev.brightness = 0; +			qca8k_led_brightness_set(port_led, 0); +		} + +		port_led->cdev.max_brightness = 1; +		port_led->cdev.brightness_set_blocking = qca8k_cled_brightness_set_blocking; +		port_led->cdev.blink_set = qca8k_cled_blink_set; +		init_data.default_label = ":port"; +		init_data.fwnode = led; +		init_data.devname_mandatory = true; +		init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d", ds->slave_mii_bus->id, +						 port_num); +		if (!init_data.devicename) +			return -ENOMEM; + +		ret = devm_led_classdev_register_ext(priv->dev, &port_led->cdev, &init_data); +		if (ret) +			dev_warn(priv->dev, "Failed to init LED %d for port %d", led_num, port_num); + +		kfree(init_data.devicename); +	} + +	return 0; +} + +int +qca8k_setup_led_ctrl(struct qca8k_priv *priv) +{ +	struct fwnode_handle *ports, *port; +	int port_num; +	int ret; + +	ports = device_get_named_child_node(priv->dev, "ports"); +	if (!ports) { +		dev_info(priv->dev, "No ports node specified in device tree!"); +		return 0; +	} + +	fwnode_for_each_child_node(ports, port) { +		if (fwnode_property_read_u32(port, "reg", &port_num)) +			continue; + +		/* Skip checking for CPU port 0 and CPU port 6 as not supported */ +		if (port_num == 0 || port_num == 6) +			continue; + +		/* Each port can have at most 3 different leds attached. +		 * Switch port starts from 0 to 6, but port 0 and 6 are CPU +		 * port. The port index needs to be decreased by one to identify +		 * the correct port for LED setup. +		 */ +		ret = qca8k_parse_port_leds(priv, port, qca8k_port_to_phy(port_num)); +		if (ret) +			return ret; +	} + +	return 0; +} diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h index 7996975d29d3..c5cc8a172d65 100644 --- a/drivers/net/dsa/qca/qca8k.h +++ b/drivers/net/dsa/qca/qca8k.h @@ -11,6 +11,7 @@  #include <linux/delay.h>  #include <linux/regmap.h>  #include <linux/gpio.h> +#include <linux/leds.h>  #include <linux/dsa/tag_qca.h>  #define QCA8K_ETHERNET_MDIO_PRIORITY			7 @@ -85,6 +86,51 @@  #define   QCA8K_MDIO_MASTER_DATA(x)			FIELD_PREP(QCA8K_MDIO_MASTER_DATA_MASK, x)  #define   QCA8K_MDIO_MASTER_MAX_PORTS			5  #define   QCA8K_MDIO_MASTER_MAX_REG			32 + +/* LED control register */ +#define QCA8K_LED_PORT_COUNT				3 +#define QCA8K_LED_COUNT					((QCA8K_NUM_PORTS - QCA8K_NUM_CPU_PORTS) * QCA8K_LED_PORT_COUNT) +#define QCA8K_LED_RULE_COUNT				6 +#define QCA8K_LED_RULE_MAX				11 +#define QCA8K_LED_PORT_INDEX(_phy, _led)		(((_phy) * QCA8K_LED_PORT_COUNT) + (_led)) + +#define QCA8K_LED_PHY123_PATTERN_EN_SHIFT(_phy, _led)	((((_phy) - 1) * 6) + 8 + (2 * (_led))) +#define QCA8K_LED_PHY123_PATTERN_EN_MASK		GENMASK(1, 0) + +#define QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT		0 +#define QCA8K_LED_PHY4_CONTROL_RULE_SHIFT		16 + +#define QCA8K_LED_CTRL_REG(_i)				(0x050 + (_i) * 4) +#define QCA8K_LED_CTRL0_REG				0x50 +#define QCA8K_LED_CTRL1_REG				0x54 +#define QCA8K_LED_CTRL2_REG				0x58 +#define QCA8K_LED_CTRL3_REG				0x5C +#define   QCA8K_LED_CTRL_SHIFT(_i)			(((_i) % 2) * 16) +#define   QCA8K_LED_CTRL_MASK				GENMASK(15, 0) +#define QCA8K_LED_RULE_MASK				GENMASK(13, 0) +#define QCA8K_LED_BLINK_FREQ_MASK			GENMASK(1, 0) +#define QCA8K_LED_BLINK_FREQ_SHITF			0 +#define   QCA8K_LED_BLINK_2HZ				0 +#define   QCA8K_LED_BLINK_4HZ				1 +#define   QCA8K_LED_BLINK_8HZ				2 +#define   QCA8K_LED_BLINK_AUTO				3 +#define QCA8K_LED_LINKUP_OVER_MASK			BIT(2) +#define QCA8K_LED_TX_BLINK_MASK				BIT(4) +#define QCA8K_LED_RX_BLINK_MASK				BIT(5) +#define QCA8K_LED_COL_BLINK_MASK			BIT(7) +#define QCA8K_LED_LINK_10M_EN_MASK			BIT(8) +#define QCA8K_LED_LINK_100M_EN_MASK			BIT(9) +#define QCA8K_LED_LINK_1000M_EN_MASK			BIT(10) +#define QCA8K_LED_POWER_ON_LIGHT_MASK			BIT(11) +#define QCA8K_LED_HALF_DUPLEX_MASK			BIT(12) +#define QCA8K_LED_FULL_DUPLEX_MASK			BIT(13) +#define QCA8K_LED_PATTERN_EN_MASK			GENMASK(15, 14) +#define QCA8K_LED_PATTERN_EN_SHIFT			14 +#define   QCA8K_LED_ALWAYS_OFF				0 +#define   QCA8K_LED_ALWAYS_BLINK_4HZ			1 +#define   QCA8K_LED_ALWAYS_ON				2 +#define   QCA8K_LED_RULE_CONTROLLED			3 +  #define QCA8K_GOL_MAC_ADDR0				0x60  #define QCA8K_GOL_MAC_ADDR1				0x64  #define QCA8K_MAX_FRAME_SIZE				0x78 @@ -382,6 +428,19 @@ struct qca8k_pcs {  	int port;  }; +struct qca8k_led_pattern_en { +	u32 reg; +	u8 shift; +}; + +struct qca8k_led { +	u8 port_num; +	u8 led_num; +	u16 old_rule; +	struct qca8k_priv *priv; +	struct led_classdev cdev; +}; +  struct qca8k_priv {  	u8 switch_id;  	u8 switch_revision; @@ -406,6 +465,7 @@ struct qca8k_priv {  	struct qca8k_pcs pcs_port_0;  	struct qca8k_pcs pcs_port_6;  	const struct qca8k_match_data *info; +	struct qca8k_led ports_led[QCA8K_LED_COUNT];  };  struct qca8k_mib_desc { @@ -421,6 +481,20 @@ struct qca8k_fdb {  	u8 mac[6];  }; +static inline u32 qca8k_port_to_phy(int port) +{ +	/* From Andrew Lunn: +	 * Port 0 has no internal phy. +	 * Port 1 has an internal PHY at MDIO address 0. +	 * Port 2 has an internal PHY at MDIO address 1. +	 * ... +	 * Port 5 has an internal PHY at MDIO address 4. +	 * Port 6 has no internal PHY. +	 */ + +	return port - 1; +} +  /* Common setup function */  extern const struct qca8k_mib_desc ar8327_mib[];  extern const struct regmap_access_table qca8k_readable_table; diff --git a/drivers/net/dsa/qca/qca8k_leds.h b/drivers/net/dsa/qca/qca8k_leds.h new file mode 100644 index 000000000000..ab367f05b173 --- /dev/null +++ b/drivers/net/dsa/qca/qca8k_leds.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __QCA8K_LEDS_H +#define __QCA8K_LEDS_H + +/* Leds Support function */ +#ifdef CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT +int qca8k_setup_led_ctrl(struct qca8k_priv *priv); +#else +static inline int qca8k_setup_led_ctrl(struct qca8k_priv *priv) +{ +	return 0; +} +#endif + +#endif /* __QCA8K_LEDS_H */ diff --git a/drivers/net/dsa/realtek/realtek-mdio.c b/drivers/net/dsa/realtek/realtek-mdio.c index 3e54fac5f902..5a8fe707ca25 100644 --- a/drivers/net/dsa/realtek/realtek-mdio.c +++ b/drivers/net/dsa/realtek/realtek-mdio.c @@ -21,6 +21,7 @@  #include <linux/module.h>  #include <linux/of_device.h> +#include <linux/overflow.h>  #include <linux/regmap.h>  #include "realtek.h" @@ -152,7 +153,9 @@ static int realtek_mdio_probe(struct mdio_device *mdiodev)  	if (!var)  		return -EINVAL; -	priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); +	priv = devm_kzalloc(&mdiodev->dev, +			    size_add(sizeof(*priv), var->chip_data_sz), +			    GFP_KERNEL);  	if (!priv)  		return -ENOMEM; diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index da31d8b839ac..41ea3b5a42b1 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -98,6 +98,7 @@  #include <linux/of_irq.h>  #include <linux/regmap.h>  #include <linux/if_bridge.h> +#include <linux/if_vlan.h>  #include "realtek.h" @@ -267,6 +268,7 @@  /* Maximum packet length register */  #define RTL8365MB_CFG0_MAX_LEN_REG	0x088C  #define   RTL8365MB_CFG0_MAX_LEN_MASK	0x3FFF +#define RTL8365MB_CFG0_MAX_LEN_MAX	0x3FFF  /* Port learning limit registers */  #define RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE		0x0A20 @@ -1135,6 +1137,35 @@ static void rtl8365mb_phylink_mac_link_up(struct dsa_switch *ds, int port,  	}  } +static int rtl8365mb_port_change_mtu(struct dsa_switch *ds, int port, +				     int new_mtu) +{ +	struct realtek_priv *priv = ds->priv; +	int frame_size; + +	/* When a new MTU is set, DSA always sets the CPU port's MTU to the +	 * largest MTU of the slave ports. Because the switch only has a global +	 * RX length register, only allowing CPU port here is enough. +	 */ +	if (!dsa_is_cpu_port(ds, port)) +		return 0; + +	frame_size = new_mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; + +	dev_dbg(priv->dev, "changing mtu to %d (frame size: %d)\n", +		new_mtu, frame_size); + +	return regmap_update_bits(priv->map, RTL8365MB_CFG0_MAX_LEN_REG, +				  RTL8365MB_CFG0_MAX_LEN_MASK, +				  FIELD_PREP(RTL8365MB_CFG0_MAX_LEN_MASK, +					     frame_size)); +} + +static int rtl8365mb_port_max_mtu(struct dsa_switch *ds, int port) +{ +	return RTL8365MB_CFG0_MAX_LEN_MAX - VLAN_ETH_HLEN - ETH_FCS_LEN; +} +  static void rtl8365mb_port_stp_state_set(struct dsa_switch *ds, int port,  					 u8 state)  { @@ -1980,10 +2011,7 @@ static int rtl8365mb_setup(struct dsa_switch *ds)  		p->index = i;  	} -	/* Set maximum packet length to 1536 bytes */ -	ret = regmap_update_bits(priv->map, RTL8365MB_CFG0_MAX_LEN_REG, -				 RTL8365MB_CFG0_MAX_LEN_MASK, -				 FIELD_PREP(RTL8365MB_CFG0_MAX_LEN_MASK, 1536)); +	ret = rtl8365mb_port_change_mtu(ds, cpu->trap_port, ETH_DATA_LEN);  	if (ret)  		goto out_teardown_irq; @@ -2103,6 +2131,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops_smi = {  	.get_eth_mac_stats = rtl8365mb_get_mac_stats,  	.get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats,  	.get_stats64 = rtl8365mb_get_stats64, +	.port_change_mtu = rtl8365mb_port_change_mtu, +	.port_max_mtu = rtl8365mb_port_max_mtu,  };  static const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = { @@ -2124,6 +2154,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = {  	.get_eth_mac_stats = rtl8365mb_get_mac_stats,  	.get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats,  	.get_stats64 = rtl8365mb_get_stats64, +	.port_change_mtu = rtl8365mb_port_change_mtu, +	.port_max_mtu = rtl8365mb_port_max_mtu,  };  static const struct realtek_ops rtl8365mb_ops = { diff --git a/drivers/net/dsa/rzn1_a5psw.c b/drivers/net/dsa/rzn1_a5psw.c index 919027cf2012..c37d2e537230 100644 --- a/drivers/net/dsa/rzn1_a5psw.c +++ b/drivers/net/dsa/rzn1_a5psw.c @@ -120,6 +120,22 @@ static void a5psw_port_mgmtfwd_set(struct a5psw *a5psw, int port, bool enable)  	a5psw_port_pattern_set(a5psw, port, A5PSW_PATTERN_MGMTFWD, enable);  } +static void a5psw_port_tx_enable(struct a5psw *a5psw, int port, bool enable) +{ +	u32 mask = A5PSW_PORT_ENA_TX(port); +	u32 reg = enable ? mask : 0; + +	/* Even though the port TX is disabled through TXENA bit in the +	 * PORT_ENA register, it can still send BPDUs. This depends on the tag +	 * configuration added when sending packets from the CPU port to the +	 * switch port. Indeed, when using forced forwarding without filtering, +	 * even disabled ports will be able to send packets that are tagged. +	 * This allows to implement STP support when ports are in a state where +	 * forwarding traffic should be stopped but BPDUs should still be sent. +	 */ +	a5psw_reg_rmw(a5psw, A5PSW_PORT_ENA, mask, reg); +} +  static void a5psw_port_enable_set(struct a5psw *a5psw, int port, bool enable)  {  	u32 port_ena = 0; @@ -292,6 +308,22 @@ static int a5psw_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)  	return 0;  } +static void a5psw_port_learning_set(struct a5psw *a5psw, int port, bool learn) +{ +	u32 mask = A5PSW_INPUT_LEARN_DIS(port); +	u32 reg = !learn ? mask : 0; + +	a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg); +} + +static void a5psw_port_rx_block_set(struct a5psw *a5psw, int port, bool block) +{ +	u32 mask = A5PSW_INPUT_LEARN_BLOCK(port); +	u32 reg = block ? mask : 0; + +	a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg); +} +  static void a5psw_flooding_set_resolution(struct a5psw *a5psw, int port,  					  bool set)  { @@ -308,6 +340,14 @@ static void a5psw_flooding_set_resolution(struct a5psw *a5psw, int port,  		a5psw_reg_writel(a5psw, offsets[i], a5psw->bridged_ports);  } +static void a5psw_port_set_standalone(struct a5psw *a5psw, int port, +				      bool standalone) +{ +	a5psw_port_learning_set(a5psw, port, !standalone); +	a5psw_flooding_set_resolution(a5psw, port, !standalone); +	a5psw_port_mgmtfwd_set(a5psw, port, standalone); +} +  static int a5psw_port_bridge_join(struct dsa_switch *ds, int port,  				  struct dsa_bridge bridge,  				  bool *tx_fwd_offload, @@ -323,8 +363,7 @@ static int a5psw_port_bridge_join(struct dsa_switch *ds, int port,  	}  	a5psw->br_dev = bridge.dev; -	a5psw_flooding_set_resolution(a5psw, port, true); -	a5psw_port_mgmtfwd_set(a5psw, port, false); +	a5psw_port_set_standalone(a5psw, port, false);  	return 0;  } @@ -334,8 +373,7 @@ static void a5psw_port_bridge_leave(struct dsa_switch *ds, int port,  {  	struct a5psw *a5psw = ds->priv; -	a5psw_flooding_set_resolution(a5psw, port, false); -	a5psw_port_mgmtfwd_set(a5psw, port, true); +	a5psw_port_set_standalone(a5psw, port, true);  	/* No more ports bridged */  	if (a5psw->bridged_ports == BIT(A5PSW_CPU_PORT)) @@ -344,28 +382,35 @@ static void a5psw_port_bridge_leave(struct dsa_switch *ds, int port,  static void a5psw_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)  { -	u32 mask = A5PSW_INPUT_LEARN_DIS(port) | A5PSW_INPUT_LEARN_BLOCK(port); +	bool learning_enabled, rx_enabled, tx_enabled;  	struct a5psw *a5psw = ds->priv; -	u32 reg = 0;  	switch (state) {  	case BR_STATE_DISABLED:  	case BR_STATE_BLOCKING: -		reg |= A5PSW_INPUT_LEARN_DIS(port); -		reg |= A5PSW_INPUT_LEARN_BLOCK(port); -		break;  	case BR_STATE_LISTENING: -		reg |= A5PSW_INPUT_LEARN_DIS(port); +		rx_enabled = false; +		tx_enabled = false; +		learning_enabled = false;  		break;  	case BR_STATE_LEARNING: -		reg |= A5PSW_INPUT_LEARN_BLOCK(port); +		rx_enabled = false; +		tx_enabled = false; +		learning_enabled = true;  		break;  	case BR_STATE_FORWARDING: -	default: +		rx_enabled = true; +		tx_enabled = true; +		learning_enabled = true;  		break; +	default: +		dev_err(ds->dev, "invalid STP state: %d\n", state); +		return;  	} -	a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg); +	a5psw_port_learning_set(a5psw, port, learning_enabled); +	a5psw_port_rx_block_set(a5psw, port, !rx_enabled); +	a5psw_port_tx_enable(a5psw, port, tx_enabled);  }  static void a5psw_port_fast_age(struct dsa_switch *ds, int port) @@ -673,7 +718,7 @@ static int a5psw_setup(struct dsa_switch *ds)  	}  	/* Configure management port */ -	reg = A5PSW_CPU_PORT | A5PSW_MGMT_CFG_DISCARD; +	reg = A5PSW_CPU_PORT | A5PSW_MGMT_CFG_ENABLE;  	a5psw_reg_writel(a5psw, A5PSW_MGMT_CFG, reg);  	/* Set pattern 0 to forward all frame to mgmt port */ @@ -722,13 +767,15 @@ static int a5psw_setup(struct dsa_switch *ds)  		if (dsa_port_is_unused(dp))  			continue; -		/* Enable egress flooding for CPU port */ -		if (dsa_port_is_cpu(dp)) +		/* Enable egress flooding and learning for CPU port */ +		if (dsa_port_is_cpu(dp)) {  			a5psw_flooding_set_resolution(a5psw, port, true); +			a5psw_port_learning_set(a5psw, port, true); +		} -		/* Enable management forward only for user ports */ +		/* Enable standalone mode for user ports */  		if (dsa_port_is_user(dp)) -			a5psw_port_mgmtfwd_set(a5psw, port, true); +			a5psw_port_set_standalone(a5psw, port, true);  	}  	return 0; diff --git a/drivers/net/dsa/rzn1_a5psw.h b/drivers/net/dsa/rzn1_a5psw.h index c67abd49c013..b869192eef3f 100644 --- a/drivers/net/dsa/rzn1_a5psw.h +++ b/drivers/net/dsa/rzn1_a5psw.h @@ -19,6 +19,7 @@  #define A5PSW_PORT_OFFSET(port)		(0x400 * (port))  #define A5PSW_PORT_ENA			0x8 +#define A5PSW_PORT_ENA_TX(port)		BIT(port)  #define A5PSW_PORT_ENA_RX_SHIFT		16  #define A5PSW_PORT_ENA_TX_RX(port)	(BIT((port) + A5PSW_PORT_ENA_RX_SHIFT) | \  					 BIT(port)) @@ -36,7 +37,7 @@  #define A5PSW_INPUT_LEARN_BLOCK(p)	BIT(p)  #define A5PSW_MGMT_CFG			0x20 -#define A5PSW_MGMT_CFG_DISCARD		BIT(7) +#define A5PSW_MGMT_CFG_ENABLE		BIT(6)  #define A5PSW_MODE_CFG			0x24  #define A5PSW_MODE_STATS_RESET		BIT(31)  |